WPF命令(Command)介绍、命令和数据绑定集成应用

网友投稿 1042 2022-11-18

WPF命令(Command)介绍、命令和数据绑定集成应用

WPF命令(Command)介绍、命令和数据绑定集成应用

要开始使用命令,必须做三件事:                                              一:定义一个命令                                              二:定义命令的实现                                              三:为命令创建一个触发器

WPF中命令系统的基础是一个相对简单的ICommand的接口,代码如下:

public interface ICommand { event EventHandler CanExecuteChanged; bool CanExecute(object parameter); void Execute(object parameter); }

.csharpcode, .csharpcode pre { font-size: small; color: rgba(0, 0, 0, 1); font-family: consolas, "Courier New", courier, monospace; background-color: rgba(255, 255, 255, 1) }.csharpcode pre { margin: 0 }.csharpcode .rem { color: rgba(0, 128, 0, 1) }.csharpcode .kwrd { color: rgba(0, 0, 255, 1) }.csharpcode .str { color: rgba(0, 96, 128, 1) }.csharpcode .op { color: rgba(0, 0, 192, 1) }.csharpcode .preproc { color: rgba(204, 102, 51, 1) }.csharpcode .asp { background-color: rgba(255, 255, 0, 1) }.csharpcode .html { color: rgba(128, 0, 0, 1) }.csharpcode .attr { color: rgba(255, 0, 0, 1) }.csharpcode .alt { background-color: rgba(244, 244, 244, 1); width: 100%; margin: 0 }.csharpcode .lnum { color: rgba(96, 96, 96, 1) }

CanExecute用于确定命令是否处于可执行的状态。典型的,UI控件能使用CanExecute来启用或禁用自己。也就是说,在相关的命令从CanExecute中返回False的时候,按钮将变得不可用。

Execute是命令的关键,当被调用时,它将触发命令的执行。

要定义一个新命令,可以实现ICommand接口。如希望ICommand在被调用后关闭应用程序,代码如下:

public class Exit : ICommand { event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) { return true; } public void Execute(object parameter) { Application.Current.Shutdown(); } }

.csharpcode, .csharpcode pre { font-size: small; color: rgba(0, 0, 0, 1); font-family: consolas, "Courier New", courier, monospace; background-color: rgba(255, 255, 255, 1) }.csharpcode pre { margin: 0 }.csharpcode .rem { color: rgba(0, 128, 0, 1) }.csharpcode .kwrd { color: rgba(0, 0, 255, 1) }.csharpcode .str { color: rgba(0, 96, 128, 1) }.csharpcode .op { color: rgba(0, 0, 192, 1) }.csharpcode .preproc { color: rgba(204, 102, 51, 1) }.csharpcode .asp { background-color: rgba(255, 255, 0, 1) }.csharpcode .html { color: rgba(128, 0, 0, 1) }.csharpcode .attr { color: rgba(255, 0, 0, 1) }.csharpcode .alt { background-color: rgba(244, 244, 244, 1); width: 100%; margin: 0 }.csharpcode .lnum { color: rgba(96, 96, 96, 1) }

要把一个菜单项绑定到应用程序关闭这个命令上,可以把他们的Command属性挂到Exit命令上,代码如下:

.csharpcode, .csharpcode pre { font-size: small; color: rgba(0, 0, 0, 1); font-family: consolas, "Courier New", courier, monospace; background-color: rgba(255, 255, 255, 1) }.csharpcode pre { margin: 0 }.csharpcode .rem { color: rgba(0, 128, 0, 1) }.csharpcode .kwrd { color: rgba(0, 0, 255, 1) }.csharpcode .str { color: rgba(0, 96, 128, 1) }.csharpcode .op { color: rgba(0, 0, 192, 1) }.csharpcode .preproc { color: rgba(204, 102, 51, 1) }.csharpcode .asp { background-color: rgba(255, 255, 0, 1) }.csharpcode .html { color: rgba(128, 0, 0, 1) }.csharpcode .attr { color: rgba(255, 0, 0, 1) }.csharpcode .alt { background-color: rgba(244, 244, 244, 1); width: 100%; margin: 0 }.csharpcode .lnum { color: rgba(96, 96, 96, 1) }

