我试图找出如何绑定我在ItemsControl中添加的Button的ContextMenu。基本上,我希望能够右键单击按钮并将其从位于我的viewmodel上的可观察集合中删除。我知道ContextMenu不是VisualTree的一部分,所以使用RelativeSource来查找我的DataContext并不是对我有用。
我想要做的最终目标是将MenuItem上的命令绑定到我的ViewModel上的RemoveCommand,然后传入右键单击的Button的Content属性,以便我可以从observable集合中删除它
对此的任何帮助将不胜感激。
型号:
public class Preset
{
public string Name { get; set; }
}
视图模型:
public class SettingsWindowViewModel
{
public ObservableCollection<Preset> MyPresets { get; } = new ObservableCollection<Preset>();
private ICommand _plusCommand;
public ICommand PlusCommand => _plusCommand ?? (_plusCommand = new DelegateCommand(AddPreset));
private ICommand _removeCommand;
public ICommand RemoveCommand => _removeCommand ?? (_removeCommand = new DelegateCommand<string>(RemovePreset));
private void AddPreset()
{
var count = MyPresets.Count;
MyPresets.Add(new Preset {Name = $"Preset{count+1}"});
}
private void RemovePreset(string name)
{
var preset = MyPresets.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.CurrentCultureIgnoreCase));
if (preset!= null)
{
MyPresets.Remove(preset);
}
}
}
XAML:
<Window x:Class="WpfTesting.Esper.Views.SettingsWindow"
x:Name="MainSettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:WpfTesting.Esper.ViewModels"
mc:Ignorable="d"
Title="SettingsWindow" Height="470" Width="612">
<Window.DataContext>
<viewModels:SettingsWindowViewModel/>
</Window.DataContext>
<Window.Resources>
<Style BasedOn="{StaticResource {x:Type MenuItem}}" TargetType="{x:Type MenuItem}" x:Key="PopupMenuItem">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Border>
<ContentPresenter ContentSource="Header"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="35"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="2" Orientation="Horizontal">
<Button Width="70" Content="Load"/>
<Button Width="70" Content="Save As"/>
<ItemsControl ItemsSource="{Binding MyPresets}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Width="70" Content="{Binding Name}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Style="{StaticResource PopupMenuItem}" Header="Remove">
<!--
I need to set up binding a Command to a method on the DataContext of the Window, and I need to pass in the Content of the Button that is the parent of the ContextMenu
-->
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Width="20" Background="Transparent" BorderBrush="Transparent" Content="+" FontSize="21.333" HorizontalAlignment="Center" VerticalAlignment="Center" Command="{Binding PlusCommand}"/>
</StackPanel>
</Grid>
</Window>
答案 0 :(得分:0)
我基本上遇到了类似的问题,我找到的解决方案是使用一些MVVM框架,如Devexpress或Mvvm Light。
基本上,您可以在viewModel中注册以侦听传入的消息。类本身,至少在Devexpress实现中使用弱引用,因此您甚至可能不注销消息处理程序,也不会导致内存泄漏。
我曾使用此方法从ObservableCollection中删除右键单击选项卡,因此它与您的场景类似。
你可以看一下:
在这里:
答案 1 :(得分:0)
使用WPF: Binding a ContextMenu to an MVVM Command作为标签可以做的介绍,我通过使用多个标签来保存我正在寻找的上下文,找到了如何做我想要的事情。
我首先确保给我的窗口一个x:名称
<Window x:Name="MainSettingsWindow"
接下来,在我的ItemsControl的DataTemplate里面的Button上,我设置了一个Tag并将其设置为我的Window
<ItemsControl ItemsSource="{Binding MyPresets}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Width="70" Content="{Binding Name}" Tag="{Binding ElementName=MainSettingsWindow}">
接下来,在ContextMenu中,我将ContextMenu的DataContext设置为Button上设置的Tag,我还需要在ContextMenu上创建一个Tag并将其指回Button的Content属性以便我可以将其传递给CommandParameter
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Mode=Self}}" Tag="{Binding PlacementTarget.Content, RelativeSource={RelativeSource Mode=Self}}">
此时,我现在可以使用ViewModel中的Command和Button中的Content属性正确绑定我的MenuItem
这是我的ItemsControl的最终XAML:
<ItemsControl ItemsSource="{Binding MyPresets}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Width="70" Content="{Binding Name}" Tag="{Binding ElementName=MainSettingsWindow}">
<Button.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Mode=Self}}" Tag="{Binding PlacementTarget.Content, RelativeSource={RelativeSource Mode=Self}}">
<MenuItem Header="Remove"
Style="{StaticResource PopupMenuItem}"
Command="{Binding Path=DataContext.RemoveCommand}"
CommandParameter="{Binding Path=Tag, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
需要注意的一点是,我必须更改ViewModel上的CommandParameter以获取Object而不是String。我这样做的原因是因为我在DelegateCommand
中的CanExecute方法上遇到了异常这是我得到的例外:
Unable to cast object of type 'MS.Internal.NamedObject' to type 'System.String'.
我不确定是什么导致该异常抛出,但将其更改为Object对我来说没问题。