Prism程序入口、View ViewModel关联、数据绑定、数据校验、cmd

Prism程序入口、View ViewModel关联、数据绑定、数据校验、cmd

这是一篇朝夕Jovan老师讲Prism的课堂学习笔记。

关于Prism框架

Prism.Core:【Prism.dll】实现MVVM的核心功能,属于一个与平台无关的项目

Prism.Wpf:【Prism.Wpf】包含了DialogService,Region,Module,Navigation,其他的一些WPF的功能

Prism.Unity:【Prism.Unity.Wpf】,IOC容器

Prism.Unity=>Prism.Wpf=>Prism.Core

创建启动程序

第一种初始化方式:8.0版本以前只能使用PrismBootstrapper

  1. 添加启动类:Bootstrapper
public class Bootstrapper : PrismBootstrapper
{
  protected override DependencyObject CreateShell()
  {        
      return Container.Resolve<FirstWindow>();
  }

  protected override void RegisterTypes(IContainerRegistry containerRegistry)
  {
      
  }
}
  1. 修改App.cs
public partial class App
{
	protected override void OnStartup(StartupEventArgs e)
  {
  base.OnStartup(e);

  var bs = new Bootstrapper();
  bs.Run();
  }  
}

第二种初始化方式:8.0版本开始提供一种新的方式PrismApplication【全局资源管控,项目启动】

两者类似,只不过把Bootstrapper类的东西,拿到App.cs实现

  1. App.xaml 中删除程序的入口:StartupUrl="Main.xaml"
  2. App.xaml 中引入Prism名称空间
<prism:PrismApplication x:Class="PrismLesson.App"
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:local="clr-namespace:Zhaoxi.PrismLesson"
           xmlns:prism="http://prismlibrary.com/"
           >
  <Application.Resources>

  </Application.Resources>
</prism:PrismApplication>
  1. 修改App.cs,继承PrismApplication,并重写CreateShell方法
public partial class App : PrismApplication
{
  protected override Window CreateShell()
	{
  return Container.Resolve<FirstWindow>();
	}

protected override void RegisterTypes(IContainerRegistry containerRegistry)
	{

	}
}

View与ViewModel的多种关联方法

使用方法

  1. View中引入名称空间:xmlns:prism="http://prismlibrary.com/"
  2. 设置为自动关联:prism:ViewModelLocator.AutoWireViewModel="True"

注意事项:

  1. 必须是Views和ViewModels目录
  2. 需要保证命名规范的正确性
    1. View可以以View结尾,也可以不写。
    2. ViewModel必须以View的名称+”ViewModel”进行命名

自定义ViewModel文件后缀规则

App.cs 中重写ConfigureViewModelLocator方法

  1. View的名字
  2. 在那个名称空间
  3. ViewModel的名字
protected override void ConfigureViewModelLocator()
{
    base.ConfigureViewModelLocator();

    //自定义ViewModel文件后缀,自动匹配
    //第一种方式
    //ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) => 
    //{
    	//两个参数表示在那个名称空间
    //    var viewName = viewType.FullName.Replace("YtViews","YtViewmodels");
    //    var viewAssemblyName = viewType.GetType().Assembly.FullName;
    //    var viewModelName = $"{viewName}VM,{viewAssemblyName}";    
    //    return Type.GetType(viewModelName);
    //});

    //一对一注册的多种方式 01
    //ViewModelLocationProvider.Register(typeof(MainWindow).ToString(),typeof(MainWindowViewModel));

    //02
    //ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), 
    //    () =>Container.Resolve<MainWindowViewModel>()
    //);

    //03
    //ViewModeegisterlLocationProvider.R<MainWindow,M>();ainWindowViewModel
}

一对一注册

三种代码注册方式:第三种方式最为简洁,使用1对1注册的时候,就可以取消掉prism:ViewModelLocator.AutoWireViewModel="True"

protected override void ConfigureViewModelLocator()
{
    base.ConfigureViewModelLocator();
    //01 第一种方式
    ViewModelLocationProvider.Register(typeof(FirstWindow).ToString(), typeof(FirstWindowVM));
      //02
    //ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), 
    //    () =>Container.Resolve<MainWindowViewModel>()
    //);

    //03
    //ViewModelLocationProvider.Register<FirstWindow, FirstWindowVM>();   
}