.csharpcode, .csharpcode pre { font-size: small; color: rgba(0, 0, 0, 1); font-family: consolas, "Courier New", courier, monospace; background-color: rgba(255, 255, 255, 1) }.csharpcode pre { margin: 0 }.csharpcode .rem { color: rgba(0, 128, 0, 1) }.csharpcode .kwrd { color: rgba(0, 0, 255, 1) }.csharpcode .str { color: rgba(0, 96, 128, 1) }.csharpcode .op { color: rgba(0, 0, 192, 1) }.csharpcode .preproc { color: rgba(204, 102, 51, 1) }.csharpcode .asp { background-color: rgba(255, 255, 0, 1) }.csharpcode .html { color: rgba(128, 0, 0, 1) }.csharpcode .attr { color: rgba(255, 0, 0, 1) }.csharpcode .alt { background-color: rgba(244, 244, 244, 1); width: 100%; margin: 0 }.csharpcode .lnum { color: rgba(96, 96, 96, 1) }

由于把命令用于多个位置比较常见,所以创建一个存储命令的静态字段也常见:

public static readonly ICommand ExitCommand = new Exit();

这样做的好处是,通过这个类型为ICommand的字段,可以让Exit命令的实现完全私有化。现在,可以把Exit标记为私有类,并把标记转化为绑定到静态字段,代码如下:

.csharpcode, .csharpcode pre { font-size: small; color: rgba(0, 0, 0, 1); font-family: consolas, "Courier New", courier, monospace; background-color: rgba(255, 255, 255, 1) }.csharpcode pre { margin: 0 }.csharpcode .rem { color: rgba(0, 128, 0, 1) }.csharpcode .kwrd { color: rgba(0, 0, 255, 1) }.csharpcode .str { color: rgba(0, 96, 128, 1) }.csharpcode .op { color: rgba(0, 0, 192, 1) }.csharpcode .preproc { color: rgba(204, 102, 51, 1) }.csharpcode .asp { background-color: rgba(255, 255, 0, 1) }.csharpcode .html { color: rgba(128, 0, 0, 1) }.csharpcode .attr { color: rgba(255, 0, 0, 1) }.csharpcode .alt { background-color: rgba(244, 244, 244, 1); width: 100%; margin: 0 }.csharpcode .lnum { color: rgba(96, 96, 96, 1) }

下面我们通过添加一个和Close命令挂接的按钮,可以为窗口编写一个模板,以实现关闭窗口的功能,代码如下:

.csharpcode, .csharpcode pre { font-size: small; color: rgba(0, 0, 0, 1); font-family: consolas, "Courier New", courier, monospace; background-color: rgba(255, 255, 255, 1) }.csharpcode pre { margin: 0 }.csharpcode .rem { color: rgba(0, 128, 0, 1) }.csharpcode .kwrd { color: rgba(0, 0, 255, 1) }.csharpcode .str { color: rgba(0, 96, 128, 1) }.csharpcode .op { color: rgba(0, 0, 192, 1) }.csharpcode .preproc { color: rgba(204, 102, 51, 1) }.csharpcode .asp { background-color: rgba(255, 255, 0, 1) }.csharpcode .html { color: rgba(128, 0, 0, 1) }.csharpcode .attr { color: rgba(255, 0, 0, 1) }.csharpcode .alt { background-color: rgba(244, 244, 244, 1); width: 100%; margin: 0 }.csharpcode .lnum { color: rgba(96, 96, 96, 1) }

我们接着要通过把命令绑定添加到窗口中让窗口关闭。

///

/// WinCommand.xaml 的交互逻辑 /// public partial class WinCommand : Window { public static readonly ICommand ExitCommand = new Exit(); public WinCommand() { InitializeComponent(); CommandBindings.Add( new CommandBinding( ApplicationCommands.Close, CloseExecuted)); } void CloseExecuted(object sender, ExecutedRoutedEventArgs e) { this.Close(); } }

