Проверка аргумента в настраиваемом конструкторе действий

У меня возникают проблемы с правильной работой проверки в конструкторе для моей настраиваемой операции. Самый простой пример для воспроизведения поведения выглядит следующим образом:

У меня есть настраиваемое действие 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>

Метод InitializeGrid добавляет текстовые поля выражения для аргументов в ArgumentGrid. Под ним у меня есть отдельное текстовое поле статически определенного выражения для фиксированного аргумента в действии, чтобы продемонстрировать (почти) желаемое поведение.

Теперь о проблемах:

  1. Недопустимые выражения для динамических аргументов вызывают только появление значка ошибки рядом с текстовым полем, но он не распространяется на верхнюю панель конструктора, как это происходит в случае ошибки в статически определенном текстовом поле.

  2. Если я закрою конструктор в таком недопустимом состоянии (и сохраню определение), значок ошибки правильно распространяется на верхнюю панель, даже если ошибка находится только в динамическом текстовом поле. Хотя потом поведение становится еще более странным. После изменения значений аргументов теперь даже значок ошибки рядом с текстовым полем больше не работает последовательно.

  3. Если я полностью удаляю содержимое динамического текстового поля, значение в словаре становится равным нулю, что проявляется в определении рабочего процесса как <x:Null x:Key="String Arg" /> вместо <InArgument x:TypeArguments="x:String" x:Key="String Arg">["a"]</InArgument> или просто опускается запись, как в случае, прежде чем редактировать выражение в первый раз. Если я снова открою такой рабочий процесс, даже статически созданное текстовое поле больше не будет работать должным образом (значок ошибки виден только тогда, когда текстовое поле сфокусировано, и оно больше не распространяется наверх).

Кажется очевидным, что я делаю что-то не так при создании динамических текстовых полей. Как правильно это сделать? Есть ли какой-нибудь пример создания дизайнера для настраиваемого действия с динамическим числом аргументов?

РЕДАКТИРОВАТЬ:

Для заинтересованных:


person Damir Arh    schedule 09.05.2011    source источник
comment
Подождите, разве вы не проверяете каждый вызов CacheMetadata? Это почти единственное место, где вы можете выполнять проверку своей деятельности и распространять ошибки / предупреждения по рабочему процессу.   -  person    schedule 10.05.2011
comment
@Will Я мог бы добавить ошибки проверки и предупреждения в CacheMetadata, но ExpressionTextBox имеет собственную встроенную проверку, которая отображает значок ошибки рядом с текстовым полем, если введенное выражение недействительно. Я не хотел бы повторно реализовывать эту проверку, и я не знаю, как я могу получить доступ к этой функции проверки из CacheMetadata, чтобы распространить ее самостоятельно, добавив ошибку проверки. Также: распространение, кажется, работает нормально, пока я избегаю сборов аргументов.   -  person Damir Arh    schedule 10.05.2011


Ответы (1)


Я столкнулся с проблемой, описанной здесь, при попытке создать конструктор для динамического набора аргументов в действии. Мне удалось обойти проблему, используя встроенный Окно DynamicArgumentDialog. Мне пришлось реструктурировать свою деятельность, чтобы принять единую коллекцию как входных, так и выходных аргументов:

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

вместо двух отдельных коллекций, которые я использовал раньше:

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

Я нашел очень полезным настраиваемое действие для вызова дочерних рабочих процессов на основе XAML. при выполнении этой работы.

person Damir Arh    schedule 02.08.2011