WPF绑定上下文菜单菜单项';s项目来源
我正在尝试将单个MenuItem的ItemsSource绑定到ViewModel中的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
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>