在MVVM中处理鼠标输入命令

时间:2017-11-30 22:52:01

标签: c# wpf xaml mvvm command

我在ViewModel中使用Custom UserControlViewModel填充了带有ItemControl的Canvas。我想给每个UserControl一个MouseEnter Command或者什么。

我的ViewModel的XAML如下:

<Canvas x:Name="gameFieldCanvas" Width="{Binding CanvasWidth}" Height="{Binding CanvasHeight}">
   <ItemsControl ItemsSource="{Binding GameFieldContent}">
      <ItemsControl.ItemTemplate>
         <DataTemplate DataType="{x:Type local:GameFieldTileViewModel}">
             <Canvas>
                <local:GameFieldTileUserControl X="{Binding TileX}" Y="{Binding TileY}">
                   <local:GameFieldTileUserControl.InputBindings>
                      <MouseBinding MouseAction="LeftClick"
                                                  Command="{Binding DataContext.OnGameFieldTileLeftClicked, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
                                                  CommandParameter="{Binding}"/>
                      <MouseBinding MouseAction="RightClick" Command="{Binding DataContext.OnGameFieldTileRightClicked, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
                                                  CommandParameter="{Binding}"/>
                   </local:GameFieldTileUserControl.InputBindings>
                </local:GameFieldTileUserControl>
             </Canvas>
         </DataTemplate>
      </ItemsControl.ItemTemplate>
   </ItemsControl>
</Canvas>

就像你可以看到我已经为Left- / RightClick命令工作正常。但是MouseEnterEvents没有InputBinding。

我已经读过Blend SDK,你可以在其中编写EventTriggers或类似的内容,但是使用电路板工具是不是可能?

我只想知道在输入UserControl时获取它的UserControlViewModel。

2 个答案:

答案 0 :(得分:2)

你能详细介绍一下你最终想要完成什么吗?我可能会为您提供更好的答案,但您可以,我有很多次,覆盖现有控件并添加所需的命令属性。

这是一个具有鼠标移动命令的自定义用户控件。

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WPF_Question_Answer_App
{
    public partial class MouseMoveCommandUserControl : UserControl
    {
        public MouseMoveCommandUserControl()
        {
            InitializeComponent();
            MouseMove += (s, e) =>
            {
                if (MouseMoveCommand?.CanExecute(MouseMoveCommandParameter) ?? false)
                    MouseMoveCommand.Execute(MouseMoveCommandParameter);
            };
        }

        public ICommand MouseMoveCommand
        {
            get => (ICommand)GetValue(MouseMoveCommandProperty);
            set => SetValue(MouseMoveCommandProperty, value);
        }

        public static readonly DependencyProperty MouseMoveCommandProperty =
            DependencyProperty.Register(nameof(MouseMoveCommand), typeof(ICommand), typeof(MouseMoveCommandUserControl), new PropertyMetadata(null));

        public object MouseMoveCommandParameter
        {
            get => GetValue(MouseMoveCommandParameterProperty);
            set => SetValue(MouseMoveCommandParameterProperty, value);
        }

        public static readonly DependencyProperty MouseMoveCommandParameterProperty =
            DependencyProperty.Register(nameof(MouseMoveCommandParameter), typeof(object), typeof(MouseMoveCommandUserControl), new PropertyMetadata(null));
    }
}

并且在视图中使用in是显而易见的。

<Window x:Class="WPF_Question_Answer_App.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPF_Question_Answer_App"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <local:MouseMoveCommandUserControl MouseMoveCommand="{Binding SomeMouseMoveCommand}"
                                           MouseMoveCommandParameter="{Binding SomeMouseMoveCommandParameter}"/>
    </Grid>
</Window>

希望这有帮助......如果不是,我会删除答案,请告诉我。

答案 1 :(得分:0)

仅举例说明如何使用内置组件实现它。

您可以利用已存在的UI组件来完成大部分目标。有副作用(不能连续两次点击相同的图块,需要修改选择行为的方式等),但这是另一种做事方式。

在窗口中,使用带有适当面板的ListBox。在我的例子中,你可以用它作为tic tac toe游戏的基础:

<Window x:Class="TileClicker.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TileClicker"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <ListBox
        ItemsSource="{Binding Tiles}"
        SelectedItem="{Binding SelectedTile}" DisplayMemberPath="Name">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid
                    Rows="3"
                    Columns="3" />
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>
</Window>

窗口的代码隐藏是最小的

public MainWindow()
{
    InitializeComponent();
    DataContext = new ViewModel();
}

我们的瓷砖由

表示
public class Tile : Notifier
{
    private string _name = "Click me";

    public string Name
    {
        get
        {
            return _name;
        }
        set { _name = value; Notify(); }
    }
}

和我们的视图模型使用SelectedItem绑定来了解何时单击一个图块,正确响应

public class ViewModel : Notifier
{
    private Tile _tile;
    public ObservableCollection<Tile> Tiles { get; } = new ObservableCollection<Tile> {
        new Tile(), new Tile(), new Tile(), 
        new Tile(), new Tile(), new Tile(), 
        new Tile(), new Tile(), new Tile()
    };
    public Tile SelectedTile
    {
        get
        {
            return _tile;
        }
        set
        {
            _tile = value;
            Notify();
            UpdateName(value);
        }
    }

    private void UpdateName(Tile value)
    {
        if(value.Name == "Click me")
            value.Name = "Woot!";
        else
            value.Name = "Click me";
    }
}

基础只是从上面的类中删除了INPC impl以便于阅读

public class Notifier : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void Notify([CallerMemberName] string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}