WPF绑定上下文菜单菜单项';s项目来源

WPF绑定上下文菜单菜单项';s项目来源,wpf,mvvm,data-binding,contextmenu,menuitem,Wpf,Mvvm,Data Binding,Contextmenu,Menuitem,我正在尝试将单个MenuItem的ItemsSource绑定到ViewModel中的ReadOnlyCollection。我已经读到ContextMenu不在主可视化树下,所以我不能直接绑定它,但是我尝试的任何方法都不起作用。我有代码片段,请让我知道我做错了什么 <Window> … <DockPanel> <!-- Task bar Icon --> <tb:TaskbarIcon x:Name="AppNotifyIcon" D

我正在尝试将单个MenuItem的ItemsSource绑定到ViewModel中的
ReadOnlyCollection
。我已经读到ContextMenu不在主可视化树下,所以我不能直接绑定它,但是我尝试的任何方法都不起作用。我有代码片段,请让我知道我做错了什么

<Window>
…
<DockPanel>
  <!-- Task bar Icon -->
  <tb:TaskbarIcon x:Name="AppNotifyIcon"
       DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
       ToolTipText="{Binding Source={StaticResource LocalizedStrings}, Path=Strings.MainTitle}">
      <tb:TaskbarIcon.ContextMenu>
          <ContextMenu DataContext="{Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
                <MenuItem Header="{Binding Source={StaticResource LocalizedStrings}, Path=Strings.NotifyIconOpen}" Click="MenuItem_Open_Click"/>
                <MenuItem Header="Technologies" ItemsSource="{Binding to the ReadOnlyCollection of string in ViewModel}">
                   <MenuItem.ItemContainerStyle>
                       <Style>
                          <Setter Property="MenuItem.Command" Value="{Binding <!--Command in ViewModel-->, RelativeSource={RelativeSource AncestorType=Window}}"/>
                          <Setter Property="MenuItem.CommandParameter" Value="{Binding}"/> <!—Binding to the menuItem Header item -->
                       </Style>
                   </MenuItem.ItemContainerStyle>
               </MenuItem>
               <MenuItem Header="{Binding Source={StaticResource LocalizedStrings}, Path=Strings.NotifyIconExit}" Click="MenuItem_Exit_Click"/>
          </ContextMenu>
      </tb:TaskbarIcon.ContextMenu>
   </tb:TaskbarIcon>
…
</DockPanel>

…
请尝试查看以下内容:
1.XAML代码:

        <DataGrid x:Name="SelectDataGrid" 
              ItemsSource="{Binding Persons}" HorizontalAlignment="Left" CellEditEnding="SelectDataGrid_OnCellEditEnding"
              VerticalAlignment="Top" AutoGenerateColumns="False" Loaded="SelectDataGrid_OnLoaded">
        <DataGrid.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Technologies" ItemsSource="{Binding MenuItems}">
                    <MenuItem.ItemContainerStyle>
                        <Style TargetType="MenuItem">
                            <Setter Property="Command" Value="{Binding Command}"/>
                            <Setter Property="Header" Value="{Binding Content}"/>
                            <Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ContextMenu}}, Path=DataContext}"/>
                        </Style>
                    </MenuItem.ItemContainerStyle>
                </MenuItem>
            </ContextMenu>
        </DataGrid.ContextMenu>
        <DataGrid.Columns>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Button Command="{Binding HelloCommand}"></Button>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn></DataGrid>
五,。MVVM部件实现:

    public class BaseObservableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

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

    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
    {
        var propName = ((MemberExpression)raiser.Body).Member.Name;
        OnPropertyChanged(propName);
    }

    protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, value))
        {
            field = value;
            OnPropertyChanged(name);
            return true;
        }
        return false;
    }
}

public class RelayCommand<T> : ICommand
{
    readonly Action<T> _execute;
    readonly Func<T, bool> _canExecute;

    public event EventHandler CanExecuteChanged;

