65.9K
CodeProject 正在变化。 阅读更多。
Home

Prism 中的 DelegateCommand 和 CompositeCommand

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (19投票s)

2015 年 11 月 12 日

CPOL

7分钟阅读

viewsIcon

56329

downloadIcon

980

在本文中,我们将学习 Prism 框架提供的 DelegateCommand 和 CompositeCommand。为了掌握这些概念,我们将创建一个功能最简化的演示,以突出这些概念。

引言

在本文中,我们将学习 Prism 框架提供的 DelegateCommand 和 CompositeCommand。特别是 CompositeCommand 是一个非常简单的概念。但是,当我们开始学习 Prism 时,会发现它包含在有很多其他功能/特性的示例中(即 Prism 提供的标准 示例),因此很难快速掌握这个概念。在本文中,我们将创建一个功能最简化的演示,只突出这些概念,以便于理解。

Prism 框架经常在 WPF/Silverlight 应用程序中与 MVVM 模式一起使用。在深入本文的核心概念之前,我们先快速介绍一下 Prism 框架。然后,我们将创建演示应用程序并使用 DelegateCommand 和 CompositeCommand。

注意:对于本文,作为先决条件,我们必须对 WPF 和 ICommand 接口有基本了解。如果您是 ICommand 接口的新手,请参阅 本文

目录

  • 什么是 Prism 框架
  • 演示应用程序概述
  • 什么是 DelegateCommand
  • DelegateCommand 的用法
  • 什么是 CompositeCommand
  • CompositeCommand 的用法

什么是 Prism 框架

Prism 是一个用于使用 WPF/Silverlight 构建“复合应用程序”的框架。Prism 框架提供了一套库,帮助我们构建模块化的企业应用程序。通过使用这些库,我们可以将应用程序划分为多个模块,根据需要加载它们,并轻松地编排它们。有关 Prism 的更多学习,请参阅 Prism 基本介绍MSDN 上的 Prism 页面,以及 关于 Prism 的更多信息
 

什么是 DelegateCommand

DelegateCommand 是 Prism 框架提供的一个类。它位于“Microsoft.Practices.Prism.Commands”命名空间下。DelegateCommand 类继承自抽象类 DelegateCommandBase。DelegateCommandBase 类实现了 ICommand 和 IActiveAware 接口。这样,通过使用 DelegateCommand,我们可以实现与我们在 上一篇文章 中使用 RelayCommand 实现的相同功能。并且 DelegateCommand 还提供了一些额外的可能性。因此,如果我们正在使用 Prism,则没有必要创建通用的命令类(如 RelayCommand),而是应该使用 DelegateCommand 类。现在,让我们创建一个使用 DelegateCommand 的演示应用程序。

演示应用程序概述

演示应用程序具有简单的布局,包含 3 个按钮和 8 个 TextBlock。2 个 TextBlock 用于显示数字,6 个 TextBlock 用于显示纯文本信息。单击按钮时,相应 TextBlock 中的数字会增加到某个上限。

当我们单击“Increment - First”按钮时,它会增加第一个 TextBlock 中的数字。对于第一个 TextBlock,数字最多可以增加到 5(作为最大数字),然后按钮将被禁用。

当我们单击“Increment - Second”按钮时,它会增加第二个 TextBlock 中的数字。对于第二个 TextBlock,数字最多可以增加到 10(作为最大数字),然后按钮将被禁用。

当我们单击“Increment - All”按钮时,它会增加两个 TextBlock 中的数字。

我们将看到 DelegateCommand 和 CompositeCommand 如何用于实现上述功能。演示应用程序的最终截图

Final Screen Shot of Demo App
 

让我们构建演示应用程序

现在打开 Visual Studio 来创建演示应用程序,请按照以下步骤操作

