5

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

两个问题:

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

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

4

6 回答 6

6

我找到了一种方法来处理由 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
}
于 2009-05-15T21:55:39.260 回答
3

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

两种选择:

  • 按照 Thomas 的建议,在 ViewModel 中调用命令
  • 将拇指的位置绑定到 ViewModel 中的一个属性,然后当控件在 WPF 中移动时会更新 ViewModel 中的位置值。
于 2009-05-16T02:04:18.943 回答
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);

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

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

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

于 2009-05-15T19:51:32.023 回答
1

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

您可以使用附加的行为模式将事件映射到命令,请在此处查看 Marlon Grech 的实现

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

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

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

于 2009-05-15T23:04:41.713 回答
1

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

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

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

于 2009-05-20T16:43:58.530 回答
0

我正在使用一种更优雅的方法。我使用 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}">
于 2010-11-02T16:11:48.190 回答