Splinter -WPF MVVM 框架
了解如何使用开源 Splinter 框架编写 MVVM 应用程序。
引言
本文教导开发人员如何使用 Splinter 框架开始使用 C# 和 WPF 编写应用程序,以便轻松遵循 MVVM 模式。
背景
作为一项学术研究,我决定编写自己的 MVVM 框架来启动 WPF 应用程序开发。随着时间的推移,并且在多个应用程序中使用后,该框架演变成了 Splinter。我最近已将其开源,可在此处获取: https://github.com/MrShoes/Splinter。本文是入门指南,其中也包含在链接中作为 Word 文档。
引言
本文档的读者是谁?
本文档适用于希望使用 Splinter MVVM 框架来促进 WPF MVVM 应用程序开发的开发人员。它讨论了 Splinter 的使用、其类、功能和目的。使用 Splinter 可以让开发人员通过易于使用的表示逻辑流程开始开发强大的数据驱动应用程序。
什么是 MVVM?
MVVM(Model-View-View Model,模型-视图-视图模型)是一种软件架构设计模式。它是 MVC(Model-View-Controller,模型-视图-控制器)在支持事件驱动编程的 UI 平台上的一个特定衍生。
MVVM 要求将业务逻辑与表示逻辑分离。Splinter,本文档中讨论的 MVVM 框架,通过允许 UI 代码保留在 View 标记中(在 WPF 中使用 XAML)来实现这一点,而 View 背后的逻辑由 View Model 类处理,该类允许轻松的数据绑定和 UI 值的自动更新。命令允许将按钮点击绑定到行为,并可以进一步将 View 与 View Model 分离。此外,Splinter 包含一个 Broker 类,该类使用发布-订阅模型,允许 View Model 发布事件,其他 View Model 可以订阅这些事件。如您将很快看到的,Broker 是使用 Splinter 构建的应用程序的核心;它取代了 MVVMLight 中的 Messenger 和 Prism 中的 EventAggregator,并增加了允许简单线程控制消息的功能。
必备组件
软件
- Visual Studio 2013+
- Splinter MVVM 框架 (https://github.com/MrShoes/Splinter)
知识
- C# 和 .NET 经验
- WPF 经验
使用 Splinter
创建 View Model
在本节中,我们将创建一个简单的 View Model,稍后可以将其绑定到 View。
INotifyPropertyChanged 接口
在 MVVM 中很常见,INotifyPropertyChanged 接口用于提供 .NET 类和 XAML 视图之间的通信机制。该接口允许开发人员创建一个属性,当该属性更改时,该属性会通知任何订阅的类。在 WPF 中,当创建 Binding 时,这种通信是隐式创建的。在 C#(甚至 VB)View Model 类中,我们只需定义一种在属性更改时引发 PropertyChanged 事件的方式。这意味着每次属性更改时,UI 都会更新以反映该更改。在 Splinter MVVM 框架中,已经创建了一个名为“ViewModel”的基类,它已经实现了此功能。还有一个名为“SubscribingViewModel”的类,用于订阅 Broker 的 View Models(参见第 4.3 节),并在 View Model 处置时取消订阅。
继承 ViewModel 类
Splinter 中的“ViewModel”类已经实现了 INotifyPropertyChanged 接口,并提供了一种简单的机制,允许开发人员快速引发任何更改属性的 PropertyChanged 事件。要继承该类,必须引用 Splinter.dll 程序集,并且必须使用 Splinter 命名空间。然后,您可以使用“:”符号继承该类,例如:
public class MyViewModel : ViewModel
{}
为了使事情更简单,将任何 View Model 类命名为以“ViewModel”结尾,这被认为是良好的实践。如果您对 View 类也这样做,那么更容易看出哪个 View 属于哪个 View Model。
添加 View Model 属性
一旦一个类继承自“ViewModel”类,任何可绑定属性——即您希望在 UI 上表示的任何属性——都必须以特定方式实现,以便在需要时引发 PropertyChanged 事件。请注意,View Model 是表示 View 数据的类,因此大多数属性都可能在 UI 上表示。如果您发现有许多非显示属性或字段,那么您可能正在破坏 MVVM 模式,并将 View Model 类用于其目的之外的用途。
将通知 UI 更改的属性可能会遵循此模式
- 将使用私有字段来存储值。
- “get”访问器将允许公共访问该字段。
- “set”访问器将使用 ViewModel.SetPropertyAndNotify() 方法,将私有字段的引用以及所需的新值作为参数传入。
这种字段类型的示例将如下所示:
public class MyViewModel : ViewModel
{
private string _name;
public string Name
{
get { return _name; }
set { SetPropertyAndNotify(ref _name, value); }
}
}
为多个属性引发 PropertyChanged
有时,当一个属性更改时,您可能希望为多个属性引发 PropertyChanged 事件,例如,如果您有一个计算属性。考虑这个属性:
public bool NameIsNotBlank
{ get { return !String.IsNullOrWhitespace(Name); }}
显然,这个属性没有“set”访问器,也没有后备字段。同样清楚的是,如果 UI 依赖于此属性,那么每次 Name 属性更改时,它都需要引发 PropertyChanged 事件。为了实现这一点,我们只需在 Name 属性的“set”访问器中添加对 ViewModel.OnPropertyChanged() 方法的调用,并传递一个属性名。我们的 View Model 类现在将如下所示:
public class MyViewModel : ViewModel
{
private string _name;
public string Name
{
get { return _name; }
set {
SetPropertyAndNotify(ref _name, value);
OnPropertyChanged("NameIsNotBlank");
}
}
public bool NameIsNotBlank
{ get { return !String.IsNullOrWhitespace(Name); }}
}
使用 Splinter 可以更轻松地实现这一点。最简单的方法是使用 ViewModel.SetPropertyAndNotify() 的重载,该重载接受任何 IEnumerable<string> 作为要通知更改的属性名称。代码可能如下所示:
public class MyViewModel : ViewModel
{
private string _name;
public string Name
{
get { return _name; }
set {
SetPropertyAndNotify(ref _name, value, new[] {"NameIsNotBlank"} );
}
}
public bool NameIsNotBlank
{ get { return !String.IsNullOrWhitespace(Name); }}
}
如果要通知更改的附加属性是单个属性,我们可以将属性直接传递到另一个重载中。
public class MyViewModel : ViewModel
{
private string _name;
public string Name
{
get { return _name; }
set {
SetPropertyAndNotify(ref _name, value, () => NameIsNotBlank );
}
}
public bool NameIsNotBlank
{ get { return !String.IsNullOrWhitespace(Name); }}
}
还可以使用类似的语法传递属性的 IEnumerable。
public class MyViewModel : ViewModel
{
private string _name;
public string Name
{
get { return _name; }
set {
SetPropertyAndNotify(ref _name, value,
new Expression<Func<object>>[] { () => NameIsNotBlank } );
}
}
public bool NameIsNotBlank
{
get { return !String.IsNullOrWhitespace(Name); }}
}
}
绑定到 View
现在我们有了一个简单的 View Model,我们可以开始创建一个 View 来在用户界面上表示它。
将 View Model 用作 DataContext
View 类通常是 Window 或 User Control,具体取决于它的使用方式。在此示例中,我们将有一个名为 MyView 的 Window。
为了在 XAML View 中使用我们的 View Model,我们首先需要将其作为 XML 命名空间引用。使用“xmlns” XAML 语法来实现,由于这是一个 .NET(CLR)命名空间,我们需要使用“clr-namespace”将其定义为此类。在本例中,我们将使用以下行:
xmlns:viewModels="clr-namespace:MyApplication.ViewModels"
我们将将其与其他命名空间声明一起放在 <Window> 标签中。
一旦我们在 XAML 中获得了命名空间的引用,我们就需要声明一个实例,并且由于我们需要使用 XAML Binding,我们应该将其设置为 Window 的 Data Context。
在 Window 标签内,为了约定,我们在顶部使用以下语法声明实例并将其设置为 Data Context:
<Window.DataContext>
<viewModels:MyViewModel/>
</Window.DataContext>
请注意,Splinter 还提供了 Splinter.WpfControls 命名空间中的“DisposingWindow”和“DisposingUserControl”,它们通过调用 DataContext 的 Dispose() 来实现 IDisposable。
将属性绑定到 UI 元素
由于我们将 Window 的 Data Context 设置为“MyViewModel”的实例(请注意,我们已在标记中创建了一个实例,而未使用 C# 的“new”关键字),我们可以创建一个绑定到 View Model 的 Name 属性的控件。由于这是一个我们将要编辑的字符串类型,TextBox 似乎是最合适的选择。在这种情况下,绑定很简单,使用以下语法:
<TextBox Text="{Binding Name}"/>
当前的 MyView XAML 应该如下所示:
<Window x:Class=MyApplication.Views.MyView
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:viewModels="clr-namespace:MyApplication.ViewModels">
<Window.DataContext>
<viewModels:MyViewModel/>
</Window.DataContext>
<Grid>
<TextBox Text="{Binding Name}"/>
</Grid>
</Window>
如果将 MyView.xaml 设置为 App.xaml 文件中的 StartupUri 并运行应用程序,您将看到一个带有文本框的窗口,您可以在其中输入字段。您还看不到,但您在文本框中所做的任何更改都会更改 View Model 的 Name 属性。让我们证明这一点,并证明我们的另一个 View Model 属性(“NameIsNotBlank”)也正在工作。
向 MyView 类添加一个 Button,使用以下语法:
<Button Grid.Row="1" Content="Say Hello"
IsEnabled="{Binding NameIsNotBlank}"/>
再次运行应用程序,您应该会看到当任何文本添加到文本框时,按钮都会启用,并在清除文本框时禁用。请注意,您可能需要将 TextBox 的 Binding 更改为“{Binding Name, UpdateSourceTrigger=PropertyChanged}”才能使即时更改生效,并且您需要向现有 Grid 添加第二行。
绑定集合
每当需要在屏幕上显示对象集合时,都应将 ObservableCollection<T> 用作 View Model 类的属性(并且很可能包含继承自 ViewModel 的类集合)。我们将扩展当前应用程序以展示如何执行此操作。首先,我们将创建一个名为 ItemViewModel 的新 View Model 类。
public class ItemViewModel : ViewModel
{
private string _displayText;
public string DisplayText
{
get { return _displayText; }
set { SetPropertyAndNotify(ref _displayText, value); }
}
}
我们将扩展 MyViewModel 类以包含 ItemViewModels 的集合。
public class MyViewModel : ViewModel
{
private string _name;
public string Name
{
get { return _name; }
set {
SetPropertyAndNotify(ref _name, value);
OnPropertyChanged("NameIsNotBlank");
}
}
public bool NameIsNotBlank
{ get { return !String.IsNullOrWhitespace(Name); }}
public ObservableCollection<ItemViewModel> Items { get; set; }
public MyViewModel
{
Items = new ObservableCollection<ItemViewModel>();
Items.Add(new ItemViewModel { DisplayText = "foo" });
Items.Add(new ItemViewModel { DisplayText = "bar" });
Items.Add(new ItemViewModel { DisplayText = "baz" });
}
}
请注意,我们不需要对 Items 属性执行任何操作,因为 ObservableCollection 在更改时已引发事件,并且如果绑定设置正确,则会自动更新。将 MyView.xaml 更新为以下内容:
<Window x:Class=MyApplication.Views.MyView
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:viewModels="clr-namespace:MyApplication.ViewModels">
<Window.DataContext>
<viewModels:MyViewModel/>
</Window.DataContext>
<Grid>
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
<Button Grid.Column="1" Content="Say Hello"
IsEnabled="{Binding NameIsNotBlank}"/>
<ListView Grid.Row="1" ItemsSource="{Binding Items, Mode=OneWay}" DisplayMemberPath="DisplayText"/>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
</Grid>
</Window>
再次运行应用程序,您将看到集合中的 3 个项目显示在 ListView 中。
现在您已经了解了如何创建 View Model 并将其用作 View 的 Data Context。您已经了解了如何将属性绑定到 UI,以及如何创建响应式 UI。请注意,本介绍并未涵盖所有可能性,因为 WPF 的 Binding 提供了很大的灵活性和控制力,但这应该能作为入门。
通过 Broker 发送消息
Broker 是 Splinter MVVM 框架的重要组成部分;它是一个单例消息处理类,可以实现类型之间的轻松、匿名通信。换句话说,它可以允许 View Model 类响应在其他 View Models 中处理的更改或操作,而无需任何一个类拥有引用。
发布-订阅模式
Broker 类基于“发布-订阅”模式。这意味着一个类可以发布消息,而不知道(如果有的话)哪些类订阅了它,并且每个订阅的类都可以以某种方式响应消息。
使用 Broker
要使用 Broker,只需引用 Splinter.dll 并在类中使用 Splinter.Messaging 命名空间。这可以访问单例 Broker 实例(通过 Broker.Current 访问)。抽象 Message 类,Broker 处理它,并且所有要发送的消息都应该继承自它,该类在 Splinter.Messaging.Messages 命名空间中可用。
订阅消息类型
Splinter.Messaging.Messages 命名空间包含库中的简单 Message 类:TextMessage。TextMessage 类是一个具有两个属性的消息:“Publisher”,它通常保存对发布消息的对象的引用(尽管不良实践可能将其设置为 null),以及“Text”,一个简单的字符串。
如果一个类使用 Broker 订阅 TextMessage 类型,那么每当发布 TextMessage 时,该类都会收到通知(除非发布者针对特定类型或对象),并会对其执行操作。然后,它将能够访问 TextMessage 的属性,包括 Publisher 和 Text。
要订阅一个类,您可以使用 Broker.Subscribe<TMessage>() 方法,其中 TMethod 是 Message 类型。您必须传入对订阅者的引用(使用“this”)以及要执行的 Action<TMessage>。考虑这个类:
class Responder : IDisposable
{
public Responder()
{
Broker.Current.Subscribe<TextMessage>(this, OutputText);
}
private void OutputText(TextMessage msg)
{
Console.WriteLine(msg.Text);
}
public void Dispose()
{
Broker.Current.Unsubscribe(this);
}
}
上面显示的 Responder 类执行以下操作:
- 在其构造函数中订阅 TextMessage 类型的消息。
- 实现 IDisposable,并在处置时取消订阅所有消息(良好实践)。
- 有一个名为 OutputText 的方法,它接受一个 TextMessage 作为参数,这就是订阅的操作。
这意味着,只要 Responder 实例已创建且未处置,每次发布 TextMessage 时,该实例就会通过 Console.WriteLine() 输出 Text 属性。如果您有 x 个 Responder 实例,Text 属性将被输出 x 次。
请注意,lambda 表达式可用于订阅,前提是它们符合签名。考虑 Responder 编写如下的好处:
class Responder : IDisposable
{
public Responder()
{
Broker.Current.Subscribe<TextMessage>(this, msg => Console.WriteLine(msg.Text));
}
public void Dispose()
{
Broker.Current.Unsubscribe(this);
}
}
这要简洁得多,对于简单的消息处理方法来说,可能是首选。但是,避免对复杂消息处理程序使用 lambda 表达式,因为这可能难以阅读,并且会降低调试时堆栈的可读性。
Splinter.Messaging.Messages 还包含一个 ObjectRelayMessage,允许发送任何对象。
发布消息
发布消息比订阅更简单,因为发布者不知道(如果有的话)将采取任何操作的哪些对象,因此发布对象没有定义任何操作。使用 Broker.Publish<TMessage>() 方法发布任何 Message 类型,请注意 TMessage 不需要定义,因为它可从正在发布的消息中推断出来。
Broker.Current.Publish(new TextMessage("A TextMessage has been published.", this));
这就是所需的所有内容。现在,任何已订阅的对象都将收到一个 TextMessage,其中包含一个 Publisher 属性(指向发布对象的引用)和一个 Text 属性(保存字符串“A TextMessage has been published.”)。如果您有一个 Responder 类的实例已创建,它将通过 Console.WriteLine() 输出该文本。
自定义消息
显然,如果所有您能发送的都是 TextMessage 实例,那么 Broker 的作用就不会很大。任何订阅者都会响应所有消息,并且只能发送字符串。好消息是开发人员可以创建自己的消息供 Broker 使用,方法是继承抽象 Message 类。然后,它可以包含任意数量的属性,尽管出于性能考虑,建议尽量保持属性数量少且简单,同时能够传达正确的信息。
class NumberMessage : Message
{
public int Number { get; set; }
public NumberMessage(int number, object publisher = null)
: base(publisher)
{
Number = number;
}
}
上面定义的 NumberMessage 类是如何继承 Message 类的示例,它是一个包含整数属性的简单消息。请注意,构造函数允许设置发布对象,并将发布者发送到基构造函数。在这种情况下,默认发布者为 null。
高级 Broker 行为
Broker 类的最常见用法已经定义,现在是时候研究其中一些更高级的用法了。
发布到类型
通过标准发布操作(即调用 Broker.Publish() 方法时),消息将由所有活动订阅者处理。这可能不是您应用程序想要的行为,Broker 的 Publish() 方法有一些重载允许您进一步控制它。一个例子是能够以只允许特定类型的订阅者接收消息通知的方式发布。要做到这一点,请遵循以下语法:
Broker.Current.Publish(new TextMessage("Sent to type.", this), typeof(Responder));
请注意,Publish() 方法中有一个新参数,它接受一个 Type。任何指定类型的订阅对象都将接收该消息,而其他订阅类型的对象将不会被通知已发送。通过这种方式,开发人员可以在发布消息时仅定位特定类型。
发布到实例
通过 Broker 仅发布到特定实例也是可能的,前提是您拥有该实例的引用。这是一个极端情况,因为很可能如果发布对象拥有对订阅对象的引用,它更倾向于调用其公共方法而不是使用 Broker。
var responder = new Responder();
Broker.Current.Publish(new TextMessage("Sent to instance.", this), responder);
此代码演示了如何发送到特定实例。在此代码中,只有名为“responder”的 Responder 实例将接收 TextMessage,即使存在 TextMessage 类型的其他订阅者。
控制线程
之前的代码都展示了消息在发布它们的任何线程上发布和处理。Broker 提供了额外的功能来控制用于发布和响应消息的线程类型。发布者可以选择在新线程上发送所有消息,这意味着所有订阅者的操作都将在该新线程上发生。但是,任何订阅者也可以选择在 UI 线程(WPF Dispatcher 线程)上处理消息,以允许更改 UI 元素。
线程决策是通过从“ThreadOption”枚举中选择来处理的。有三个选项:
- “Current”– 将继续使用当前线程。这是发布和处理消息时的默认选项。
- “New”– .NET ThreadPool 将创建一个新线程来发布和处理消息。
- “Dispatcher”– Broker 将尝试使用 WPF 应用程序的 Dispatcher 来发布或处理消息。如果无法使用 WPF Dispatcher,例如,如果代码是从非 WPF 应用程序调用的,则线程选项将默认为 Current。
要控制发布时使用的线程,请使用以下语法:
Broker.Current.Publish(new TextMessage("Sent on new thread."), ThreadOption.New);
要控制订阅者的线程选项,请使用以下语法:
Broker.Current.Subscribe<TextMessage>(this, o => Console.WriteLine(o.Text), ThreadOption.Current);
取消订阅
在 Responder 类中,我们添加了一个 Dispose() 方法,该方法取消了 Responder 实例对所有消息类型的订阅。要取消订阅所有消息,只需调用 Unsubscribe 并传入对该对象的引用。从实例本身,您将使用:
Broker.Current.Unsubscribe(this);
还可以通过使用 TMessage 泛型参数来删除仅针对特定消息类型的订阅。语法如下:
Broker.Current.Unsubscribe<TextMessage>(this);
更进一步,如果您的订阅者有多个已订阅 Broker 队列的消息处理程序,而您只想删除其中一个处理程序,您也可以做到这一点,方法是指定消息处理 Action 以及 TMessage 类型参数。语法是:
Broker.Current.Unsubscribe<TextMessage>(this, OutputText);
Commands
到目前为止,我们已经介绍了如何创建一个简单的 View Model 类并使用强大的 XAML 功能将其属性绑定到 View。然而,很少有用户界面应用程序会局限于这种简单性,并且通常会包含像按钮这样的 UI 功能。当按下按钮时,用户期望发生一些事情。Splinter MVVM 框架提供了一种编写这种内容的方法:Command 类。
创建 Command
Command 类是框架中的另一个抽象类,位于 Splinter.dll 中,位于 Splinter 命名空间(与 ViewModel 类一起)。要创建可以绑定到 WPF 控件的 Command,请继承 Command 类。
class SayHelloCommand : Command
{
public override void Execute(object parameter)
{
MessageBox.Show("Hello.");
}
}
继承 Command 的类必须重写 Execute() 方法。此方法定义了当 Command 被调用时(例如,当用户单击 Command 所绑定的按钮时)执行的代码。该方法还接受一个参数,该参数的类型为 System.Object,这将在下一节中讨论。
Command 参数
通常,在执行任何操作之前,您需要向 Command 提供某种信息,这就是 Command 参数的用途。在 Command 类中,这将被传递到 Execute() 方法,并来自 UI 作为 Command 所绑定控件的 Dependency Property。默认情况下,如果未在控件上设置参数,则参数为 null。在其他情况下,参数可以是任何类型的对象,无论是 View Model、其属性之一,还是来自 View 本身的对象。就像 Message 一样,传递尽可能简单的对象,同时确保提供所有必要的信息,这被认为是良好的实践。
对于 SayHelloCommand,我们将要传递一个名字来问好(实际上,我们将传递前面示例中 MyViewModel 的 Name 属性,并将 SayHelloCommand 绑定到 MyView 中的按钮)。
考虑到这一点,最好的做法是在执行必要的操作之前将“parameter”强制转换为字符串。进行安全强制转换并返回(如果结果为 null)通常是个好主意。
public override void Execute(object parameter)
{
var nameString = parameter as string;
if(nameString == null) return;
MessageBox.Show(String.Format("Hello, {0}.", nameString));
}
将 Command 绑定到 Button
为了让按钮能够调用 Command,需要进行绑定。要将 SayHelloCommand 绑定到 MyView 中的 Button,您可以首先创建一个 SayHelloCommand 实例来使用(我建议将其添加到 Window.Resources 部分)。请注意,我已使用“xmlns”将命令命名空间添加到文件中,并为其提供了别名“cmd”。
<Window.Resources>
<cmd:SayHelloCommand x:Key="SayHelloCommand"/>
</Window.Resources>
此语法创建了一个 SayHelloCommand 实例,该实例可供我们的 Window 及其所有子项访问,使用其键“SayHelloCommand”作为资源进行访问。现在,要将 Command 和 MyViewModel 的 Name 属性作为参数进行绑定,请按如下方式更改按钮:
<Button Grid.Column="1" Content="Say Hello"
IsEnabled="{Binding NameIsNotBlank}"
Command="{StaticResource SayHelloCommand}"
CommandParameter="{Binding Name}"/>
运行应用程序,输入一个名字,然后单击按钮。现在,应用程序应该会因为绑定而按名字向您致意。
使用 Command 发布消息
由于 MVVM 和 Splinter 框架的解耦特性,很可能 Command 类经常被用来通过 Broker 发布消息。这允许一个事件,例如按钮单击,间接通知其他 View Model 类事件,甚至轻松地在 View Models 之间传递数据。我们将利用我们已经学到的知识来了解如何做到这一点。
首先,我们将更改 SayHelloCommand 类,使其发布到 Broker 而不是简单地创建 MessageBox。SayHelloCommand 的 Execute 方法的代码现在将如下所示:
public override void Execute(object parameter)
{
var nameString = parameter as string;
if(nameString == null) return;
Broker.Current.Publish(new TextMessage(nameString, this));
}
现在运行应用程序,按钮似乎什么也没做;它确实做了一些事情,它发布了一条消息。但是,目前没有订阅者供 Broker 通知。将以下内容添加到 MyViewModel 的构造函数中:
Broker.Current.Subscribe(this, msg => MessageBox.Show(
String.Format("Hello, {0}.", msg.Text)));
我们已将事件处理移至 View Model,以演示如何从 Command 使用 Broker。请注意,当然,任何 Message 类型都可以发布,从而允许构建更复杂的 Event -> Publish -> Subscriber Action 模型。
重写 CanExecute 方法
Command 类是抽象的,它有一个处理是否可以执行的结构。如果 Command 绑定到 Button 控件并且其 CanExecute 返回 false,则按钮将显示为禁用状态。默认情况下,Command 类的 CanExecute 将返回 true。但是,在需要时也可以通过使用以下语法进行设置:
ChangeCanExecute(false);
传递给此方法的布尔值将设置 CanExecute 的返回值,因此,您可以通过代码控制 Command 是否可执行。
摘要
希望本文能为您开始使用 Splinter 提供所需的一切。如有任何问题、错误或改进建议,请随时与我联系。