【问题标题】:WPF: How to attach mouse events to a viewmodel?WPF:如何将鼠标事件附加到视图模型?
【发布时间】:2014-12-09 21:19:55
【问题描述】:

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

两个问题:

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

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

【问题讨论】:

    标签: wpf events mvvm datatemplate


    【解决方案1】:

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

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

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

    (2)找出事件适用于哪个item,把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
    }
    

    【讨论】:

      【解决方案2】:

      ViewModel 应该与 GUI 断开连接,因此它对控件或鼠标点击一无所知。

      两种选择:

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

      【讨论】:

      • 我明白了,你如何让拇指自己移动?
      • 查看codeproject.com/KB/WPF/WPFDiagramDesigner_Part1.aspx 了解如何制作可移动拇指控件的示例。
      • 该项目只是通过处理 DragDelta 事件在代码隐藏中移动拇指。
      • 是的。你不想这样做吗?
      【解决方案3】:

      我想出了第二个问题的答案。我需要一个支持滚动的 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"/>
      

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

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

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

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

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

      【讨论】:

        【解决方案4】:

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

        您可能希望在拖动过程中拆分 UI 反馈和最终拖放时执行的操作。

        不过,我同意上面的 Thomas 和 Cameron。您需要限制事件处理和数据绑定的混合/匹配。如果您要走事件处理路线,您可能不想避免为您的对象使用术语“视图模型”,因为它通常表示数据绑定替代方案。

        【讨论】:

        • 这实际上对我没有用,但感谢这篇有趣的文章!
        【解决方案5】:

        有一些方法可以在没有代码隐藏的情况下做到这一点......

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

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

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

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

        【讨论】:

        • 我不这么认为,因为这是一个用于创建和移动图形对象的图形界面,所以我需要鼠标坐标,并且我需要在拖动过程中显示反馈。
        【解决方案6】:

        我正在使用一种更优雅的方法。我使用 Prism 2 和数据模板。所以我所做的是这样的:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-02-10
          • 1970-01-01
          • 2012-10-13
          • 1970-01-01
          相关资源
          最近更新 更多