正在验证WPF动态创建的框架元素



我正在创建一个由动态创建的控件组成的窗口。我正在使用ItemsControl:创建它们

<ItemsControl Grid.Row="0" Name="DynamicContent" ItemsSource="{Binding Path=EmbeddedInputControls}" ItemTemplateSelector="{Binding Path=EmbeddedInputControlsTemplateSelector}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

其中ItemTemplateSelector根据资源类型选择DataTemplate

<Window.Resources>
<converters:ErrorBooleanToBrush x:Key="ErrorBooleanToBrush"/>
<DataTemplate x:Key="EmbeddedStringInput" DataType="embeddedInputDescriptors:StringEmbeddedInputDescriptor">
<StackPanel Orientation="Horizontal" Background="{Binding IsErrored, Converter={StaticResource ErrorBooleanToBrush}}">
<Label Content="{Binding LabelContent}"></Label>
<dxe:TextEdit EditValue="{Binding TextValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="{Binding IsReadOnly}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="EmbeddedIntegerInput" DataType="embeddedInputDescriptors:IntegerEmbeddedInputDescriptor">
<StackPanel Orientation="Horizontal" Name="IntStackPanel">
<Label Content="{Binding LabelContent}"></Label>
<dxe:SpinEdit Value="{Binding IntValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="{Binding IsReadOnly}"
MinValue="{Binding MinValue}" MaxValue="{Binding MaxValue}" Validate="OnValidate" Tag="{Binding Self}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>

这对显示效果很好,但验证已被证明是一个令人头疼的问题。我最终找到的解决方案使用代码隐藏和OnValidate事件处理程序:

private void OnValidate(object sender, ValidationEventArgs e)
{
var dynamicInputElement=((FrameworkElement)sender).Tag as IEmbeddedInputDescriptor;
var errorContent = dynamicInputElement?.ErrorContent(e.Value);
if(!string.IsNullOrEmpty(errorContent))
{
e.IsValid = false;
e.ErrorContent = errorContent;
}
}

这是我必须编写的唯一代码,我想去掉它,但我不知道没有它如何实现验证。验证规则将基于业务逻辑,所以我不能在XAML中使用它们。当我给每个DataTemplate一个样式时,我几乎得到了一个解决方案,该样式的成员是从相关的描述符中获得的,但它有点难看:

{Binding IsErrored, Converter={StaticResource ErrorBooleanToBrush}}
<StackPanel.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsErrored}" Value="True">
<Setter Property="StackPanel.Background" Value="LightCoral">
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>

用于派生框架元素的对象有:

internal interface IEmbeddedInputDescriptor
{
bool IsReadOnly { get; }
bool IsRequired { get; }
object Value { get; }
bool IsErrored { get; }
string ErrorContent(object value);
}

去掉后面的代码会很好,我相信在这个过程中我会学到更多关于WPF的有用东西。

更多信息:重新绑定验证:

<dxe:SpinEdit Value="{Binding IntValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidationRules={Binding valRules}}"

public IEnumerable<ValidationRule> valRules
{
get { throw new NotImplementedException(); }
}

错误"A"Binding的结果不能在"ValidationRuleCollection"集合中使用。只能在DependencyObject的DependencyProperty上设置"Binding"。

我的IDataErrorInfo也有问题。无论是在主视图模型中还是在描述符本身中实现,我都从未设法调用任何一个属性。如果我走到这一步,一个可能的问题是如何识别错误属性,因为我怀疑它总是"Value",这是不明确的。事实上,这就是大多数解决方案的问题——那些产生了我可以回应的事件的解决方案让我没有工作的背景。

这就是为什么我目前的解决方案采用Tag属性。如果没有这些,就很难将表单事件链接回底层业务对象。

答案与我实现描述符类(成为每个创建的框架元素的数据上下文的对象)的方式有关。我在"错误"基类上实现了IDataErrorInfo,这意味着它在类层次结构中不可见。睡了一个好觉后,我眼睛一亮,几乎立刻就发现了它。

经常这样。

最新更新