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

时间:2011-05-09 20:47:36

标签: wpf validation workflow-foundation-4 designer

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

我有一个自定义的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)
    { }
}

在设计师中,我用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);
    }
}

以下是设计师的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>

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

现在出现问题:

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

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

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

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

    修改

    对于那些感兴趣的人:

1 个答案:

答案 0 :(得分:4)

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

public Dictionary<string, Argument> Arguments { get; set; }

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

public Dictionary<string, InArgument> InArguments { get; set; }
public Dictionary<string, OutArgument> OutArguments { get; set; }

我发现Custom Activity to Invoke XAML Based Child Workflows在做这项工作时非常有用。