首先,我会总结一下我在做什么。然后,我将介绍我认为是这些步骤之一的相关代码位,这些步骤展示了标题问题,然后我将在最后解释问题。
我正在公开一个内部库,用于将我们的一个软件产品自动化到 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为空,我一直无法弄清楚原因。