Xaml注册方式

  1. 引入命名空间:xmlns:vm="clr-namespace:Zhaoxi.PrismLesson.ZxViewModels"
  2. 设置关联
<Window.DataContext>
    <prism:ContainerProvider Type="{x:Type vm:FirstWindowVM}"/>
</Window.DataContext>

MVVM数据属性的多种方式

  1. 继承BindableBase
  2. 实现数据绑定

第一种方式:RaisePropertyChanged()

public string MyProperty
{
    get { return myVar; }
    set
    {
        myVar = value;        
        //this.RaisePropertyChanged();
        //改变的时候通知其他属性
        this.RaisePropertyChanged("其他属性名称");        
    }
}

第二种方式:SetProperty

写了SetProperty,就可以删除 myVar = value,因为ref.

//思考:为什么不直接在这里写值改变后的处理,SetProperty方法内的回调对值是否改变做了判断,只有真正改变了才会执行回调,直接写SetProperty后面,则每次无论是否改变成功都会执行。

public string MyProperty
{
    get { return myVar; }
    set
    {       
        // 第二种方式
		//this.SetProperty(ref myVar, value, "MyProperty");
        
        //多了一个回调,变化之后调用
		this.SetProperty<string>(ref myVar, value,
    		() =>
    		{
        		//  onChanged
    		});

		//思考:为什么不直接在这里写值改变后的处理,SetProperty方法内的回调对值是否改变做了判断,只有真正改变了才会执行回调,直接写SetProperty后面,则每次无论是否改变成功都会执行。
    }
}

MVVM数据验证

  1. 继承INotifyDataErrorInfo接口,实现接口:
public class FirstWindowVM : BindableBase, INotifyDataErrorInfo
{
    /// <summary>
    ///  INotifyDataErrorInfo 接口方法
    /// </summary>
    //
    //错误变化 错误属性中调用这个事件    
    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
    //检查是否有错误
    public bool HasErrors => ErrorsContainer.HasErrors;
   	//获取错误
    public IEnumerable GetErrors(string propertyName) => ErrorsContainer.GetErrors(propertyName);
}
  1. 实现一个ErrorsContainer属性
//OnPropertyChanged;
//    CanExecuteChanged;
private ErrorsContainer<string> errorsContainer;

public ErrorsContainer<string> ErrorsContainer
{
    get
    {
        if (errorsContainer == null)
            errorsContainer = new ErrorsContainer<string>((propName) =>
            {
                // 异常信息的处理
                ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propName));
            });

        return errorsContainer;
    }
    set { errorsContainer = value; }
}
  1. 对数据进行错误处理
public string MyProperty
{
    get { return myVar; }
    set
    {
        this.SetProperty(ref myVar, value, "MyProperty");

        if (value == "1231")
        {
            // 异常消息
            ErrorsContainer.SetErrors("MyProperty", new string[] { "输入值无效1231231" });
        }
        else
        {
            ErrorsContainer.ClearErrors("MyProperty");
        }
    }
}
  1. UI错误提示样式
<Window.Resources>
    <ControlTemplate TargetType="{x:Type TextBox}" x:Key="ct">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="auto"/>
                </Grid.RowDefinitions>
                <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" 
                Background="{TemplateBinding Background}" SnapsToDevicePixels="True"
                CornerRadius="5">
                    <ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"
                          VerticalContentAlignment="Center" Margin="3,5" BorderThickness="0"/>
                </Border>

                <TextBlock Grid.Row="1" Text="{Binding (Validation.Errors)[0].ErrorContent,RelativeSource={RelativeSource AncestorType=TextBox,Mode=FindAncestor}}" 
                   Foreground="Red" Margin="10,5"
                   Name="txtError"/>

            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="Validation.HasError" Value="True">
                    <Setter Property="Visibility" Value="Visible" TargetName="txtError"/>
                    <Setter Property="ToolTip" 
                    Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
</Window.Resources>
<Grid>
    <StackPanel Margin="30">
        <TextBox Text="{Binding MyProperty,UpdateSourceTrigger=PropertyChanged}" FontSize="30"
                 Template="{StaticResource ct}"/>
        <TextBlock Text="{Binding MyProperty}" FontSize="30"/>
    </StackPanel>
