自定义活动设计器中的参数验证

Dam*_*Arh 5 validation wpf designer workflow-foundation-4

我在设计器中为自定义活动正确地进行验证时遇到问题.重现行为的最简单示例如下:

我有一个自定义的WF4活动,其中包含存储在字典中的动态参数集合:

[Designer(typeof(DictionaryActivityDesigner))]
public class DictionaryActivity : NativeActivity
{
    [Browsable(false)]
    public Dictionary<string, InArgument> Arguments { get; set; }
    public InArgument<string> StringArg { get; set; }

    public DictionaryActivity()
    {
        Arguments = new Dictionary<string, InArgument>();
    }

    protected override void Execute(NativeActivityContext context)
    { }
}
Run Code Online (Sandbox Code Playgroud)

在设计器中,我用dinamically创建用于编辑这些参数的表达式文本框.用户可以在单独的模态窗口中定义参数及其类型,但为了简单起见,我修复了此示例中的参数:

public partial class DictionaryActivityDesigner
{
    private Dictionary<string, Type> definition;

    public DictionaryActivityDesigner()
    {
        definition = new Dictionary<string, Type>
        {
            { "String Arg", typeof(string) },
            { "Int Arg", typeof(int) }
        };

        InitializeComponent();
    }

    public void InitializeGrid(Dictionary<string, Type> arguments)
    {
        ArgumentsGrid.RowDefinitions.Clear();
        ArgumentsGrid.Children.Clear();

        int gridRow = 0;
        foreach (var arg in arguments)
        {
            ArgumentsGrid.RowDefinitions.Add(new RowDefinition());

            var label = new Label()
            {
                Content = arg.Key + ":"
            };
            Grid.SetRow(label, gridRow);
            Grid.SetColumn(label, 0);
            ArgumentsGrid.Children.Add(label);

            var textbox = new ExpressionTextBox()
            {
                ExpressionType = arg.Value,
                OwnerActivity = ModelItem,
                UseLocationExpression = false
            };
            var binding = new Binding()
            {
                Mode = BindingMode.TwoWay,
                Converter = new ArgumentToExpressionConverter(),
                ConverterParameter = "In",
                Path = new PropertyPath("ModelItem.Arguments[(0)]", arg.Key)
            };
            textbox.SetBinding(ExpressionTextBox.ExpressionProperty, binding);
            Grid.SetRow(textbox, gridRow);
            Grid.SetColumn(textbox, 1);
            ArgumentsGrid.Children.Add(textbox);

            gridRow++;
        }
    }

    private void ActivityDesigner_Loaded(object sender, RoutedEventArgs e)
    {
        InitializeGrid(definition);
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是设计师的XAML:

<sap:ActivityDesigner x:Class="ActivityValidation.DictionaryActivityDesigner"
                      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                      xmlns:s="clr-namespace:System;assembly=mscorlib"
                      xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
                      xmlns:sapc="clr-namespace:System.Activities.Presentation.Converters;assembly=System.Activities.Presentation"
                      xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation"
                      Loaded="ActivityDesigner_Loaded">
    <sap:ActivityDesigner.Resources>
        <ResourceDictionary>
            <sapc:ArgumentToExpressionConverter x:Key="ArgumentToExpressionConverter" />
        </ResourceDictionary>
    </sap:ActivityDesigner.Resources>
    <StackPanel Orientation="Vertical">
        <Grid Name="ArgumentsGrid">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition MinWidth="250" />
            </Grid.ColumnDefinitions>
        </Grid>
        <sapv:ExpressionTextBox ExpressionType="s:String" 
                                OwnerActivity="{Binding ModelItem}" 
                                Expression="{Binding ModelItem.StringArg, Mode=TwoWay, Converter={StaticResource ArgumentToExpressionConverter}, ConverterParameter=In}" />
    </StackPanel>
</sap:ActivityDesigner>
Run Code Online (Sandbox Code Playgroud)

InitializeGrid方法将参数的表达式文本框添加到ArgumentGrid.在它下面,我有一个单独的静态定义的表达式文本框,用于活动中的固定参数,以演示(几乎)所需的行为.

现在出现问题:

  1. 动态参数的无效表达式只会导致错误图标出现在文本框旁边,但它不会传播到设计器的顶部栏,就像静态定义的文本框中出现错误一样.

  2. 如果我以无效状态关闭设计器(并保存定义),即使错误仅在动态文本框中,错误图标也会正确传播到顶部栏.虽然之后的行为变得更加奇怪.更改参数的值后,现在甚至文本框旁边的错误图标也不再一致.

  3. 如果我完全删除动态文本框的内容,则字典中的值将设置为null,这将在工作流定义中显示为<x:Null x:Key="String Arg" />代替<InArgument x:TypeArguments="x:String" x:Key="String Arg">["a"]</InArgument>或仅删除条目,就像第一次编辑表达式之前的情况一样.如果我重新打开这样的工作流程,即使静态创建的文本框也不能正常工作(错误图标仅在文本框聚焦时才可见,并且不再传播到顶部).

在创建动态文本框时,我似乎做错了.这样做的正确方法是什么?是否有任何示例可用于为具有动态参数的自定义活动创建设计器?

编辑:

对于有兴趣的人:

Dam*_*Arh 4

我在尝试为活动中的动态参数集合创建设计器时遇到了此处描述的问题。我设法通过使用内置的DynamicArgumentDialog窗口解决了该问题。我必须重组我的活动以接受输入和输出参数的单个集合:

public Dictionary<string, Argument> Arguments { get; set; }
Run Code Online (Sandbox Code Playgroud)

而不是我之前使用的两个单独的集合:

public Dictionary<string, InArgument> InArguments { get; set; }
public Dictionary<string, OutArgument> OutArguments { get; set; }
Run Code Online (Sandbox Code Playgroud)

我发现调用基于 XAML 的子工作流的自定义活动在完成这项工作时非常有帮助。