我有一个MainWindowVM和从中继承的多个子视图模型。 MainWindowVM继承自ViewModelBase,后者实现了INotifyPropertychanged。
每个视图都将DataContext设置为MainWindowVM中定义的CurrentViewModel和每个按钮 已经绑定了命令。
如果我将命令(以及构造函数中的其他命令处理代码)放在MainWindowVM中, 每个视图中的按钮单击按预期工作。我在MainWindowVM的构造函数中将MainControlVM设置为CurrentViewModel。 除MainControlVM和MainWindowVM外,在任何其他VM中设置命令意味着它们不会执行。
但是,我想只在他们使用的虚拟机中拥有命令。
我在MVVM上发现了许多只有一个或两个视图模型的教程,所以这种情况对他们来说不是问题。
编辑包含代码: 这是相关的代码:
XAML中具有绑定的其中一个子视图的一部分:
<Grid DataContext="{Binding CurrentViewModel}" Margin="0,0,-186,0">
<Button Content="Add" HorizontalAlignment="Left" Margin="25,249,0,0" VerticalAlignment="Top" Width="62" Height="32"
Command="{Binding AddCategoryVMCommand}" />
MainWindowVM类包含:
public ICommand AddCategoryVMCommand { get; private set; }
,在构造函数中:
AddCategoryVMCommand = new RelayCommand(() => ExecuteAddCategoryVMCommand());
和
protected void ExecuteAddCategoryVMCommand()
{
CurrentViewModel = new AddCategoryVM();
}
....和每个命令的相同类型的代码。 Aso,CurrentViewModel在MainWindowVM类中设置。这是MainWindow视图用于确定要与datatemplate一起显示的视图的属性:
public ViewModelBase CurrentViewModel
{
get { return _currentViewModel; }
set
{
if (_currentViewModel == value)
return;
_currentViewModel = value;
this.RaiseNotifyPropertyChanged("CurrentViewModel");
}
}
如何在子视图模型中声明时执行命令?
答案 0 :(得分:4)
有很多评论正在进行,所有评论都不同步,它们似乎使问题卷曲,所以我想我会尝试用一个基本的例子来解决你的问题。该示例仅处理您看起来具有的命令绑定问题。
我创建了3个ViewModel,MyViewModel1和MyViewModel2是MyViewModel的派生。在ViewModel基础中定义了一个命令,用于加载CurrentViewModel。其他2个ViewModels包含自己的命令。
public class MyViewModel : INotifyPropertyChanged
{
private MyViewModel currentViewModel;
public RelayCommand<object> MyCommand { get; set; }
public MyViewModel()
{
MyCommand = new RelayCommand<object>(MyCommandExecute);
}
public MyViewModel CurrentViewModel
{
get { return currentViewModel; }
set
{
if (value != currentViewModel)
{
currentViewModel = value;
OnPropertyChanged();
}
}
}
protected virtual void MyCommandExecute(object obj)
{
switch (int.Parse(obj.ToString()))
{
case 1:
CurrentViewModel = new MyViewModel1();
break;
case 2:
CurrentViewModel = new MyViewModel2();
break;
}
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class MyViewModel1 : MyViewModel
{
public RelayCommand<object> MyCommand1 { get; set; }
public MyViewModel1()
{
MyCommand1 = new RelayCommand<object>(MyCommand1Execute);
}
private void MyCommand1Execute(object obj)
{
Debug.WriteLine("MyCommand1");
}
}
public class MyViewModel2 : MyViewModel
{
public RelayCommand<object> MyCommand2 { get; set; }
public MyViewModel2()
{
MyCommand2 = new RelayCommand<object>(MyCommand2Execute);
}
private void MyCommand2Execute(object obj)
{
Debug.WriteLine("MyCommand2");
}
}
UserControl1背后的代码是
public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel", typeof(MyViewModel1), typeof(UserControl1));
public UserControl1()
{
InitializeComponent();
}
public MyViewModel1 ViewModel
{
get { return GetValue(ViewModelProperty) as MyViewModel1; }
set { SetValue(ViewModelProperty, value); }
}
}
我已经将ViewModel属性创建为DependencyProperty,因此我可以从MainWindow绑定它。
用户控件的Xaml是
<UserControl x:Class="StackOverflow._20937791.UserControl1" 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" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:this="clr-namespace:StackOverflow._20937791"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
<StackPanel DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type this:UserControl1}}, Path=ViewModel}">
<Button Content="View 1 Command" Command="{Binding Path=MyCommand1}" />
</StackPanel>
</UserControl>
注意我已在控件的第一个内容元素上设置了DataContext。所有子元素的绑定都是针对UserControl的ViewModel,而任何传入的绑定(来自父控件)都将从该父控件的DataContext进行评估。
需要注意的另一点是,通过在Xaml中定义DataContext,您将在Binding表达式中获得自动完成功能,这将减少错误的表达式错误。
第二个UserControl是相同的,但ViewModel的类型是MyViewModel2。
最后,MainWindow的代码是
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public MyViewModel ViewModel { get; set; }
}
Xaml是
<Window x:Class="StackOverflow._20937791.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:this="clr-namespace:StackOverflow._20937791"
DataContext="{Binding RelativeSource={RelativeSource Self}, Path=ViewModel}"
Title="MainWindow" Height="200" Width="300">
<Window.Resources>
<DataTemplate DataType="{x:Type this:MyViewModel1}">
<this:UserControl1 ViewModel="{Binding}" />
</DataTemplate>
<DataTemplate DataType="{x:Type this:MyViewModel2}">
<this:UserControl2 ViewModel="{Binding}" />
</DataTemplate>
</Window.Resources>
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="Show View 1" Command="{Binding Path=MyCommand}" CommandParameter="1" Width="100" Margin="4" />
<Button Content="Show View 2" Command="{Binding Path=MyCommand}" CommandParameter="2" Width="100" Margin="0 4" />
</StackPanel>
<ContentControl Content="{Binding Path=CurrentViewModel}" Margin="20" />
</StackPanel>
</Window>
UserControl在主窗口中引用,并且传入了ViewModel。
该应用程序显示一个类似于
的窗口
我希望这会有所帮助。
答案 1 :(得分:0)
Firt,FYI - 您的方法被称为战略模式。 现在你正在做的事情听起来不错,但是看到你的xaml很难。 也许您需要在设置vm属性后引发propertychanged事件?
答案 2 :(得分:0)
如果您发布您的代码会很有帮助。但如果我没有误解您的问题,那么您可以试试这个
<Button Command="{Binding MainControlVM.ClickCommand}"
设置绑定 MainControlVM.ClickCommand 。这里是ClickCommand命令的名称。
<强>更新强>
我认为问题在于设置CurrentViewModel。您正在Action Of Command中设置CurrentViewModel。我想你想在Command的基础上设置CurrentViewModel。我认为CommandParameter可能会更好。就像将所有按钮绑定到相同的Base ViewModel命令一样,并从每个Command传递不同的CommandParameter,然后在Command上比较CommandParameter并相应地设置CurrentViewModel。
ViewModelBase,Child1ViewModel,Child2ViewModel
public class ViewModelBase:INotifyPropertyChanged
{
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(MyAction,()=>true));
}
}
public void MyAction(object obj)
{
if(obj == null )
return;
//if CommandParameter is Cild1VM
if (obj.ToString() == "Child1VM")
CurrentViewModel = new Child1ViewModel();
//if CommandParameter is Cild1VM
else if (obj.ToString() == "Child2VM")
CurrentViewModel = new Child2ViewModel();
}
ViewModelBase _currentViewModel;
public ViewModelBase CurrentViewModel
{
get { return _currentViewModel; }
set
{
if (_currentViewModel == value)
return;
_currentViewModel = value;
this.RaiseNotifyPropertyChanged("CurrentViewModel");
}
}
public event PropertyChangedEventHandler PropertyChanged;
void RaiseNotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public class Child1ViewModel : ViewModelBase
{ }
public class Child2ViewModel : ViewModelBase
{ }
XAML
<StackPanel>
<Button Content="Foo" Command="{Binding ClickCommand}" CommandParameter="Child1VM"/>
<Button Content="Bar" Command="{Binding ClickCommand}" CommandParameter="Child2VM"/>
</StackPanel>
xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModelBase();
}
}
我希望这会给你一个想法。