.csharpcode, .csharpcode pre { font-size: small; color: rgba(0, 0, 0, 1); font-family: consolas, "Courier New", courier, monospace; background-color: rgba(255, 255, 255, 1) }.csharpcode pre { margin: 0 }.csharpcode .rem { color: rgba(0, 128, 0, 1) }.csharpcode .kwrd { color: rgba(0, 0, 255, 1) }.csharpcode .str { color: rgba(0, 96, 128, 1) }.csharpcode .op { color: rgba(0, 0, 192, 1) }.csharpcode .preproc { color: rgba(204, 102, 51, 1) }.csharpcode .asp { background-color: rgba(255, 255, 0, 1) }.csharpcode .html { color: rgba(128, 0, 0, 1) }.csharpcode .attr { color: rgba(255, 0, 0, 1) }.csharpcode .alt { background-color: rgba(244, 244, 244, 1); width: 100%; margin: 0 }.csharpcode .lnum { color: rgba(96, 96, 96, 1) }

使用命令可以清晰地把显示和行为分开。通过使用单一的名称为所需的语义动作签名,在尝试把多个控件和单个事件处理过程挂接起来的时候,可以避免很多由此引发的紧耦合问题。通常,应用程序逻辑应该总是通过命令的方式来实现的,而不是事件处理程序。对于很多需要直接挂接到事件处理过程上的常见例子,用触发器来处理更好。

命令与数据绑定

使用命令的一个令人振奋和强大的特性 就是和数据绑定集成。由于Command和CommandParameter都是元素上的属性,所以他们都能被设置为一些绑定到他们的数据。因此,可以使用绑定的数据内容来确定应该发生的动作。

为了演示他们是如何融合到一起的,将以C:\下面的文件的应用程序来开头。首先,定义一个显示内容的ListBox,和一个显示了每个文件名的数据模板,代码如下:

在后台,把ItemSource属性设置为文件列表:

public WinCommandAndBinding() { InitializeComponent(); FileInfo[] fileList = new DirectoryInfo("C:\\").GetFiles("*.*"); lbFile.ItemsSource = fileList; }

.csharpcode, .csharpcode pre { font-size: small; color: rgba(0, 0, 0, 1); font-family: consolas, "Courier New", courier, monospace; background-color: rgba(255, 255, 255, 1) }.csharpcode pre { margin: 0 }.csharpcode .rem { color: rgba(0, 128, 0, 1) }.csharpcode .kwrd { color: rgba(0, 0, 255, 1) }.csharpcode .str { color: rgba(0, 96, 128, 1) }.csharpcode .op { color: rgba(0, 0, 192, 1) }.csharpcode .preproc { color: rgba(204, 102, 51, 1) }.csharpcode .asp { background-color: rgba(255, 255, 0, 1) }.csharpcode .html { color: rgba(128, 0, 0, 1) }.csharpcode .attr { color: rgba(255, 0, 0, 1) }.csharpcode .alt { background-color: rgba(244, 244, 244, 1); width: 100%; margin: 0 }.csharpcode .lnum { color: rgba(96, 96, 96, 1) }

.csharpcode, .csharpcode pre { font-size: small; color: rgba(0, 0, 0, 1); font-family: consolas, "Courier New", courier, monospace; background-color: rgba(255, 255, 255, 1) }.csharpcode pre { margin: 0 }.csharpcode .rem { color: rgba(0, 128, 0, 1) }.csharpcode .kwrd { color: rgba(0, 0, 255, 1) }.csharpcode .str { color: rgba(0, 96, 128, 1) }.csharpcode .op { color: rgba(0, 0, 192, 1) }.csharpcode .preproc { color: rgba(204, 102, 51, 1) }.csharpcode .asp { background-color: rgba(255, 255, 0, 1) }.csharpcode .html { color: rgba(128, 0, 0, 1) }.csharpcode .attr { color: rgba(255, 0, 0, 1) }.csharpcode .alt { background-color: rgba(244, 244, 244, 1); width: 100%; margin: 0 }.csharpcode .lnum { color: rgba(96, 96, 96, 1) }

运行如下:

现在,再添加一个按钮用来显示文件,但不希望任何文件都被打开。所以,要在加载的文件上提供某种类型的过滤器。现实现两个命令Open和Blocked并为他们提供某种类型的处理过程,代码如下:

