如何:创建自定义活动设计器
通常实现自定义活动设计器,以便其相关活动可与其他活动(这些活动的设计器可与它们一起放置到设计图面上)组合在一起。 此功能要求自定义活动设计器提供一个可放置任意活动的“放置区”,并提供用于管理设计图面上得到的元素集合的方法。 本主题介绍如何创建一个包含上述放置区的自定义活动设计器,以及如何创建一个可提供管理设计器元素集合所需的编辑功能的自定义活动设计器。
自定义活动设计器通常继承自 ActivityDesigner,后者是任何无特定设计器的活动的默认基活动设计器类型。 此类型提供与属性网格交互以及配置管理颜色和图标等基本方面的设计时体验。
ActivityDesigner 采用两个帮助器控件,即 WorkflowItemPresenter 和 WorkflowItemsPresenter,以便更易于开发自定义活动设计器。 这两个控件处理常用功能,例如,拖放子元素,删除、选择和添加这些子元素。 WorkflowItemPresenter 允许其内部存在单个子级 UI 元素,用于提供“放置区”,而 WorkflowItemsPresenter 可以支持多个 UI 元素,包括附加功能,如对子元素的排序、移动、删除和添加功能。
在实现自定义活动设计器的过程需要强调的其他关键部分是,考虑如何使用 WPF 数据绑定将可视化编辑操作绑定到为记住我们在设计器中编辑的内容而存储的实例。 这是通过模型项树实现的,它还负责启用更改通知和跟踪事件(例如状态的更改)。
本主题概述了两个过程。
第一个过程介绍如何使用 WorkflowItemPresenter 创建一个自定义活动设计器,用于提供接收其他活动的放置区。 此过程基于自定义复合设计器 - 工作流项演示器示例。
第二个过程介绍如何使用 WorkflowItemsPresenter 创建一个自定义活动设计器,用于提供编辑包含的元素的集合所需的功能。 此过程基于自定义复合设计器 - 工作流项演示器示例。
使用 WorkflowItemPresenter 创建带有放置区的自定义活动设计器
启动 Visual Studio 2010。
在“文件”菜单上,指向“新建”,然后选择“项目…”。
“新建项目” 对话框随即打开。
在“已安装的模板”窗格中,从首选的语言类别中选择“Windows”。
在“模板”窗格中,选择“WPF 应用程序”。
在“名称”框中,输入 。
在“位置”框中,输入要保存项目的目录,或者单击“浏览”以导航到该目录。
在“解决方案”框中,接受默认值。
单击 “确定” 。
在“解决方案资源管理器”中,右键单击 MainWindows.xaml 文件,然后选择“删除”,并在“Microsoft Visual Studio”对话框中单击“确定”。
在“解决方案资源管理器”中,右键单击 UsingWorkflowItemPresenter 项目,选择“添加”,再选择“新建项…”以打开“添加新项”对话框,然后从左侧的“已安装的模板”部分选择“WPF”类别。
选择“Window (WPF)”模板,将其命名为
RehostingWFDesigner
,然后单击“添加”。打开 RehostingWFDesigner.xaml 文件,将下面的代码粘贴到其中以定义应用程序的 UI:
<Window x:Class=" UsingWorkflowItemPresenter.RehostingWFDesigner" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sapt="clr-namespace:System.Activities.Presentation.Toolbox;assembly=System.Activities.Presentation" xmlns:sys="clr-namespace:System;assembly=mscorlib" Title="Window1" Height="600" Width="900"> <Window.Resources> <sys:String x:Key="AssemblyName">System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</sys:String> </Window.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="2*"/> <ColumnDefinition Width="7*"/> <ColumnDefinition Width="3*"/> </Grid.ColumnDefinitions> <Border Grid.Column="0"> <sapt:ToolboxControl Name="Toolbox"> <sapt:ToolboxCategory CategoryName="Basic"> <sapt:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}" > <sapt:ToolboxItemWrapper.ToolName> System.Activities.Statements.Sequence </sapt:ToolboxItemWrapper.ToolName> </sapt:ToolboxItemWrapper> <sapt:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}"> <sapt:ToolboxItemWrapper.ToolName> System.Activities.Statements.WriteLine </sapt:ToolboxItemWrapper.ToolName> </sapt:ToolboxItemWrapper> <sapt:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}"> <sapt:ToolboxItemWrapper.ToolName> System.Activities.Statements.If </sapt:ToolboxItemWrapper.ToolName> </sapt:ToolboxItemWrapper> <sapt:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}"> <sapt:ToolboxItemWrapper.ToolName> System.Activities.Statements.While </sapt:ToolboxItemWrapper.ToolName> </sapt:ToolboxItemWrapper> </sapt:ToolboxCategory> </sapt:ToolboxControl> </Border> <Border Grid.Column="1" Name="DesignerBorder"/> <Border Grid.Column="2" Name="PropertyBorder"/> </Grid> </Window>
若要将活动设计器与活动类型相关联,您必须向元数据存储注册活动设计器。 为此,请将
RegisterMetadata
方法添加到RehostingWFDesigner
类中。 在RegisterMetadata
方法的范围内,创建一个 AttributeTableBuilder 对象,并调用 AddCustomAttributes 方法以向其添加特性。 调用 AddAttributeTable 方法以向元数据存储添加 AttributeTable。 下面的代码包含设计器的重新承载逻辑。 它注册元数据,将SimpleNativeActivity
放入工具箱中,并创建工作流。 将此代码放入 RehostingWFDesigner.xaml.cs 文件中。using System; using System.Activities.Core.Presentation; using System.Activities.Presentation; using System.Activities.Presentation.Metadata; using System.Activities.Presentation.Toolbox; using System.Activities.Statements; using System.ComponentModel; using System.Windows; namespace UsingWorkflowItemPresenter { // Interaction logic for RehostingWFDesigner.xaml public partial class RehostingWFDesigner { public RehostingWFDesigner() { InitializeComponent(); } protected override void OnInitialized(EventArgs e) { base.OnInitialized(e); // Register metadata. (new DesignerMetadata()).Register(); RegisterCustomMetadata(); // Add custom activity to toolbox. Toolbox.Categories.Add(new ToolboxCategory("Custom activities")); Toolbox.Categories[1].Add(new ToolboxItemWrapper(typeof(SimpleNativeActivity))); // Create the workflow designer. var wd = new WorkflowDesigner(); wd.Load(new Sequence()); DesignerBorder.Child = wd.View; PropertyBorder.Child = wd.PropertyInspectorView; } void RegisterCustomMetadata() { var builder = new AttributeTableBuilder(); builder.AddCustomAttributes(typeof(SimpleNativeActivity), new DesignerAttribute(typeof(SimpleNativeDesigner))); MetadataStore.AddAttributeTable(builder.CreateTable()); } } }
在解决方案资源管理器中,右键单击“引用”目录,并选择“添加引用…”以打开“添加引用”对话框。
单击“.NET”选项卡,找到名为“System.Activities.Core.Presentation”的程序集,选择它并单击“确定”。
使用相同的过程,添加对下列程序集的引用:
System.Data.DataSetExtensions.dll
System.Activities.Presentation.dll
System.ServiceModel.Activities.dll
打开 App.xaml 文件,将 StartUpUri 的值更改为“RehostingWFDesigner.xaml”。
在“解决方案资源管理器”中,右键单击 UsingWorkflowItemPresenter 项目,选择“添加”,再选择“新建项…”以打开“添加新项”对话框,然后从左侧的“已安装的模板”部分选择“工作流”类别。
选择“活动设计器”模板,将其命名为
SimpleNativeDesigner
,然后单击“添加”。打开 SimpleNativeDesigner.xaml 文件,并将下列代码粘贴到其中。 请注意,此代码使用 ActivityDesigner 作为根元素,并演示如何使用绑定将 WorkflowItemPresenter 集成到设计器中,以便可以在复合活动设计器中显示子类型。
注意
ActivityDesigner 的架构只允许向自定义活动设计器定义中添加一个子元素;不过,这个元素可以是
StackPanel
、Grid
或某些其他复合 UI 元素。<sap:ActivityDesigner x:Class=" UsingWorkflowItemPresenter.SimpleNativeDesigner" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation" xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation"> <sap:ActivityDesigner.Resources> <DataTemplate x:Key="Collapsed"> <StackPanel> <TextBlock>This is the collapsed view</TextBlock> </StackPanel> </DataTemplate> <DataTemplate x:Key="Expanded"> <StackPanel> <TextBlock>Custom Text</TextBlock> <sap:WorkflowItemPresenter Item="{Binding Path=ModelItem.Body, Mode=TwoWay}" HintText="Please drop an activity here" /> </StackPanel> </DataTemplate> <Style x:Key="ExpandOrCollapsedStyle" TargetType="{x:Type ContentPresenter}"> <Setter Property="ContentTemplate" Value="{DynamicResource Collapsed}"/> <Style.Triggers> <DataTrigger Binding="{Binding Path=ShowExpanded}" Value="true"> <Setter Property="ContentTemplate" Value="{DynamicResource Expanded}"/> </DataTrigger> </Style.Triggers> </Style> </sap:ActivityDesigner.Resources> <Grid> <ContentPresenter Style="{DynamicResource ExpandOrCollapsedStyle}" Content="{Binding}" /> </Grid> </sap:ActivityDesigner>
在“解决方案资源管理器”中,右键单击 UsingWorkflowItemPresenter 项目,选择“添加”,再选择“新建项…”以打开“添加新项”对话框,然后从左侧的“已安装的模板”部分选择“工作流”类别。
选择“代码活动”模板,将其命名为
SimpleNativeActivity
,然后单击“添加”。通过在 SimpleNativeActivity.cs 文件中输入下面的代码,实现
SimpleNativeActivity
类:using System.Activities; namespace UsingWorkflowItemPresenter { public sealed class SimpleNativeActivity : NativeActivity { // this property contains an activity that will be scheduled in the execute method // the WorkflowItemPresenter in the designer is bound to this to enable editing // of the value public Activity Body { get; set; } protected override void CacheMetadata(NativeActivityMetadata metadata) { metadata.AddChild(Body); base.CacheMetadata(metadata); } protected override void Execute(NativeActivityContext context) { context.ScheduleActivity(Body); } } }
从“生成”菜单中选择“生成解决方案”。
从“调试”菜单中选择“开始执行(不调试)”,以打开重新承载的自定义设计窗口。
使用 WorkflowItemsPresenter 创建自定义活动设计器
第二个自定义活动设计器的过程与第一个自定义活动设计器的过程相同,但有几处修改,第一处修改是将第二个应用程序命名为
UsingWorkflowItemsPresenter
。 另外,此应用程序不会定义新的自定义活动。主要差别包含在 CustomParallelDesigner.xaml 和 RehostingWFDesigner.xaml.cs 文件中。 下面是 CustomParallelDesigner.xaml 文件中定义 UI 的代码:
<sap:ActivityDesigner x:Class=" UsingWorkflowItemsPresenter.CustomParallelDesigner" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation" xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation"> <sap:ActivityDesigner.Resources> <DataTemplate x:Key="Collapsed"> <TextBlock>This is the Collapsed View</TextBlock> </DataTemplate> <DataTemplate x:Key="Expanded"> <StackPanel> <TextBlock HorizontalAlignment="Center">This is the</TextBlock> <TextBlock HorizontalAlignment="Center">extended view</TextBlock> <sap:WorkflowItemsPresenter HintText="Drop Activities Here" Items="{Binding Path=ModelItem.Branches}"> <sap:WorkflowItemsPresenter.SpacerTemplate> <DataTemplate> <Ellipse Width="10" Height="10" Fill="Black"/> </DataTemplate> </sap:WorkflowItemsPresenter.SpacerTemplate> <sap:WorkflowItemsPresenter.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal"/> </ItemsPanelTemplate> </sap:WorkflowItemsPresenter.ItemsPanel> </sap:WorkflowItemsPresenter> </StackPanel> </DataTemplate> <Style x:Key="ExpandOrCollapsedStyle" TargetType="{x:Type ContentPresenter}"> <Setter Property="ContentTemplate" Value="{DynamicResource Collapsed}"/> <Style.Triggers> <DataTrigger Binding="{Binding Path=ShowExpanded}" Value="true"> <Setter Property="ContentTemplate" Value="{DynamicResource Expanded}"/> </DataTrigger> </Style.Triggers> </Style> </sap:ActivityDesigner.Resources> <Grid> <ContentPresenter Style="{DynamicResource ExpandOrCollapsedStyle}" Content="{Binding}"/> </Grid> </sap:ActivityDesigner>
下面是 RehostingWFDesigner.xaml.cs 文件中提供重新承载逻辑的代码:
using System; using System.Activities.Core.Presentation; using System.Activities.Presentation; using System.Activities.Presentation.Metadata; using System.Activities.Statements; using System.ComponentModel; using System.Windows; namespaceUsingWorkflowItemsPresenter { public partial class RehostingWfDesigner : Window { public RehostingWfDesigner() { InitializeComponent(); } protected override void OnInitialized(EventArgs e) { base.OnInitialized(e); // Register metadata. (new DesignerMetadata()).Register(); RegisterCustomMetadata(); // Create the workflow designer. var wd = new WorkflowDesigner(); wd.Load(new Sequence()); DesignerBorder.Child = wd.View; PropertyBorder.Child = wd.PropertyInspectorView; } void RegisterCustomMetadata() { var builder = new AttributeTableBuilder(); builder.AddCustomAttributes(typeof(Parallel), new DesignerAttribute(typeof(CustomParallelDesigner))); MetadataStore.AddAttributeTable(builder.CreateTable()); } } }