WPF:如何将鼠标事件附加到视图模型?

Qwe*_*tie 5 wpf events datatemplate mvvm

我试图第一次使用MVVM模式.所以我ItemsControl填充了我的viewmodel对象,使用DataTemplate's 显示; 对象是"节点"和"边缘",DataTemplateThumbPolyline对象表示,我希望能够检测到点击和拖动ItemsControl,以便移动节点和边缘.

两个问题:

  • 如何将鼠标事件处理程序附加到由小视图模型处理的Polyline's和Thumb?(我可以将一个Thumb.DragDelta处理程序附加到ItemsControle.OriginalSource指向Thumb,但是如何获取相应的viewmodel对象?)
  • 如何将鼠标事件处理程序附加ItemsControl到检测鼠标单击和拖动空白区域?(答案如下)

注意:我知道如果它直接处理View的事件,它可能不被认为是正确的ViewModel.但重要的是,我需要处理鼠标事件,我不知道如何附加它们.

Qwe*_*tie 6

我找到了一种方法来处理DataTemplate中的对象引发的事件.

(1)将事件处理程序附加到ItemsControl

<ItemsControl x:Name="_itemsControl" 
              Thumb.DragStarted="Node_DragStarted"
              Thumb.DragDelta="Node_DragDelta"
              Thumb.DragCompleted="Node_DragCompleted"
              MouseDoubleClick="OnMouseDoubleClick"
              .../>
Run Code Online (Sandbox Code Playgroud)

(2)找出事件适用的项目,将OriginalSource视为FrameworkElement,并获取其DataContext:

void Node_DragStarted(object sender, DragStartedEventArgs e)
{
    var os = (FrameworkElement)e.OriginalSource;
    var vm = os.DataContext as ItemViewModel;
    if (vm != null)
        // do something with the item ViewModel
}
Run Code Online (Sandbox Code Playgroud)


Qwe*_*tie 2

我找到了第二个问题的答案。我需要一个支持滚动的 ItemsControl,并且需要将项目放在 Grid 而不是默认的 StackPanel 上。为了满足这两个要求,我使用了 ControlTemplate:

<!--In the resources...-->
<ControlTemplate x:Key="GraphTemplate" TargetType="ItemsControl">
    <ScrollViewer Name="ScrollViewer"
                  Padding="{TemplateBinding Padding}"
                  HorizontalScrollBarVisibility="Auto">
        ...
            <Grid Name="Panel" IsItemsHost="True"
                  Background="{TemplateBinding ItemsControl.Background}"/>
        ...
    </ScrollViewer>
</ControlTemplate>
<!--Later...-->
<ItemsControl x:Name="_itemsControl" 
              ItemsSource="{Binding Items}"
              Template="{StaticResource GraphTemplate}"
              Background="LightYellow"/>
Run Code Online (Sandbox Code Playgroud)

为了获取具有有意义的鼠标坐标(即可滚动空间中的坐标)的鼠标事件,有必要使用奇怪的咒语来获取对网格的引用:

Grid grid = (Grid)_itemsControl.Template.FindName("Panel", _itemsControl);
Run Code Online (Sandbox Code Playgroud)

然后将事件处理程序附加到网格,并在鼠标事件处理程序内,使用以下命令获取网格的鼠标坐标

Point p = e.GetPosition((IInputElement)sender);
Run Code Online (Sandbox Code Playgroud)

为了在整个表面上获取鼠标事件,控件(实际上是网格)必须有背景,因此我在上面设置了Background =“LightYellow”,它通过ControlTemplate中的绑定传播到网格。