</Grid>

MVVM命令的多种方式

语法糖

  1. cmd 基本使用
private DelegateCommand _fieldName;
public DelegateCommand CommandName =>
    _fieldName ?? (_fieldName = new DelegateCommand(ExecuteCommandName));

void ExecuteCommandName()
{

}
  1. cmdfull
private DelegateCommand _fieldName;
public DelegateCommand CommandName =>
    _fieldName ?? (_fieldName = new DelegateCommand(ExecuteCommandName, CanExecuteCommandName));

void ExecuteCommandName()
{

}
bool CanExecuteCommandName()
{
    return true;
}

  1. cmdg
private DelegateCommand<string> _fieldName;
public DelegateCommand<string> CommandName =>
    _fieldName ?? (_fieldName = new DelegateCommand<string>(ExecuteCommandName));

void ExecuteCommandName(string parameter)
{

}
  1. cmdgfull
private DelegateCommand<string> _fieldName;
public DelegateCommand<string> CommandName =>
    _fieldName ?? (_fieldName = new DelegateCommand<string>(ExecuteCommandName, CanExecuteCommandName));

void ExecuteCommandName(string parameter)
{

}

bool CanExecuteCommandName(string parameter)
{
    return true;
}

基础使用

传统方式挂载执行委托、检查可执行委托。

默认直接执行cmd

  1. 继承基类DelegateCommand
  2. 实现命令属性
private DelegateCommand _changeValue;
public DelegateCommand ChangeValue
{
    get
    {
        if (_changeValue == null)
    		_changeValue = new DelegateCommand(DoChangeValue);
        return _changeValue;
    }
    set{_changeValue = value;}
}
private void DoChangeValue()
{
    this.Value == "1231";
}

默认判断是否执行cmd

IsCan为true,才可以执行命令。

private DelegateCommand _changeValue;
public DelegateCommand ChangeValue
{
    get
    {
        if (_changeValue == null)
    		_changeValue = new DelegateCommand(DoChangeValue, DoCanChangeValue);
        return _changeValue;
    }
    set{_changeValue = value;}
}
// 判断命令相关按钮是否可执行
private bool DoCanChangeValue()
{
    return IsCan;
}

// 命令执行体
private void DoChangeValue(string param)
{
    this.Value = "456";
}

private bool _isCan;

public bool IsCan
{
    get { return _isCan; }
    set
    {
        SetProperty(ref _isCan, value);
        ChangeValue.RaiseCanExecuteChanged();
    }
} 

监控属性变化,执行cmd

  1. 监控某个属性是否变化,如果变化出发cmd。

  2. 使用DelegateCommand对象的ObservesProperty方法,不需要ChangeValue.RaiseCanExecuteChanged()。

ObservesCanExecute监控的是某个属性的变化,而不是某个值。属性变化的时候Prism内部触发了ChangeValue.RaiseCanExecuteChanged()

精简版

if (_changeValue == null)
{
    _changeValue = new DelegateCommand(DoChangeValue, DoCanChangeValue);     
    //观察一个属性,当这个属性变化的时候触发DoCanChangeValue
    _changeValue.ObservesProperty(() => Value);
}

详细版

private string _value;
public string Value
{
    get { return _value; }
    set { SetProperty<string>(ref _value, value); }
}

private DelegateCommand _changeValue;
public DelegateCommand ChangeValue
{
    get
    {
        if (_changeValue == null)
        {
            _changeValue = new DelegateCommand(DoChangeValue,DoCanChangeValue).ObservesCanExecute(() => Value);
        }

        return _changeValue;
    }
}

//判断命令相关按钮是否可执行
private bool DoCanChangeValue()
{
    return IsCan;    
}

//命令执行体
private void DoChangeValue()
{
    this.Value = "456";
}

private string _value;

public string Value
{
    get { return _value; }
    set { SetProperty<string>(ref _value, value); }
}

监控属性是否为true,执行cmd,可以省略大量代码,但有局限性。

通过ObservesCanExecute观察属性是否为true,如果为true则执行cmd.可以删除DoCanChangeValue回调。

简化了DoCanChangeValue,但是功能性少了多了局限性。

