我目前正在实现ValidationRule来检查TextBox中是否有一些无效字符。我很高兴设置我实现的继承了TextBox上ValidationRule的类,当找到此类字符时将其设置为红色,但是我也想使用Validation.HasError属性或Validation.Errors属性来弹出一个消息框,告诉用户页面的各个文本框中均存在错误。

有没有一种方法可以将ViewModel中的属性绑定(bind)到Validation.HasError和/或Validation.Errors属性,以便我可以在ViewModel中访问它们?

这是我的TextBox错误样式:

<Style x:Key="ErrorValidationTextBox" TargetType="{x:Type pres:OneTextBox}">
    <Setter Property="Validation.ErrorTemplate">
        <Setter.Value>
            <ControlTemplate>
                <DockPanel LastChildFill="True">
                    <TextBlock DockPanel.Dock="Right"
                    Foreground="Red"
                    FontSize="12pt"
                    Text="{Binding ElementName=MyAdorner,
                           Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
                    </TextBlock>
                    <AdornedElementPlaceholder x:Name="MyAdorner"/>
                </DockPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

这是我在XAML中声明我的TextBox(OneTextBox封装常规WPF TextBox)的方式:
<pres:OneTextBox Watermark="Name..." Margin="85,12,0,0" Style="{StaticResource ErrorValidationTextBox}"
                 AcceptsReturn="False" MaxLines="1" Height="22" VerticalAlignment="Top"
                 HorizontalAlignment="Left" Width="300" >
    <pres:OneTextBox.Text>
        <Binding Path="InterfaceSpecification.Name" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <interfaceSpecsModule:NoInvalidCharsRule/>
            </Binding.ValidationRules>
        </Binding>
    </pres:OneTextBox.Text>
</pres:OneTextBox>

最佳答案

Validation.HasError是只读属性,因此Binding不适用于此属性。可以在 ILSpy 中看到:

public virtual bool HasError
{
    get
    {
        return this._validationError != null;
    }
}

另外,您应该看到一个很棒的 article ,它以使用附加的依赖项属性的形式提供了一种解决方案,在那里您将看到该示例的详细说明。

以下是本文的完整示例,我只是将其翻译为C#,原始语言为VB.NET:

XAML
<Window x:Class="HasErrorTestValidation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:HasErrorTestValidation"
        WindowStartupLocation="CenterScreen"
        Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <local:TestData />
    </Window.DataContext>

    <StackPanel>
        <TextBox x:Name="TestTextBox"
                 local:ProtocolSettingsLayout.MVVMHasError="{Binding Path=HasError}">
            <TextBox.Text>
                <Binding Path="TestText" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <local:OnlyNumbersValidationRule ValidatesOnTargetUpdated="True"/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>

        <TextBlock>
            <TextBlock.Text>
                <Binding Path="HasError" StringFormat="HasError is {0}"/>
            </TextBlock.Text>
        </TextBlock>

        <TextBlock>
            <TextBlock.Text>
                <Binding Path="(Validation.HasError)" ElementName="TestTextBox" StringFormat="Validation.HasError is {0}"/>
            </TextBlock.Text>
        </TextBlock>
    </StackPanel>
</Window>

Code-behind
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
}

#region Model

public class TestData : INotifyPropertyChanged
{
    private bool _hasError = false;

    public bool HasError
    {
        get
        {
            return _hasError;
        }

        set
        {
            _hasError = value;
            NotifyPropertyChanged("HasError");
        }
    }

    private string _testText = "0";

    public string TestText
    {
        get
        {
            return _testText;
        }

        set
        {
            _testText = value;
            NotifyPropertyChanged("TestText");
        }
    }

    #region PropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(string sProp)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(sProp));
        }
    }

    #endregion
}

#endregion

#region ValidationRule

public class OnlyNumbersValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        var result = new ValidationResult(true, null);

        string NumberPattern = @"^[0-9-]+$";
        Regex rgx = new Regex(NumberPattern);

        if (rgx.IsMatch(value.ToString()) == false)
        {
            result = new ValidationResult(false, "Must be only numbers");
        }

        return result;
    }
}

#endregion

public class ProtocolSettingsLayout
{
    public static readonly DependencyProperty MVVMHasErrorProperty= DependencyProperty.RegisterAttached("MVVMHasError",
                                                                    typeof(bool),
                                                                    typeof(ProtocolSettingsLayout),
                                                                    new FrameworkPropertyMetadata(false,
                                                                                                  FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                                                                                                  null,
                                                                                                  CoerceMVVMHasError));

    public static bool GetMVVMHasError(DependencyObject d)
    {
        return (bool)d.GetValue(MVVMHasErrorProperty);
    }

    public static void SetMVVMHasError(DependencyObject d, bool value)
    {
        d.SetValue(MVVMHasErrorProperty, value);
    }

    private static object CoerceMVVMHasError(DependencyObject d,Object baseValue)
    {
        bool ret = (bool)baseValue;

        if (BindingOperations.IsDataBound(d,MVVMHasErrorProperty))
        {
            if (GetHasErrorDescriptor(d)==null)
            {
                DependencyPropertyDescriptor desc = DependencyPropertyDescriptor.FromProperty(Validation.HasErrorProperty, d.GetType());
                desc.AddValueChanged(d,OnHasErrorChanged);
                SetHasErrorDescriptor(d, desc);
                ret = System.Windows.Controls.Validation.GetHasError(d);
            }
        }
        else
        {
            if (GetHasErrorDescriptor(d)!=null)
            {
                DependencyPropertyDescriptor desc= GetHasErrorDescriptor(d);
                desc.RemoveValueChanged(d, OnHasErrorChanged);
                SetHasErrorDescriptor(d, null);
            }
        }

        return ret;
    }

    private static readonly DependencyProperty HasErrorDescriptorProperty = DependencyProperty.RegisterAttached("HasErrorDescriptor",
                                                                            typeof(DependencyPropertyDescriptor),
                                                                            typeof(ProtocolSettingsLayout));

    private static DependencyPropertyDescriptor GetHasErrorDescriptor(DependencyObject d)
    {
        var ret = d.GetValue(HasErrorDescriptorProperty);
        return ret as DependencyPropertyDescriptor;
    }

    private static void OnHasErrorChanged(object sender, EventArgs e)
    {
        DependencyObject d = sender as DependencyObject;

        if (d != null)
        {
            d.SetValue(MVVMHasErrorProperty, d.GetValue(Validation.HasErrorProperty));
        }
    }

   private static void SetHasErrorDescriptor(DependencyObject d, DependencyPropertyDescriptor value)
   {
        var ret = d.GetValue(HasErrorDescriptorProperty);
        d.SetValue(HasErrorDescriptorProperty, value);
    }
}

作为使用ValidationRule的替代方法,可以使用MVVM样式尝试实现 IDataErrorInfo 接口(interface)。有关更多信息,请参见:

Enforcing Complex Business Data Rules with WPF

关于c# - MVVM中的绑定(bind)Validation.HasError属性,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/22827437/

10-13 05:57