17 data-binding wpf contextmenu mvvm
在选项卡页面上的用户控件上的上下文菜单中绑定命令时遇到问题.我第一次使用菜单(右键单击选项卡)它工作得很好,但如果我切换选项卡,命令将使用第一次使用的数据绑定实例.
如果我在usercontrol中放置一个绑定到该命令的按钮,它按预期工作...
有人可以告诉我,我做错了什么?
这是一个暴露问题的测试项目:
App.xaml.cs:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
CompanyViewModel model = new CompanyViewModel();
Window1 window = new Window1();
window.DataContext = model;
window.Show();
}
}
Run Code Online (Sandbox Code Playgroud)
Window1.xaml:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vw="clr-namespace:WpfApplication1"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="HeaderTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type vw:PersonViewModel}">
<vw:UserControl1/>
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl ItemsSource="{Binding Path=Persons}"
ItemTemplate="{StaticResource HeaderTemplate}"
IsSynchronizedWithCurrentItem="True" />
</Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)
UserControl1.xaml:
<UserControl x:Class="WpfApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
MinWidth="200">
<UserControl.ContextMenu>
<ContextMenu >
<MenuItem Header="Change" Command="{Binding Path=ChangeCommand}"/>
</ContextMenu>
</UserControl.ContextMenu>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0">The name:</Label>
<TextBox Grid.Column="1" Text="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</UserControl>
Run Code Online (Sandbox Code Playgroud)
CompanyViewModel.cs:
public class CompanyViewModel
{
public ObservableCollection<PersonViewModel> Persons { get; set; }
public CompanyViewModel()
{
Persons = new ObservableCollection<PersonViewModel>();
Persons.Add(new PersonViewModel(new Person { Name = "Kalle" }));
Persons.Add(new PersonViewModel(new Person { Name = "Nisse" }));
Persons.Add(new PersonViewModel(new Person { Name = "Jocke" }));
}
}
Run Code Online (Sandbox Code Playgroud)
PersonViewModel.cs:
public class PersonViewModel : INotifyPropertyChanged
{
Person _person;
TestCommand _testCommand;
public PersonViewModel(Person person)
{
_person = person;
_testCommand = new TestCommand(this);
}
public ICommand ChangeCommand
{
get
{
return _testCommand;
}
}
public string Name
{
get
{
return _person.Name;
}
set
{
if (value == _person.Name)
return;
_person.Name = value;
OnPropertyChanged("Name");
}
}
void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Run Code Online (Sandbox Code Playgroud)
TestCommand.cs:
public class TestCommand : ICommand
{
PersonViewModel _person;
public event EventHandler CanExecuteChanged;
public TestCommand(PersonViewModel person)
{
_person = person;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_person.Name = "Changed by command";
}
}
Run Code Online (Sandbox Code Playgroud)
Person.cs:
public class Person
{
public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
Cam*_*and 22
这里要记住的关键是上下文菜单不是可视化树的一部分.
因此,它们不会继承与它们所属的控件相同的源以进行绑定.处理此问题的方法是绑定到ContextMenu本身的放置目标.
<MenuItem Header="Change" Command="{Binding
Path=PlacementTarget.ChangeCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"
/>
Run Code Online (Sandbox Code Playgroud)
我发现将命令绑定到上下文菜单项的最简洁方法是使用一个名为CommandReference的类.您可以在WPF Futures上的Codeplex上的MVVM工具包中找到它.
XAML可能如下所示:
<UserControl x:Class="View.MyView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:ViewModel;assembly=MyViewModel"
xmlns:mvvm="clr-namespace:ViewModelHelper;assembly=ViewModelHelper"
<UserControl.Resources>
<mvvm:CommandReference x:Key="MyCustomCommandReference" Command="{Binding MyCustomCommand}" />
<ContextMenu x:Key="ItemContextMenu">
<MenuItem Header="Plate">
<MenuItem Header="Inspect Now" Command="{StaticResource MyCustomCommandReference}"
CommandParameter="{Binding}">
</MenuItem>
</MenuItem>
</ContextMenu>
</UserControl.Resources>
Run Code Online (Sandbox Code Playgroud)
MyCustomCommand是ViewModel上的RelayCommand.在此示例中,ViewModel附加到代码隐藏中的视图的datacontext.
注意:此XAML是从一个正在运行的项目中复制并简化以进行说明.可能存在拼写错误或其他轻微错误.
小智 5
我最近遇到了与ListBox中的ContextMenu相同的问题.我试图在没有任何代码隐藏的情况下以MVVM方式绑定命令.我终于放弃了,我向朋友求助了.他找到了一个略微扭曲但简洁的解决方案.他通过列表框的文本菜单的DataContext的,然后通过访问ListBox的DataContext的发现在视图模型的命令.这是迄今为止我见过的最简单的解决方案.没有自定义代码,没有Tag,只有纯XAML和MVVM.
我在Github上发布了一个完整的工作样本.这是XAML的摘录.
<Window x:Class="WpfListContextMenu.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow" Height="350" Width="268">
<Grid>
<DockPanel>
<ListBox x:Name="listBox" DockPanel.Dock="Top" ItemsSource="{Binding Items}" DisplayMemberPath="Name"
SelectionMode="Extended">
<ListBox.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Show Selected" Command="{Binding Path=DataContext.ShowSelectedCommand}"
CommandParameter="{Binding Path=SelectedItems}" />
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
</DockPanel>
</Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
21045 次 |
| 最近记录: |