public static readonly RoutedCommand OpenCommand = new RoutedCommand("Open", typeof(WinCommandAndBinding)); public static readonly RoutedCommand BlockedCommand = new RoutedCommand("Blocked", typeof(WinCommandAndBinding)); public WinCommandAndBinding() { InitializeComponent(); CommandBindings.Add(new CommandBinding (OpenCommand, delegate(object sender,ExecutedRoutedEventArgs e){ Process.Start("notepad.exe",(string)e.Parameter);})); CommandBindings.Add(new CommandBinding(BlockedCommand, delegate(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show((string)e.Parameter, "Blocked"); })); FileInfo[] fileList = new DirectoryInfo("C:\\").GetFiles("*.*"); lbFile.ItemsSource = fileList; } }

在定义好两个命令后,就可以更新文件的数据模板来包含按钮了。在命令参数(文件名)中使用数据绑定。对应命令本身,由于希望某些条目用OpenCommand,而其他条目用BlockedCommand,所以将使用IValueConvert把文件名转换为ICommand,代码如下:

.csharpcode, .csharpcode pre { font-size: small; color: rgba(0, 0, 0, 1); font-family: consolas, "Courier New", courier, monospace; background-color: rgba(255, 255, 255, 1) }.csharpcode pre { margin: 0 }.csharpcode .rem { color: rgba(0, 128, 0, 1) }.csharpcode .kwrd { color: rgba(0, 0, 255, 1) }.csharpcode .str { color: rgba(0, 96, 128, 1) }.csharpcode .op { color: rgba(0, 0, 192, 1) }.csharpcode .preproc { color: rgba(204, 102, 51, 1) }.csharpcode .asp { background-color: rgba(255, 255, 0, 1) }.csharpcode .html { color: rgba(128, 0, 0, 1) }.csharpcode .attr { color: rgba(255, 0, 0, 1) }.csharpcode .alt { background-color: rgba(244, 244, 244, 1); width: 100%; margin: 0 }.csharpcode .lnum { color: rgba(96, 96, 96, 1) }

下面是转换器:

public class FileToCommandConverter : IValueConverter { public object Convert(object value ,Type targetType,object parameter,CultureInfo culture) { string ext = ((FileInfo)value).Extension.ToLowerInvariant(); if (ext == ".txt") return WinCommandAndBinding.OpenCommand; else return WinCommandAndBinding.BlockedCommand; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }

.csharpcode, .csharpcode pre { font-size: small; color: rgba(0, 0, 0, 1); font-family: consolas, "Courier New", courier, monospace; background-color: rgba(255, 255, 255, 1) }.csharpcode pre { margin: 0 }.csharpcode .rem { color: rgba(0, 128, 0, 1) }.csharpcode .kwrd { color: rgba(0, 0, 255, 1) }.csharpcode .str { color: rgba(0, 96, 128, 1) }.csharpcode .op { color: rgba(0, 0, 192, 1) }.csharpcode .preproc { color: rgba(204, 102, 51, 1) }.csharpcode .asp { background-color: rgba(255, 255, 0, 1) }.csharpcode .html { color: rgba(128, 0, 0, 1) }.csharpcode .attr { color: rgba(255, 0, 0, 1) }.csharpcode .alt { background-color: rgba(244, 244, 244, 1); width: 100%; margin: 0 }.csharpcode .lnum { color: rgba(96, 96, 96, 1) }

运行结果:

这个例子虽然有点微不足道,不过可以使用CanExecute方法轻松地完成类似的行为,并针对“坏”文件禁用这个命令。然而,这里最重要的一点是,可以返回任何命令。可以使用任何基于数据的逻辑来确定任何元素的行为。

另外我们可以考虑下能不能用数据触发器实现呢?呵呵,可以的,这等于把命令、数据绑定和触发器三者融合到一起了?是不是很强大,呵呵下面是代码:

上一篇:买灯泡支架
下一篇:对比QQ浏览器、Chrome和FireFox
相关文章

 发表评论

暂时没有评论,来抢沙发吧~