    public RelayCommand(Action<T> execute, Func<T, bool> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public void RefreshCommand()
    {
        var cec = CanExecuteChanged;
        if (cec != null)
            cec(this, EventArgs.Empty);
    }

    public bool CanExecute(object parameter)
    {
        if (_canExecute == null) return true;
        return _canExecute((T)parameter);
    }

    public void Execute(object parameter)
    {
        _execute((T)parameter);
    }
}

public class RelayCommand : RelayCommand<object>
{
    public RelayCommand(Action execute, Func<bool> canExecute = null)
        : base(_ => execute(),
            _ => canExecute == null || canExecute())
    {

    }
}
公共类BaseObserveObject:INotifyPropertyChanged
{
公共事件属性更改事件处理程序属性更改;
受保护的虚拟void OnPropertyChanged([CallerMemberName]字符串propertyName=null)
{
var handler=PropertyChanged;
if(handler!=null)handler(这是新的PropertyChangedEventArgs(propertyName));
}
受保护的虚拟void OnPropertyChanged(表达式提升器)
{
var propName=((MemberExpression)raiser.Body).Member.Name;
OnPropertyChanged(propName);
}
受保护的布尔集合(参考T字段,T值,[CallerMemberName]字符串名称=null)
{
如果(!EqualityComparer.Default.Equals(字段,值))
{
字段=值;
OnPropertyChanged(名称);
返回true;
}
返回false;
}
}
公共类中继命令:ICommand
{
只读操作_执行;
只读功能可执行;
公共事件处理程序CanExecuteChanged;
公共RelayCommand(执行操作,Func canExecute=null)
{
_执行=执行;
_canExecute=canExecute;
}
公共命令()
{
var cec=CanExecuteChanged;
如果(cec!=null)
cec(此,EventArgs.Empty);
}
公共布尔CanExecute(对象参数)
{
if(_canExecute==null)返回true;
返回_canExecute((T)参数);
}
public void Execute(对象参数)
{
_执行((T)参数);
}
}
公共类RelayCommand:RelayCommand
{
公共RelayCommand(执行操作,Func canExecute=null)
:base(=>execute(),
_=>canExecute==null | | canExecute())
{
}
}
  • 调用Init方法生成玩具菜单项的集合DataContext

  • Execute是在按下某个菜单项时调用的方法

  • 仅此而已。如果代码有问题,我很乐意帮忙。
    关于,

    对于列表框中的上下文菜单,我将我的DataContext添加到父控件的标记中,并在与放置目标的相对源绑定中找到它。然而,关于这一点有很多问题,其中一些可能涉及更具体的实例

    <ListBox ItemsSource="{Binding ItemList}"
             SelectedItem="{Binding SelectedItem}"
             Tag="{Binding}">
                    <ListBox.ContextMenu>
                        <ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                            <MenuItem Header="Delete" 
                                      Command="{Binding Path=DeleteCommand}"/>
                        </ContextMenu>
                    </ListBox.ContextMenu>
                </ListBox>
    
    
    
    因此,对于您的示例:

    <tb:TaskbarIcon x:Name="AppNotifyIcon"
                    DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                    ToolTipText="{Binding Source={StaticResource LocalizedStrings}, Path=Strings.MainTitle}"
                    Tag="{Binding}">
        <tb:TaskbarIcon.ContextMenu>
            <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                <MenuItem Header="{Binding Source={StaticResource LocalizedStrings}, Path=Strings.NotifyIconOpen}" Click="MenuItem_Open_Click"/>
                <MenuItem Header="Technologies" ItemsSource="{Binding TechnologyList}">
                   <MenuItem.ItemContainerStyle>
                       <Style>
                          <Setter Property="MenuItem.Command" Value="{Binding VmCommand}"/>
                          <Setter Property="MenuItem.CommandParameter" Value="{Binding}"/>
                       </Style>
                   </MenuItem.ItemContainerStyle>
               </MenuItem>
               <MenuItem Header="{Binding Source={StaticResource LocalizedStrings}, Path=Strings.NotifyIconExit}" Click="MenuItem_Exit_Click"/>
          </ContextMenu>
      </tb:TaskbarIcon.ContextMenu>
    
    
    


    当然,您的绑定可能会有所不同,但从这里开始,您至少应该设置DataContext,并从那里开始。

    好的,我发现了问题,这要感谢Ilan在使用snoop实用程序的评论中提出的建议。 我在可视化树中看到,ContextMenu没有指向其父项TaskbarIcon(奇怪…)的PlacementTarget,但它有一个附加属性,名为TaskbarIcon.ParentTaskbarIcon,来自TaskbarIcon,因此我将ContextMenu的DataContext绑定到TaskbarIcon.ParentTaskbarIcon.Tag,并将其全部修复

    <Window>
    ...
    <DockPanel>
                <!-- Task bar Icon -->
                <tb:TaskbarIcon x:Name="AppNotifyIcon"
                            IconSource="pack://application:,,,/Icons/HwServerIcon.ico"
                            Tag="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                            ToolTipText="{Binding Tag, RelativeSource={RelativeSource Self}}"><!--{Binding Source={StaticResource LocalizedStrings}, Path=Strings.MainTitle}-->
                    <tb:TaskbarIcon.ContextMenu>
                        <ContextMenu DataContext="{Binding Path=(tb:TaskbarIcon.ParentTaskbarIcon).Tag, RelativeSource={RelativeSource Self}}">
                            <MenuItem Header="{Binding Source={StaticResource LocalizedStrings}, Path=Strings.NotifyIconOpen}" Click="MenuItem_Open_Click"/>
                            <MenuItem Header="Technologies" ItemsSource="{Binding TechnologiesNames}">
                                <MenuItem.ItemContainerStyle>
                                    <Style>
                                        <Setter Property="MenuItem.Command" Value="{Binding DataContext.OpenTechnology, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
                                        <Setter Property="MenuItem.CommandParameter" Value="{Binding}"/>
                                    </Style>
                                </MenuItem.ItemContainerStyle>
                            </MenuItem>
                            <MenuItem Header="{Binding Source={StaticResource LocalizedStrings}, Path=Strings.NotifyIconExit}" Click="MenuItem_Exit_Click"/>
                        </ContextMenu>
                    </tb:TaskbarIcon.ContextMenu>
                </tb:TaskbarIcon>
    
    
    ...
    