注意:构建任何基于 Prism 的应用程序时,我们需要注意匹配/兼容的 .NET 版本与给定 Prism 版本。在为本文创建演示时,我们目标是 .NET Framework 4 和 Prism 4。因此,附加的代码将适用于 Visual Studio 2010 及更高版本的 Visual Studio,而无需额外设置。DelegateCommand 和 CompositeCommand 的概念在所有 Prism 版本中都是相同的。

创建一个名为“CompositeDemoApp”的 WPF 应用程序,目标是 .NET 4.0。在此演示应用程序中,我们将使用 Prism 框架自带的 DelegateCommand 和 CompositeCommand 类。因此,要使用它们,我们需要下载 Prism 框架 4。此 Nuget 页面描述了如何下载 Prism 框架 4。

让我们通过“程序包管理器控制台”下载 prism 4.0,如下面的屏幕截图所示。

Navigate Package Mangare Console

单击程序包管理器控制台选项上的回车键,程序包管理器控制台窗口将打开。现在,在程序包管理器控制台窗口中输入以下命令并按回车键。

       Install-Package Prism -Version 4.0.0
       

所需的库将被下载,并显示类似“成功安装 Prism 4.0.0.0”的消息,如下所示。

Command to Download Prism

如果转到“引用”文件夹,您会看到 Prism 相关的库会自动引用到此项目。

现在,在项目根目录添加 ViewModel 文件夹,然后添加一个名为 CalculationViewModel.cs 的文件,我们将在后续步骤中在该文件中编写代码。

在 MainWindow.xaml 文件中编写以下 XAML 代码,这将为我们创建 UI。代码非常简单,因此我们假设它是显而易见的。

<Grid DataContext="{StaticResource calculationVM}" Background="LightBlue">
        <Grid.RowDefinitions>
            <RowDefinition ></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition Height="80"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Rectangle Grid.Column="1" Grid.RowSpan="5">
            <Rectangle.Fill>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="#FF83FF97" Offset="0" />
                    <GradientStop Color="White" Offset="1" />
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
        <Rectangle Grid.Column="2" Grid.RowSpan="5">
            <Rectangle.Fill>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="Yellow"  Offset="0" />
                    <GradientStop Color="White" Offset="1" />
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
        <TextBlock Grid.Row="0" FontSize="18"  Grid.Column="0" Margin="5" Height="30" Text="Values"></TextBlock>
        <TextBlock Grid.Row="0"  Grid.Column="1" FontSize="16"  Background="LightGreen" Margin="5" 
                   HorizontalAlignment="Center" TextAlignment="Center" Width="120" Height="30" 
                   Text="{Binding FirstInput, Mode=TwoWay}">
        </TextBlock>
        <TextBlock Grid.Row="0"  Grid.Column="2" FontSize="16"  Background="BlanchedAlmond"  Margin="5" 
                   HorizontalAlignment="Center" TextAlignment="Center" Width="120" Height="30" 
                   Text="{Binding SecondInput, Mode=TwoWay}">
        </TextBlock>

        <TextBlock Grid.Row="1" FontSize="18"  Grid.Column="0" Margin="5" Height="30" Text="Individual Commands"></TextBlock>
        <Button Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" Margin="5" Width="120" Height="30" 
                Content="Increment - First" Command="{Binding FirstIncrementCommand}">
        </Button>
        <Button Grid.Row="1" Grid.Column="2"  HorizontalAlignment="Center"  Margin="5" Width="120" Height="30"
                Content="Increment - Second" Command="{Binding SecondIncrementCommand}">
        </Button>

        <TextBlock Grid.Row="2" FontSize="18"  Grid.Column="0" Margin="5" Height="30" Text="Composite Commands"></TextBlock>
        <Button Grid.Row="2" Grid.Column="1" FontSize="16" Grid.ColumnSpan="2" Margin="10,5,10,5" 
                HorizontalAlignment="Center" Width="240"  Height="30" Content="Increment - All" 
                Command="{Binding AllIncrementCommand}">
        </Button>

        <TextBlock Grid.Row="3" FontSize="18"  Grid.Column="0" Margin="5" Height="30" Text="Information"></TextBlock>
        <TextBlock Grid.Row="3" FontSize="16"  Grid.Column="1" Margin="5" Foreground="OrangeRed" 
                   TextWrapping="Wrap" Text="First value can be increamented till 5.">
        </TextBlock>
        <TextBlock Grid.Row="3" FontSize="16"  Grid.Column="2" Margin="5" Foreground="OrangeRed" 
                   TextWrapping="Wrap" Text="Second value can be increamented till 10.">
        </TextBlock>
    </Grid>