精简版

if (_changeValue == null)
{
    _changeValue = new DelegateCommand(DoChangeValue);
    _changeValue.ObservesCanExecute(() => IsCan);
}

详细版

private string _value;
public string Value
{
    get { return _value; }
    set { SetProperty<string>(ref _value, value); }
}

private DelegateCommand _changeValue;
public DelegateCommand ChangeValue
{
    get
    {
        if (_changeValue == null)
        {
            _changeValue = new DelegateCommand(DoChangeValue).ObservesCanExecute(() => IsCan);
        }
        return _changeValue;
    }
}

//命令执行体
private void DoChangeValue()
{
    this.Value = "456";
}

private bool _isCan;

public bool IsCan
{
    get { return _isCan; }
    set
    {
        SetProperty(ref _isCan, value);
    }
}

带参数的cmd

int型,Prism会有错误提示,MvvmLight没有提示。

简化版

private DelegateCommand<string> _changeValue;
public DelegateCommand<string> ChangeValue
{
    get
    {
         if (_changeValue == null)
         {
             _changeValue = new DelegateCommand<string>(DoChangeValue);
             _changeValue.ObservesCanExecute(() => IsCan);
         }

         return _changeValue;
     }
 }

//普通
private void DoChangeValue(string param)
{
    this.Value = "456";
}

if (_changeValue == null)
{
    _changeValue = new DelegateCommand<string>(DoChangeValue);
    _changeValue.ObservesCanExecute(() => IsCan);
}
<Button Content="Button" FontSize="30" Command="{Binding ChangeValue}" CommandParameter="123"/>

普通详细版

private string _value;
public string Value
{
    get { return _value; }
    set { SetProperty<string>(ref _value, value); }
}

