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

时间:2009-05-15 18:19:24

标签: wpf events mvvm datatemplate

我第一次尝试使用MVVM模式。所以我有ItemsControl填充了我的viewmodel对象,使用DataTemplate显示;对象是DataTemplate中带有ThumbPolyline对象的“节点”和“边缘”,我希望能够检测ItemsControl上的点击和拖动为了移动节点和边缘。

两个问题:

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

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

6 个答案:

答案 0 :(得分:6)

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

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

<ItemsControl x:Name="_itemsControl" 
              Thumb.DragStarted="Node_DragStarted"
              Thumb.DragDelta="Node_DragDelta"
              Thumb.DragCompleted="Node_DragCompleted"
              MouseDoubleClick="OnMouseDoubleClick"
              .../>

(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
}

答案 1 :(得分:3)

ViewModel应该与GUI断开连接,因此它对控件或鼠标点击没有任何了解。

两个选项:

  • 按照Thomas
  • 的建议,在ViewModel中调用命令
  • 将拇指的位置绑定到ViewModel中的属性,然后当控件在WPF周围移动时,将更新ViewModel中的位置值。

答案 2 :(得分:2)

我找到了第二个问题的答案。我需要一个支持滚动的ItemsControl,我需要将项目放在网格而不是默认的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"/>

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

Grid grid = (Grid)_itemsControl.Template.FindName("Panel", _itemsControl);

然后将事件处理程序附加到网格,并在鼠标事件处理程序内部获取鼠标坐标w.r.t.网格使用

Point p = e.GetPosition((IInputElement)sender);

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

答案 3 :(得分:1)

如果没有代码隐藏,有很多方法可以做到这一点......

您可以使用附加的行为模式将事件映射到命令,请参阅Marlon Grech的实现here

您还可以使用我编写的markup extension将InputBindings绑定到ViewModel命令,如下所示:

<UserControl.InputBindings>
    <MouseBinding Gesture="LeftClick" Command="{input:CommandBinding SomeCommand}"/>
</UserControl.InputBindings>

但是我不确定它是否符合您的特定需求......

答案 4 :(得分:1)

Bea Stollnitz有一个标题为“如何在数据绑定的ItemsControls之间拖放项目?”的拖放示例。我发布链接,但StackOverflow不允许我。

您可能希望在拖动过程中分割UI反馈,并在最终删除时执行操作。

但是,我同意托马斯和卡梅隆。您需要限制事件处理和数据绑定的混合/匹配。如果您要使用事件处理路径,则可能不希望避免对对象使用术语“视图模型”,因为它通常表示数据绑定替代方案。

答案 5 :(得分:0)

我正在使用更优雅的方法。我使用Prism 2和datatemplates。所以我所做的就是:

<ItemsControl x:Name="SearchImagesList" ItemTemplate="{StaticResource SearchResultsAlbum}"   

在ItemTemplate中我刚刚创建了一个按钮!

<DataTemplate x:Key="SearchResultsAlbum">                        
    <Button CommandParameter="{Binding}"                 
            Command="{Binding Source={x:Static PhotoBookPRMainModule:ServiceProvider.DesignEditorViewManager}, Path=NavigationCommands.NavigateSearchResultAction}">