在 MainWindow.xaml 中提供 CalculationViewModel 的命名空间。

        xmlns:vm ="clr-namespace:CompositeDemoApp.ViewModel"
        
     <Window.Resources>
        <vm:CalculationViewModel x:Key="calculationVM">
        </vm:CalculationViewModel>
        <Style TargetType="{x:Type Button}">
            <Setter Property="Foreground" Value="Blue"/>
            <Setter Property="IsEnabled" Value="True"/>
            <Style.Triggers>
                <Trigger Property="IsEnabled" Value="false">
                    <Setter Property="Foreground" Value="#FFADADAD"/>
                </Trigger>
            </Style.Triggers>
        </Style>
     </Window.Resources>
        

与我们在 上一篇文章 中所做的不同,我们将使用“NotificationObject”。此类由 Prism 框架在“Microsoft.Practices.Prism.ViewModel”命名空间下提供。它实现了 INotifyPropertyChanged 接口。这是一个标准接口,每当我们想通知对象(UI 或 ViewModel)属性值更新时,都必须实现它。现在,让 CalculationViewModel 继承自 NotificationObject 类。

在 MainWindow.xaml 文件中,我们将 UI 属性绑定到了 CalculationViewModel 的属性。CalculationViewModel 的“FirstInput”属性与第一个 TextBlock 的“Text”属性绑定,CalculationViewModel 的“SecondInput”属性与第二个 TextBlock 的“Text”属性绑定。现在,我们需要在 CalculationViewModel 中创建并实现这些属性。

        private int firstInput;
        private int secondInput;
        public int FirstInput
        {
            get
            {
                return firstInput;
            }
            set
            {
                firstInput = value;
                RaisePropertyChanged("FirstInput");
            }
        }
        public int SecondInput
        {
            get
            {
                return secondInput;
            }
            set
            {
                secondInput = value;
                RaisePropertyChanged("SecondInput");
            }
         } 
        

现在,在单击“Increment - First”或“Increment - Second”按钮时,我们希望增加与“FirstInput”或“SecondInput” TextBlock 绑定的数字。为此,我们需要在按钮单击时使用命令,这将在下一节中使用 DelegateCommand 来完成。

DelegateCommand 的用法

要使用 DelegateCommand,我们首先需要为每个命令创建私有字段,类型为 DelegateCommand。在本例中,我们需要两个 DelegateCommand 来处理“Increment – First”和“Increment – Second”这两个按钮,如下所示。

       private DelegateCommand firstIncrementCommand;
       private DelegateCommand secondIncrementCommand; 
       public DelegateCommand FirstIncrementCommand
       {
           get { return firstIncrementCommand; }
       }
       public DelegateCommand SecondIncrementCommand
       {
           get { return secondIncrementCommand; }
       }
      

在 CalculationViewModel 的构造函数中,我们需要实例化命令,因此编写以下代码行。

        firstIncrementCommand = new DelegateCommand(incrementFirstInput, canIncrementFirstInput);
        secondIncrementCommand = new DelegateCommand(incrementSecondInput, canIncrementSecondInput);
        

