我在验证自定义活动的设计器中正常工作时遇到问题。重现该行为的最简单示例如下:
我有一个自定义 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)
{ }
}
在设计器中,我动态地创建表达式文本框来编辑这些参数。用户可以在单独的模式窗口中定义参数及其类型,但为了简单起见,我已修复此示例中的参数:
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>
The InitializeGrid
方法将参数的表达式文本框添加到ArgumentGrid
。在它下面,我有一个单独的静态定义表达式文本框,用于活动中的固定参数,以演示(几乎)所需的行为。
现在针对问题:
动态参数的无效表达式只会导致错误图标出现在文本框旁边,但不会像静态定义的文本框中存在错误时那样传播到设计器的顶部栏。
如果我在这种无效状态下关闭设计器(并保存定义),即使错误仅出现在动态文本框中,错误图标也会正确传播到顶部栏。尽管之后行为变得更加奇怪。更改参数的值后,现在即使文本框旁边的错误图标也不再一致工作。
如果我完全删除动态文本框的内容,字典中的值将设置为 null,这在工作流定义中表现为<x:Null x:Key="String Arg" />
代替<InArgument x:TypeArguments="x:String" x:Key="String Arg">["a"]</InArgument>
或者像第一次编辑表达式之前一样省略条目。如果我重新打开这样的工作流程,即使静态创建的文本框也无法再正常工作(错误图标仅在文本框聚焦时才可见,并且不再传播到顶部)。
很明显,我在创建动态文本框时做错了什么。正确的做法是什么?是否有任何示例可用于为具有动态参数数量的自定义活动创建设计器?
EDIT:
对于那些感兴趣的人:
- MSDN 论坛上还有一些更多讨论,我也在那里发布了问题 http://social.msdn.microsoft.com/Forums/en-US/wfprerelease/thread/037c0e00-1003-4f3a-a4a7-d30b2556371b.
- 作为讨论的结果,我还提交了关于 Microsoft Connect 的报告 https://connect.microsoft.com/wf/feedback/details/671678/argument-validation-in-custom-activity-designer.