    所以,TaskbarIcon的标记指向窗口的DataContext,ContextMenu的DataContext指向任务栏的附加属性ParentTaskbarIcon.Tag,从现在起,每个绑定都像在可视化树中的窗口下一样执行。

    您是否尝试在绑定中提供
    相对资源
    窗口作为相对资源,但它不起作用。嗨,伊兰,谢谢你的回答,但这不符合我的需要。我希望绑定到ViewModel中的字符串集合,问题是ContextMenu不位于可视化树HiArchy中的DataGrid(或TaskbarIcon)下。所以ContextMenu的DataContext不是从datagrid(或TaskBarIcon)或窗口获取的。@MaorB您好,您想绑定到哪个viewmodel?解决方案的viewmodel绑定到窗口viewmodel。请更具体地说,您要绑定到的viewmodel在哪里?对不起,我要绑定到的viewmodel是窗口的viewmodel,在其中我有一个名为TechnologyList的
    ReadOnlyCollection
    ,我希望MenuItem的ItemSource将绑定到它。谢谢。@MaorB是您的TaskbarIcon或DockPanel数据模板的一部分。你能在控件所在的地方发布整个xaml代码吗(它们是listboxitem模板或其他东西的一部分)。窗口->DockPanel->TaskbarIcon。谢谢Matthew的回答。我试过你的答案,但还是不行。我得到一个空的DataContext。为了验证它而不是MenuItem头中的“技术”,我将一个绑定放置到DataContext值,以查看是否获得DataContext的值:
    Header=“{binding}”
    ,而我什么也看不到。所以我没法去医院
    <tb:TaskbarIcon x:Name="AppNotifyIcon"
                    DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                    ToolTipText="{Binding Source={StaticResource LocalizedStrings}, Path=Strings.MainTitle}"
                    Tag="{Binding}">
        <tb:TaskbarIcon.ContextMenu>
            <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                <MenuItem Header="{Binding Source={StaticResource LocalizedStrings}, Path=Strings.NotifyIconOpen}" Click="MenuItem_Open_Click"/>
                <MenuItem Header="Technologies" ItemsSource="{Binding TechnologyList}">
                   <MenuItem.ItemContainerStyle>
                       <Style>
                          <Setter Property="MenuItem.Command" Value="{Binding VmCommand}"/>
                          <Setter Property="MenuItem.CommandParameter" Value="{Binding}"/>
                       </Style>
                   </MenuItem.ItemContainerStyle>
               </MenuItem>
               <MenuItem Header="{Binding Source={StaticResource LocalizedStrings}, Path=Strings.NotifyIconExit}" Click="MenuItem_Exit_Click"/>
          </ContextMenu>
      </tb:TaskbarIcon.ContextMenu>
    
    <Window>
    ...
    <DockPanel>
                <!-- Task bar Icon -->
                <tb:TaskbarIcon x:Name="AppNotifyIcon"
                            IconSource="pack://application:,,,/Icons/HwServerIcon.ico"
                            Tag="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                            ToolTipText="{Binding Tag, RelativeSource={RelativeSource Self}}"><!--{Binding Source={StaticResource LocalizedStrings}, Path=Strings.MainTitle}-->
                    <tb:TaskbarIcon.ContextMenu>
                        <ContextMenu DataContext="{Binding Path=(tb:TaskbarIcon.ParentTaskbarIcon).Tag, RelativeSource={RelativeSource Self}}">
                            <MenuItem Header="{Binding Source={StaticResource LocalizedStrings}, Path=Strings.NotifyIconOpen}" Click="MenuItem_Open_Click"/>
                            <MenuItem Header="Technologies" ItemsSource="{Binding TechnologiesNames}">
                                <MenuItem.ItemContainerStyle>
                                    <Style>
                                        <Setter Property="MenuItem.Command" Value="{Binding DataContext.OpenTechnology, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
                                        <Setter Property="MenuItem.CommandParameter" Value="{Binding}"/>
                                    </Style>
                                </MenuItem.ItemContainerStyle>
                            </MenuItem>
                            <MenuItem Header="{Binding Source={StaticResource LocalizedStrings}, Path=Strings.NotifyIconExit}" Click="MenuItem_Exit_Click"/>
                        </ContextMenu>
                    </tb:TaskbarIcon.ContextMenu>
                </tb:TaskbarIcon>