0

首先,我会总结一下我在做什么。然后,我将介绍我认为是这些步骤之一的相关代码位,这些步骤展示了标题问题,然后我将在最后解释问题。

我正在公开一个内部库,用于将我们的一个软件产品自动化到 Windows 工作流,允许工程师(不是软件工程师)在不实际编写代码的情况下使用我们的库,以及其他原因。我正在设计一个步骤,用户将通过该步骤选择要使用的方法,然后通过 Workflow Arguments 填充输入参数列表。执行工作流将在运行时在对象上调用此方法(是的,这有效)。

我的步骤逻辑

[Designer(typeof(EntityMethodStepDesigner))]
public class EntityMethodStep : CodeActivity
{
    ...
    [Browsable(false)]
    public ObservableCollection<ArgumentWrapper> MethodArguments { get; set; }

    public EntityMethodStep()
    : base()
    {
        MethodArguments = new ObservableCollection<ArgumentWrapper>();
    }

    ....
    protected override void CacheMetadata(CodeActivityMetadata metadata)
    {
        Collection<RuntimeArgument> arguments = new Collection<RuntimeArgument>();

        foreach (ArgumentWrapper wrapper in MethodArguments)
        {
            RuntimeArgument ra = new RuntimeArgument(wrapper.MethodParameterName, wrapper.Argument.ArgumentType, wrapper.Argument.Direction, true);
            metadata.Bind(wrapper.Argument, ra);
            arguments.Add(ra);
        }

        metadata.SetArgumentsCollection(arguments);
    }
}

我的步骤视图

<sap:ActivityDesigner x:Class="MyNamespace.Steps.EntityMethodStepDesigner"
                      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>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="../Resources.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</sap:ActivityDesigner.Resources>

<Grid>

    <ComboBox Grid.Row="1"
              ItemsSource="{Binding AvailableEntityMethods}"
              SelectedItem="{Binding SelectedEntityMethod}" />

    <GroupBox Grid.Row="3" Header="Parameters">
        <ListView ItemsSource="{Binding ModelItem.MethodArguments}"
                  ItemTemplate="{StaticResource myNonEnumArgumentWrapperTemplate}">
        </ListView>
    </GroupBox>
</Grid>

myNonEnumArgumentWrapperTemplate 数据模板

<DataTemplate x:Key="myNonEnumArgumentWrapperTemplate">
    <StackPanel Orientation="Horizontal">

        <sapv:ExpressionTextBox Expression="{Binding Path=Argument, Mode=TwoWay, Converter={StaticResource ArgumentToExpressionConverter}, ConverterParameter=In }"
                                ExpressionType="{Binding Path=ArgumentType, Converter={StaticResource ModelToObjectValueConverter}, Mode=OneWay }"
                                OwnerActivity="{Binding ModelItem, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type sap:ActivityDesigner}}}"
                                MaxLines="1" />

    </StackPanel>
</DataTemplate>

我的步骤视图代码

public partial class EntityMethodStepDesigner : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public EntityMethodStepDesigner()
    {
        InitializeComponent();

        AvailableEntityMethods = new ObservableCollection<MethodInfo>(); // This list is populated elsewhere, so for all intents and purposes consider this list to be populated

        this.DataContext = this;
    }

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }

    public ObservableCollection<MethodInfo> AvailableEntityMethods { get; set; }
    private MethodInfo _selectedEntityMethod;
    public MethodInfo SelectedEntityMethod
    {
        get { return _selectedEntityMethod; }
        set
        {
            if (_selectedEntityMethod == value) return;
            _selectedEntityMethod = value;

            if (_selectedEntityMethod != null)
            {
                ModelProperty methodArguments = ModelItem.Properties["MethodArguments"];
                methodArguments.Collection.Clear();
                ParameterInfo[] parameters = _selectedEntityMethod.GetParameters();
                foreach (ParameterInfo parameter in parameters)
                {
                    ArgumentWrapper wrapper = new ArgumentWrapper(parameter);
                    methodArguments.Collection.Add(wrapper);
                }
            }
        }
    }
}