private DelegateCommand<string> _changeValue;
public DelegateCommand<string> ChangeValue
{
    get
    {
        if (_changeValue == null)
        {
            _changeValue = new DelegateCommand<string>		(DoChangeValue).ObservesCanExecute(() => IsCan);
        return _changeValue;
    }
}

//命令执行体
private void DoChangeValue(string param)
{
    this.Value = "param";
}

private bool _isCan;

public bool IsCan
{
    get { return _isCan; }
    set
    {
        SetProperty(ref _isCan, value);
    }
}

异步命令

简化版

//异步
if (_changeValue == null)
{
    _changeValue = new DelegateCommand<string>(async (o) => await DoChangeValue(o));
    _changeValue.ObservesCanExecute(() => IsCan);
}

private async Task DoChangeValue(string param)
{
    await Task.Delay(1000);
    this.Value = "456";
}

详细版

private string _value;
public string Value
{
    get { return _value; }
    set { SetProperty<string>(ref _value, value); }
}

private DelegateCommand<string> _changeValue;
public DelegateCommand<string> ChangeValue
{
    get
    {
        if (_changeValue == null)
        {
            _changeValue = new DelegateCommand<string>(async (param) => await DoChangeValue(param)).ObservesCanExecute(() => IsCan);

        return _changeValue;
    }
}

//命令执行体
private async Task DoChangeValue(string param)
{
    await Task.Delay(1000);
    this.Value = "param";
}

private bool _isCan;

public bool IsCan
{
    get { return _isCan; }
    set
    {
        SetProperty(ref _isCan, value);
    }
}

事件绑定Command,事件参数传值

  1. 引用InteractivityInteraction两个库中的一个,Prism默认继承InteractivityInteraction库,xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

  2. 一般不会在Button中使用,实际是向没有Command属性的对象事件进行绑定,例如TextBox。

  3. 参数类型:如果是Source就是把事件源对象传进来,还有多种参数类型可以F12跟进去看函数定义。

  4. 传递事件常见的e参数:删除TriggerParameterPath,默认传递的就是e参数

<TextBox FontSize="30" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="TextChanged">
            <!--  如果需要传EventArgs参数的话,可以将TriggerParameterPath移除,不指定  -->
            <prism:InvokeCommandAction Command="{Binding EventCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</TextBox>

xaml

xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

<Button Content="Button - Event" FontSize="30">
    <!--利用Button的Click事件做演示,实际是向没有Command属性的对象事件进行绑定-->
    <!--InteractivityInteraction-->
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <!--如果需要传EventArgs参数的话,可以将TriggerParameterPath移除,不指定-->
            <prism:InvokeCommandAction Command="{Binding EventCommand}"/>
            <!--<prism:InvokeCommandAction Command="{Binding EventCommand}" TriggerParameterPath="Source"/>-->

        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>
private DelegateCommand<object> _eventCommand;

public DelegateCommand<object> EventCommand
{
    get
    {
        if (_eventCommand == null)
        {
            _eventCommand = new DelegateCommand<object>((o) =>
            {
				//接收到的事件参数
            });
        }
        return _eventCommand;
    }
}

事件聚合器、弹出自定义窗口

精简版

事件订阅、发布的一个过程。

  1. 定义一个基本消息类型MessageEvent,继承PubSubEvent
  2. 声明IEventAggregator 类型字段
  3. 构造函数中注入一个IEventAggregator
  4. 订阅消息事件

执行命令以后,事件执行。

主要是通过 _ea.GetEvent<PubSubEvent>().Subscribe(DoMessage);中的PubSubEvent判断的,所以才需要创建单独的类,甚至是空类,主要是为了方便区分。

public class MessageEvent : PubSubEvent<string>
{
}
IEventAggregator _ea;

public FirstWindowViewModel(IEventAggregator ea)
{
    this.Value = "123";

    _ea = ea;
    // 订阅一个消息事件
    _ea.GetEvent<PubSubEvent<string>>().Subscribe(DoMessage);
}

private void DoMessage(string msg)
{
    
}

private DelegateCommand _eventCommand;

public DelegateCommand EventCommand
{
    get
    {
        if (_eventCommand == null)
        {
            _eventCommand = new DelegateCommand<object>(() =>
            {
                //弹出窗口
                //自定义窗口
                //发布
                _ea.GetEvent<PubSubEvent<string>>().Publish("123");
            });
        }
       return _eventCommand;
    }
}

keepSubscriberReferenceAlive参数控制是否为弱引用

keepSubscriberReferenceAlive:订阅事件属于一个弱引用。

  1. 如果是false,不需要管取消订阅,默认为false.
  2. 如果是true,手动处理取消订阅
ea.GetEvent<MessageEvent1>().Subscribe(DoMessage1,keepSubscriberReferenceAlive: false);

// 取消订立,如果 keepSubscriberReferenceAlive 为False或默认值 不需要取消订阅
//ea.GetEvent<MessageEvent1>().Unsubscribe(DoMessage1);
ThreadOption.PublisherThread参数控制线程的
  1. PublisherThread = 0, 默认是PublisherThread

  2. UIThread = 1,

  3. BackgroundThread = 2

ea.GetEvent<MessageEvent1>().Subscribe(DoMessage1, ThreadOption.PublisherThread, keepSubscriberReferenceAlive: false);
Predicate 对消息进行过滤Filter

对发布的事件传过来的参数进行过滤,满足要求的才会出发事件。

ea.GetEvent<MessageEvent1>().Subscribe(DoMessage1, ThreadOption.PublisherThread, keepSubscriberReferenceAlive: false, DoFilter);

private bool DoFilter(string msg)
{
    return msg.Contains("12");
}

详细版

xaml代码

<Button
        Command="{Binding Command}"
        CommandParameter="123"
        Content="Button 事件聚合触发"
        FontSize="30" />
<ItemsControl FontSize="30" ItemsSource="{Binding ValueList}" />

01 继承PubSubEvent

public class MessageEvent : PubSubEvent<string>
{
    public int MyProperty { get; set; }
}

public class MessageEvent1 : PubSubEvent<string>
{
}

02 订阅

private ObservableCollection<string> _valueList;

public ObservableCollection<string> ValueList
{
    get { return _valueList ?? (_valueList = new ObservableCollection<string>()); }
    set { _valueList = value; }
}


IEventAggregator _ea;
public FirstWindowViewModel(IEventAggregator ea, IContainerExtension containerRegistry)
{
    this.Value = "123";

    _ea = ea;
    // 订阅一个消息事件
    //ea.GetEvent<PubSubEvent<string>>().Subscribe(DoMessage);
    var me = ea.GetEvent<MessageEvent>();
    me.Subscribe(DoMessage);
    me.MyProperty = 123;

    /// ThreadOption,,默认PublisherThread,管理订立消息的执行线程
    /// keepSubscriberReferenceAlive:订阅事件属于一个弱引用
    /// Filter  消息过滤 ,如果回调返回True,才执行消息体,否则不处理此消息
    //ThreadOption.PublisherThread控制线程的,本案例中配合ValueList和ItemsControl
    ea.GetEvent<MessageEvent1>().Subscribe(DoMessage1, ThreadOption.PublisherThread, keepSubscriberReferenceAlive: false, DoFilter);

    //指定在UI线程执行
    //ea.GetEvent<MessageEvent1>().Subscribe(DoMessage1, ThreadOption.UIThread, keepSubscriberReferenceAlive: false, DoFilter);
    // 取消订立,如果 keepSubscriberReferenceAlive 为False或默认值 不需要取消订阅
    //ea.GetEvent<MessageEvent1>().Unsubscribe(DoMessage1);
}

private void DoMessage(string msg)
{

}
//UI线程执行
//private void DoMessage(string msg)
//{
	//ValueList.Add("123");
//}

private bool DoFilter(string msg)
{
    return msg.Contains("12");
}

03 发布

private DelegateCommand _command;

public DelegateCommand Command
{
    get
    {
        if (_command == null)
        {
            _command = new DelegateCommand(() =>
            {
                // 弹出窗口
                // 通过事件聚合器弹出自定义窗口
                // 而不是普通的类似MvvmLight中的 Messenger  Token   Type

                //_ea.GetEvent<PubSubEvent<string>>().Publish("123");
                //_ea.GetEvent<MessageEvent>().Publish("123");

                
                var value = _ea.GetEvent<MessageEvent>().MyProperty;
                _ea.GetEvent<MessageEvent1>().Publish("123");
                _ea.GetEvent<MessageEvent1>().Publish("456");
                _ea.GetEvent<MessageEvent1>().Publish("123");
                //Task.Run(() =>
                //{
                //    _ea.GetEvent<MessageEvent1>().Publish("123");
                //});
            });
        }
        return _command;
    }
}

复合命令

一个按钮,把所有相同的复合命令执行一遍。比如:保存按钮,把所有内容保存。

每个页面出发单独的保存,还有个隐藏功能,所有的命令都有个是否可执行的方法,如果设置了,那么只有所有按钮都可以执行以后,它才可以执行。

  1. 添加一个程序集,需要我们自定义一个接口,把它改成类库。这样复合命令就实现好了
public interface ICompositeCommands
{
    CompositeCommand DoCommand { get; }
}

public class CompositeCommands : ICompositeCommands
{
    private CompositeCommand _doCommand = new CompositeCommand();
    public CompositeCommand DoCommand
    {
        get { return _doCommand; }
    }
}
  1. App.xaml.cs中的RegisterTypes 注入
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    // 注册复合命令
    containerRegistry.RegisterSingleton<ICompositeCommands, CompositeCommands>();
}
  1. 创建一个按钮,xaml
<Grid>
    <StackPanel>
        <TextBlock Text="HeaderView" FontSize="30"/>
        <Button Content="Button" FontSize="30" Command="{Binding CompositeCommands.DoCommand}"/>
    </StackPanel>
</Grid>
  1. 创建对应的ViewModule
public class HeaderViewModel : BindableBase
{
    private ICompositeCommands _compositeCommand;

    public ICompositeCommands CompositeCommands
    {
        get { return _compositeCommand; }
        set { SetProperty(ref _compositeCommand, value); }
    }

    public HeaderViewModel(ICompositeCommands compositeCommands)
    {
        CompositeCommands = compositeCommands;
    }
}
  1. 构造函数注入ICompositeCommands,将command注册到复合命令集合中。
public DelegateCommand SaveCommand { get; private set; } 
public MenuManagementViewModel(ICompositeCommands compositeCommands)
{
     SaveCommand = new DelegateCommand(() =>
        {
            // 菜单保存
        });
	 compositeCommands.DoCommand.RegisterCommand(SaveCommand);
}
登峰造极的成就源于自律
原文地址:https://www.cnblogs.com/fishpond816/p/15216765.html