这里我们正在创建一个 DelegateCommand 类的实例。它接受两个方法名称作为委托参数。对于 firstIncrementCommand,我们将方法名称 incrementFirstInput 作为第一个参数传递,该方法包含我们在单击“Increment – First”按钮时要执行的实际逻辑。第二个参数是我们传递的 canIncrementFirstInput 方法的名称,它将决定命令是否应该启用或禁用。当执行和可执行时触发的方法将调用 respective 命令。现在实现这两个方法。

       private void incrementFirstInput()
       {
           FirstInput += 1;
           FirstIncrementCommand.RaiseCanExecuteChanged();
       }
       // Enable Increment - First button only if first value is less than 5
      private bool canIncrementFirstInput()
       {
           bool ifFirstInputLessThan5 = firstInput < 5;
           return ifFirstInputLessThan5;
       }
      

同样,我们需要实现两个方法来响应“Increment – Second”按钮命令 - SecondIncrementCommand。

       private void incrementSecondInput()
       {
            SecondInput += 1;
            SecondIncrementCommand.RaiseCanExecuteChanged();
       }
       // Enable Increment - Second button only if second value is less than 10
       private bool canIncrementSecondInput()
       {
            bool ifSecondInputLessThan10 = secondInput < 10;
            return ifSecondInputLessThan10;
       }
       

现在运行应用程序。单击“Increment – First”或“Increment – Second”按钮,我们将看到第一个 TextBlock 中的数字最多增加到 5,第二个 TextBlock 中的数字最多增加到 10。

到目前为止,在此演示应用程序中,当我们单击“Increment – First”按钮时,第一个 TextBlock 中的数字会增加;单击“Increment – Second”按钮时,第二个 TextBlock 中的数字会增加。

现在,我们希望通过重用我们已经为“Increment – First”和“Increment – Second”按钮单击实现的逻辑,在单击单个按钮时增加两个 TextBlock 中的数字。这种可重用性可以通过使用 CompositeCommand 来实现。让我们了解 CompositeCommand。

什么是 CompositeCommand

CompositeCommand 是一个可以包含多个子命令的类。它提供了执行所有关联方法(ExecuteCanExecute)的功能。如果任何子命令的 CanExecute 方法返回 false,则不会执行任何子命令。

例如,如果 CompositeCommand 有 5 个子命令,其中任何一个关联的子命令的 CanExecute 方法返回 false,CompositeCommand 将不会被启用/执行。

CompositeCommand 的用法

要使用 CompositeCommand,首先创建一个 CompositeCommand 的私有字段。

        private CompositeCommand allIncrementCommand;
       

在 CalculationViewModel 的构造函数中,创建 CompositeCommand 类的实例。然后将 firstIncrementCommand 和 secondIncrementCommand 注册到 CompositeCommand。这里 firstIncrementCommand 和 secondIncrementCommand 是子命令。

       allIncrementCommand = new CompositeCommand();
       allIncrementCommand.RegisterCommand(firstIncrementCommand); 
       allIncrementCommand.RegisterCommand(secondIncrementCommand);
        

现在,在 CalculationViewModel 中创建另一个公共命令,以便我们可以在 UI 代码中绑定它,如下所示。

       public CompositeCommand AllIncrementCommand
       {
           get { return allIncrementCommand; }
       }
        

由于 AllIncrementCommand 命令已绑定到 UI 中的“Increment – All”按钮,因此当 AllIncrementCommand 被触发时,这两个子命令将自动与其关联的方法一起执行。
现在运行应用程序。单击“Increment – All”按钮,我们将看到两个 TextBlock 中的数字最多增加到 5。之后,“Increment – First”和“Increment – All”按钮将被禁用,但“Increment – Second”按钮仍然启用。

之所以发生这种情况,是因为第一个子命令(称为 firstIncrementCommand)的 canIncrementFirstInput 方法在 FirstInput 为 5 后返回 false。正如我们之前解释的,如果任何子命令的 CanExecute 方法返回 false,CompositeCommand 将不起作用。

结论

在本文中,我们学习了 Prism 应用程序中的 DelegateCommand 和 CompositeCommand,以及如何在 MVVM 模式下使用它们。非常欢迎您的评论/建议和疑问。谢谢。

© . All rights reserved.