WPF绑定ContextMenu MenuItem的ItemsSource

时间:2015-11-17 14:51:26

标签: wpf mvvm data-binding contextmenu menuitem

我尝试将单个MenuItem的ItemsSource绑定到位于ViewModel中的ReadOnlyCollection<string>。我已经读过ContextMenu不在主Visual树之下,所以我无法直接绑定它,但我尝试的任何方法都不起作用。我有代码片段,请让我知道我做错了什么。

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

我正在尝试绑定第二个MenuItem的ItemsSource,并且在其内部的ItemContainerStyle中我想绑定命令和commandParameter。 **更新:**我使用hardcodet的TaskbarIcon来表示wpf,如果重要的话。

由于

3 个答案:

答案 0 :(得分:1)

尝试检查一下:  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>

2。上下文菜单的DataContext与datagrid和window相同。  3.在DataContext中放入下一个代码:

    private void Init()
    {
        MenuItems = new ObservableCollection<MenuItemObject>(new List<MenuItemObject>
        {
            new MenuItemObject {Command = new RelayCommand<object>(Execute), Content = "A"},
            new MenuItemObject {Command = new RelayCommand<object>(Execute), Content = "B"},
            new MenuItemObject {Command = new RelayCommand<object>(Execute), Content = "C"},
            new MenuItemObject {Command = new RelayCommand<object>(Execute), Content = "D"},
        });
    }

    public ObservableCollection<MenuItemObject> MenuItems { get; set; }

    private void Execute(object o)
    {

    }

4。 MenuItemsObject模型代码:

public class MenuItemObject:BaseObservableObject
{
    private ICommand _command;
    private string _content;

    public ICommand Command
    {
        get { return _command; }
        set
        {
            _command = value;
            OnPropertyChanged();
        }
    }

    public string Content
    {
        get { return _content; }
        set
        {
            _content = value;
            OnPropertyChanged();
        }
    }
}

5。 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())
    {

    }
}
  1. 调用Init方法生成玩具菜单项的集合DataContext。

  2. 执行是按下某个菜单项时调用的方法。

  3. 就是这样。如果代码出现问题,我很乐意提供帮助。 问候,

答案 1 :(得分:1)

好的,我已经找到了问题,这要归功于Ilan在使用snoop实用程序的评论中提出的建议。 我在可视化树中看到,ContextMenu没有将其PlacementTarget指向其父级TaskbarIcon(Weird ..),但它有一个来自TaskbarIcon的名为TaskbarIcon.ParentTaskbarIcon的附加属性,所以我绑定了ContextMenu的DataContext到TaskbarIcon.ParentTaskbarIcon.Tag并修复了所有内容。

Class clazz = (Class) Class.forName("Your class name");
for (Class c : clazz.getClass().getInterfaces()) {
   String name=c.getName());
   if(name.equals("org.apache.SomethingInterface"))
    {
     now u know u have it implemented
    }
}

因此,TaskbarIcon的Tag指向Window的DataContext,ContextMenu的DataContext指向Taskbar的附加属性ParentTaskbarIcon.Tag,从现在起,每个绑定都像在可视树中的窗口一样执行。

答案 2 :(得分:0)

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

DbContext.Entry(order, ent => ent.State = EntityState.Modified;

特别是你的例子:

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

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

相关问题