在固定位置显示WPF验证错误消息

4
我已经让WPF验证运行起来了(在绑定中添加了ValidationRules),并且通过模板可以创建漂亮的装饰器。网络上有很多相关帖子。

但我找不到一种方法将错误消息显示在装饰控件之外的固定位置,比如窗口角落的 TextBlock 中。

我该如何实现这个?我能否将所有验证错误信息绑定到我的DataContext(这里是ViewModel)?


更新:由于得到了一个答案,现在部分工作已经完成。验证消息现在显示在另一个标签中。由于所有带有其验证规则的文本框都是通过代码动态创建的,因此进行绑定的方式如下:

Binding bindSite = new Binding();
bindSite.Source = this.validationErrorDisplayLabel;
BindingOperations.SetBinding(textBox, Validation.ValidationAdornerSiteProperty, bindSite);

但是验证消息仅被转发给最后一个执行此代码的文本框的adornersite


我在这个小例子中重现了这个问题。

XAML:

<Grid>
    <TextBox 
        Validation.ValidationAdornerSite="{Binding ElementName=ErrorDisplay}"
        HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120">
        <TextBox.Text>
            <Binding>
                <Binding.Path>Box1</Binding.Path>
                <Binding.ValidationRules>
                    <local:RuleA />
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>
    <TextBox 
        Validation.ValidationAdornerSite="{Binding ElementName=ErrorDisplay}"
        HorizontalAlignment="Left" Height="23" Margin="10,38,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120">
        <TextBox.Text>
            <Binding>
                <Binding.Path>Box2</Binding.Path>
                <Binding.ValidationRules>
                    <local:RuleA />
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>
    <TextBlock 
        x:Name="ErrorDisplay"
        Background="AntiqueWhite"
        Foreground="Red"
        Text="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.ValidationAdornerSiteFor).(Validation.Errors)[0].ErrorContent}"
        HorizontalAlignment="Left" Margin="230,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" RenderTransformOrigin="2.218,-4.577" Width="177" Height="51"/>
</Grid>

RuleA类在值等于字符串"A"时产生验证错误。第二个文本框中的错误会显示在中,而第一个文本框的错误不会被显示(它使用默认模板并获得红色边框)。

如何使其两者都适用?TextBlock不需要汇总所有错误,而只需显示第一个错误即可。

2个回答

8
您可以使用BindingGroupValidation.ValidationAdornerSiteValidation.ValidationAdornerSiteFor属性结合使用。 这篇博客文章给出了一个实现示例。
<StackPanel x:Name="FormRoot"
            Validation.ValidationAdornerSite="{Binding ElementName=ErrorDisplay}">
  <FrameworkElement.BindingGroup>
    <BindingGroup Name="FormBindingGroup" />
  </FrameworkElement.BindingGroup>

  <TextBox>
    <TextBox.Text>
      <Binding BindingGroupName="FormBindingGroup"
               UpdateSourceTrigger="LostFocus"
               Path="Box1">
        <Binding.ValidationRules>
          <l:RuleA />
        </Binding.ValidationRules>
      </Binding>
    </TextBox.Text>
  </TextBox>

<TextBox>
    <TextBox.Text>
      <Binding BindingGroupName="FormBindingGroup"
               UpdateSourceTrigger="LostFocus"
               Path="Box2">
        <Binding.ValidationRules>
          <l:RuleA />
        </Binding.ValidationRules>
      </Binding>
    </TextBox.Text>
  </TextBox>

  <ItemsControl x:Name="ErrorDisplay"
                Background="AntiqueWhite"
                Foreground="Red"
                ItemsSource="{Binding RelativeSource={RelativeSource Self},
                                      Path=(Validation.ValidationAdornerSiteFor).(Validation.Errors)}"
                DisplayMemberPath="ErrorContent" />
</StackPanel>

为了在用户输入时提交值,请将UpdateSourceTrigger的值更改为PropertyChanged。请注意,在这里使用ValidationAdornerSite并不是严格必要的;您可以直接将ErrorDisplay绑定到BindingGroup的所有者:

ItemsSource="{Binding ElementName=FormRoot, Path=(Validation.Errors)}"

感谢您的提示!通过使用 Validation.ValidationAdornerSiteValidation.ValidationAdornerSiteFor,我将验证结果传递到了另一个控件。但是它仅适用于最后一个分配了 ..AdornerSite 的文本框中的错误。 - ZoolWay
我将此答案评为最佳,因为它虽然没有指导我走向最终解决方案,但仍对我有所帮助。感谢您的回答。请参阅被接受的答案以了解我的最终解决方案。 - ZoolWay
谢谢,很高兴能帮忙! - Mike Strobel

3
感谢 http://www.scottlogic.com/blog/2008/11/28/using-bindinggroups-for-greater-control-over-input-validation.html 的帮助,我使用 BindingGroup 成功解决了这个问题,而无需使用 ValidationAdornerSite。
<Window x:Class="BindingAndValidation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:BindingAndValidation"
        Title="Binding and Validation" Height="110" Width="425"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        >
    <Grid x:Name="RootElement">
        <Grid.Resources>
            <Style TargetType="{x:Type TextBox}">
                <Style.Triggers>
                    <Trigger Property="Validation.HasError" Value="true">
                        <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Grid.Resources>
        <Grid.BindingGroup>
            <BindingGroup Name="LocalBindingGroup">
                <BindingGroup.ValidationRules>
                    <local:RuleGroup />
                </BindingGroup.ValidationRules>
            </BindingGroup>
        </Grid.BindingGroup>
        <TextBox 
            HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" LostFocus="TextBox_LostFocus">
            <TextBox.Text>
                <Binding>
                    <Binding.Path>Box1</Binding.Path>
                    <Binding.BindingGroupName>LocalBindingGroup</Binding.BindingGroupName>
                    <Binding.ValidationRules>
                        <local:RuleA />
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
        <TextBox 
            HorizontalAlignment="Left" Height="23" Margin="10,38,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120">
            <TextBox.Text>
                <Binding>
                    <Binding.Path>Box2</Binding.Path>
                    <Binding.BindingGroupName>LocalBindingGroup</Binding.BindingGroupName>
                    <Binding.ValidationRules>
                        <local:RuleA />
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
        <ItemsControl ItemsSource="{Binding Path=(Validation.Errors), ElementName=RootElement}" MinWidth="100" MinHeight="16" Margin="230,10,0,0" Background="AntiqueWhite" Foreground="Red">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Label Foreground="Red" Content="{Binding Path=ErrorContent}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
        <Button Content="Button" HorizontalAlignment="Left" Margin="150,0,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click" />
    </Grid>
</Window>

只有在调用CommitEdit时才进行验证。如果您想立即进行验证,可以使用LostFocus

    private void TextBox_LostFocus(object sender, RoutedEventArgs e)
    {
        this.RootElement.BindingGroup.CommitEdit();
    }

当然,对于一个更大的项目,附加属性可能会有帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接