ContextMenu for(Hierarchical)中的方法DataTemplate调用TreeViewItem的方法而不是ViewModel中的方法

时间:2015-01-27 14:25:31

标签: c# wpf mvvm treeview contextmenu

我有一个带有多个 HierarchicalDataTemplate&的 TreeView 。 DataTemplate 项目和我正在使用 Caliburn Micro 用于mvvm。树视图的 ItemsSource 指向视图模型中名为“TreeData”的集合,我尝试为每个HierarchicalDataTemplate&添加特定的ContextMenu。 DataTemplate中。

在ContextMenu中,我使用校准功能“ cal:Message.Attach ”来调用

中的函数

我做了一个较小的树视图示例来说明问题。

在ViewModel(集合对象)中:

public class MyViewModel
{
    // TreeData object
    public ObservableCollection<TestRoot> TreeData = new ObservableCollection<TestRoot>()
    {
        new TestRoot()
        {
            Name = "Root item"
        }
    };

    // the function on the viewmodel that should be called
    public void DoSomething(object item)
    {
        MessageBox.Show("MyViewModel - DoSomething called");
    }
}

集合对象:

public class TestRoot
{
    public string Name { get; set; }

    // caliburn calls this instead of the one on the viewmodel
    public void DoSomething(object item)
    {
        MessageBox.Show("TestRoot - DoSomething called");
    }
}

MyView.xaml treeview只有一个(Hierarchical)DataTemplate:

<TreeView Margin="5" ItemsSource="{Binding TreeData}">
    <TreeView.Resources>
        <DataTemplate DataType="{x:Type vm:TestRoot}" >

            <StackPanel Orientation="Horizontal">
                <StackPanel.ContextMenu>
                    <ContextMenu>
                        <!-- caliburn (?) chooses the method on the collection object, not the viewmodel -->
                        <MenuItem Header="test dosomething" cal:Message.Attach="DoSomething($dataContext)"></MenuItem>
                    </ContextMenu>
                </StackPanel.ContextMenu>

                <TextBlock Text="{Binding Name}"/>
            </StackPanel>

        </DataTemplate>
    </TreeView.Resources>
</TreeView>

在另一段代码中,我将ContextMenu放在TreeView.ContextMenu中。它在'应该'工作,指向viewmodel上的方法。

寻找解决方案,我看到“继承上下文”之类的东西。我认为它可能与它有关,但我不确定。我如何告诉caliburn它必须在我的方法的viewmodel中查找,而不是我点击的TreeView中的项目?

或者还有其他可能吗?例如:在Resources中定义不同的ContextMenus并将它们指向DataTemplates?但是,这不会导致完全相同的问题吗?

请注意,我希望尽可能减少代码隐藏。感谢

更新

为了完整性,这是真正的开发代码。这应该是对的,不是吗?

<TreeView ItemsSource="{Binding OrderTreeViewData.OrderTreeViewCategories}"
          cal:Message.Attach="[Event SelectedItemChanged] = [Action OnSelectedItemChanged($this)]">
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <!-- We have to select the item which is right-clicked on -->
            <EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown"
                            Handler="TreeViewItem_PreviewMouseRightButtonDown"/>
            <!-- set expanded -->
            <Setter Property="TreeViewItem.IsExpanded" Value="True"/>
        </Style>
    </TreeView.ItemContainerStyle>

    <TreeView.Resources>                   
        <!--  dredge  nodes -->
        <HierarchicalDataTemplate DataType="{x:Type programs:DredgeRoot}" 
                                    ItemsSource="{Binding Dredgezones}">
            <StackPanel>
                <StackPanel.ContextMenu>
                    <ContextMenu DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
                        <MenuItem Header="Add dredge zone" cal:Message.Attach="TreeViewAddDredgeZone($datacontext)"></MenuItem>
                    </ContextMenu>
                </StackPanel.ContextMenu>

                <TextBlock Text="{Binding Name}"/>
            </StackPanel>
        </HierarchicalDataTemplate>

        <!-- omitted other templates -->

    </TreeView.Resources>
</TreeView>

2 个答案:

答案 0 :(得分:0)

不幸的是,还有一个棘手的问题需要处理。由于特定的Popup行为,它不会继承DataContext。要访问正确的上下文,您必须获得PlacementTarget

  <StackPanel Tag="{Binding DataContext, RelativeSource={RelativeSource Self}}">
      <StackPanel.ContextMenu>
         <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
               <MenuItem Header="test dosomething" cal:Message.Attach="DoSomething"/>
         </ContextMenu>
      </StackPanel.ContextMenu>
  </StackPanel> 

答案 1 :(得分:0)

我认为你和我有几乎相同的问题,也许可以看到这个主题:Bind contextMenu to a different viewmodel from treeview

您可以尝试使用命令: 尝试将您的代码更改为:

<ContextMenu x:Key="MyContextMenu">
                    <MenuItem Header="Add dredge zone" Command="{Binding PlacementTarget.Tag.DataContext.TreeViewAddDredgeZoneCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}" 
                          CommandParameter="{Binding}"></MenuItem>
                </ContextMenu>

然后将hierarchyDataTemplate添加到Tag和ContextMenu

<HierarchicalDataTemplate DataType="{x:Type programs:DredgeRoot}" 
                                ItemsSource="{Binding Dredgezones}" Tag="{Binding RelativeSource={RelativeSource AncestorType=UserControl}} ContextMenu="{StaticResource MyContextMenu}">

在您的viewmodel中,您可以使用以下内容添加命令:

public ICommand TreeViewAddDredgeZoneCommand
    {
        //your code here
    }