ArgumentWrapper 类

[Serializable]
public class ArgumentWrapper : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public ArgumentWrapper()
    {
    }

    public ArgumentWrapper(ParameterInfo methodParameter)
    {
        // Store the method direction
        if (methodParameter.IsIn && methodParameter.IsOut)
            MethodParameterDirection = ArgumentDirection.InOut;
        else if (methodParameter.IsOut)
            MethodParameterDirection = ArgumentDirection.Out;
        else
            MethodParameterDirection = ArgumentDirection.In;

        // Create an argument, with type and direction matching the input
        Type activatorType = null;
        if (methodParameter.IsIn && methodParameter.IsOut)
            activatorType = typeof(InOutArgument<>).MakeGenericType(methodParameter.ParameterType);
        else if (methodParameter.IsOut)
            activatorType = typeof(OutArgument<>).MakeGenericType(methodParameter.ParameterType);
        else
            activatorType = typeof(InArgument<>).MakeGenericType(methodParameter.ParameterType);
        Argument = (Argument)Activator.CreateInstance(activatorType);

        ArgumentType = methodParameter.ParameterType;
    }

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }

    private Argument _argument;
    public Argument Argument
    {
        get { return _argument; }
        set
        {
            if (_argument == value) return;
            _argument = value;
            OnPropertyChanged("Argument");
        }
    }

    private Type _argumentType;
    public Type ArgumentType
    {
        get { return _argumentType; }
        set
        {
            if (_argumentType == value) return;
            _argumentType = value;
            OnPropertyChanged("ArgumentType");
        }
    }

    public ArgumentDirection MethodParameterDirection { get; set; }

    public void ReferToEntityDirectlyByName(bool enable)
    {
        if (enable)
        {
            Argument = new InArgument<string>();
            ArgumentType = typeof(string);
        }
        else
        {
            Type activatorType;
            if (MethodParameterDirection == ArgumentDirection.InOut)
                activatorType = typeof(InOutArgument<>).MakeGenericType(MethodParameterType);
            else if (MethodParameterDirection == ArgumentDirection.Out)
                activatorType = typeof(OutArgument<>).MakeGenericType(MethodParameterType);
            else
                activatorType = typeof(InArgument<>).MakeGenericType(MethodParameterType);
            Argument = (Argument)Activator.CreateInstance(activatorType);

            ArgumentType = MethodParameterType;
        }
    }
}

为了将这一切结合在一起,我在此步骤中截取了一个屏幕截图,并选择了一种方法:

在此处输入图像描述

这显示了将在对象上调用的方法,采用 5 个参数,其中 3 个是枚举(红色错误是因为字段为空)。

现在,绑定几乎可以工作了——视图正确初始化,允许我填充字段并执行工作流。但是如果我对ArgumentWrapper对象进行更改,视图不会更新。

问题: 如果我在代码中更改ArgumentWrapper.ArgumentType,则更改不会传播到ExpressionTextBox。我知道这一点是因为当ArgumentToExpressionConverter将输入的表达式转换为参数时,它总是在构造时返回该类型的参数,无论该类型是否已更改。我尝试实现INotifyPropertyChanged,但PropertyChanged为空,我一直无法弄清楚原因。

4

1 回答 1

0

在您的ArgumentType财产中,您的声明中有这段代码else

Type argumentType = null;
if (MethodParameterDirection == ArgumentDirection.InOut)
    argumentType = typeof(InOutArgument<>).MakeGenericType(MethodParameterType);
else if (MethodParameterDirection == ArgumentDirection.Out)
    argumentType = typeof(OutArgument<>).MakeGenericType(MethodParameterType);
else
    argumentType = typeof(InArgument<>).MakeGenericType(MethodParameterType);

Argument = (Argument)Activator.CreateInstance(argumentType);

argumentType变量未设置为_argumentType字段或ArgumentType属性。我不确定这是否相关,但如果是,那么您需要使用该_argumentType字段并OnPropertyChanged("ArgumentType");在属性代码的末尾调用。

于 2013-10-30T11:52:49.557 回答