c# - 如何从 ItemTemplate 中将命令绑定(bind)到 ContextMenu?

标签 c# wpf contextmenu itemtemplate

我想将某个命令绑定(bind)到 menuItem。所述菜单项是在 ItemTemplate 中定义的 ContextMenu 的一部分。

现在,我编译并运行了,但命令从未被调用。过去,我曾使用类似的模式成功地将命令挂接到 ItemTemplate 中定义的按钮。

有谁知道我如何才能做到这一点?

XAML:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Wpf_treeView" x:Name="window" x:Class="Wpf_treeView.MainWindow"
    Title="MainWindow" Height="350" Width="525">
  <Grid>
    <TreeView HorizontalAlignment="Left" Height="299" Margin="10,10,0,0" VerticalAlignment="Top" Width="228" ItemsSource="{Binding DataInfosView}" >
      <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Children}">
          <TextBlock DockPanel.Dock="Left" Text="{Binding InfoValue}" TextAlignment="Left" >
            <TextBlock.ContextMenu>
              <ContextMenu>
                <MenuItem Header="{Binding InfoValue}" IsEnabled="False"/>
                <MenuItem Header="Add child" Command="{Binding AddChildCmd, ElementName=window}"/>
              </ContextMenu>
            </TextBlock.ContextMenu>
          </TextBlock>
        </HierarchicalDataTemplate>
      </TreeView.ItemTemplate>
    </TreeView>
  </Grid>
</Window>

C#:

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;

namespace Wpf_treeView
{
    public partial class MainWindow : Window
    {
        private static readonly Random rnd = new Random();
        private List<InfoData> m_InfoData = new List<InfoData>();

        public ListCollectionView DataInfosView { get; private set; }

        public static readonly DependencyProperty AddChildProperty =
            DependencyProperty.Register("AddChildCmd", 
                                        typeof(ICommand),
                                        typeof(MainWindow));

        public ICommand AddChildCmd
        {
            get { return (ICommand) GetValue(AddChildProperty); }
            set { SetValue(AddChildProperty, value); }
        }

        public MainWindow()
        {
            AddChildCmd = new RoutedCommand();
            CommandManager.RegisterClassCommandBinding(
                GetType(), 
                new CommandBinding(AddChildCmd, AddChild));

            m_InfoData.Add(new InfoData(4));
            m_InfoData.Add(new InfoData(1));
            m_InfoData.Add(new InfoData(5));
            m_InfoData[1].Children.Add(new InfoData(3));
            m_InfoData[1].Children[0].Children.Add(new InfoData(7));

            DataInfosView = new ListCollectionView(m_InfoData);
            DataContext = this;

            InitializeComponent();
        }

        private void AddChild(object sender, RoutedEventArgs e)
        {
            ExecutedRoutedEventArgs args = (ExecutedRoutedEventArgs)e;
            InfoData info = (InfoData)args.Parameter;
            info.Children.Add(new InfoData(rnd.Next(0, 11)));
        }
    }

    class InfoData : INotifyPropertyChanged
    {
        private int infoValue;

        public int InfoValue
        {
            get { return infoValue; }
            set
            {
                if (value != infoValue)
                {
                    infoValue = value;
                    OnPropertyChanged();
                }
            }
        }

        public List<InfoData> Children { get; private set; }

        public InfoData(int infoValue)
        {
            InfoValue = infoValue;
            Children = new List<InfoData>();
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(
            [CallerMemberName] string propertyName = null)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

最佳答案

好的,这应该可以工作:

<TextBlock DockPanel.Dock="Left"
                           Text="{Binding InfoValue}"
                           TextAlignment="Left"
                           Tag="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
                    <TextBlock.ContextMenu>
                        <ContextMenu>
                            <MenuItem Header="{Binding InfoValue}"
                                      IsEnabled="False" />
                            <MenuItem Header="Add child"
                                      Command="{Binding Path=Parent.PlacementTarget.Tag.AddChildCmd, RelativeSource={RelativeSource Self}}" 
                                      CommandParameter="{Binding}" />
                        </ContextMenu>
                    </TextBlock.ContextMenu>
                </TextBlock>

常规可视化树中不存在 ContextMenu,因此您无法沿着树向上走以到达主数据上下文。通过使用标签,您可以将主窗口的数据上下文“传递”到上下文菜单。有关与上下文菜单绑定(bind)的更多信息,请参阅 this answer以及this one因为他们对正在发生的事情提供了一些很好的解释

关于c# - 如何从 ItemTemplate 中将命令绑定(bind)到 ContextMenu?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36043659/

相关文章:

c# - 在 C# 中,应该考虑重构一个类之前的多少行?

c# - 通过网络将c#字符串发送到c++服务器

c# - 附加命令

winforms - 列表框中项目的菜单?

android - onLongClick 显示菜单选项

c# - 在 C# 中合并多个列表

c# - Task.Run() 的回调方法

c# - 如何填充基于其他组合框的组合框?

wpf - 当我将模板应用于我的 MenuItem 时,单击它时不再显示下拉列表

macos - 在OSx Mojave的Finder中未选择任何内容时,如何在右键单击上下文菜单中添加选项?