【问题标题】:How to use INotifyPropertyChanged with Validation on WPF?如何在 WPF 上使用 INotifyPropertyChanged 和验证?
【发布时间】:2020-08-28 14:24:13
【问题描述】:

我在尝试将 INotifyPropertyChanged 与 WPF 中的验证一起使用时遇到了一点麻烦。基本上,我希望 TextBox 警告用户有关空字段并更新(使用绑定)XAML 后面代码中的值。

几乎一切工作都很好,唯一的问题是,例如,我在文本框中键入字符串“Hello World”,然后删除所有内容。后面的代码不会在删除的最后一个字符上更新,而是保留值“H”,而不是空字符串。

我的短代码如下:

UserControlClientes.xaml

<TextBox x:Name="NomeTextBox" CharacterCasing="Upper" HorizontalAlignment="Stretch" VerticalAlignment="Top">
    <TextBox.Text>
        <Binding Path="Nome" TargetNullValue="''" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <domain1:RequiredField ValidatesOnTargetUpdated="True" />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

(“domain1”已定义并引用RequiredField.cs所在的文件夹)

UserControlClientes.xaml.cs

public class Dados_Administrativos: INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private string _Nome;

    public string Nome
    {
        get { return _Nome; }
        set
        {
            _Nome = value;
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs("Nome"));
            }
        }
    }
}


public UserControlClientes()
{
    InitializeComponent();
    Cliente ClienteBind = new Cliente();
    DataContext = ClienteBind;
}

RequiredField.cs

    public class RequiredField: ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            return string.IsNullOrWhiteSpace((value ?? "").ToString())
                ? new ValidationResult(false, "Required field")
                : ValidationResult.ValidResult;
        }
    }

我是 WPF 新手,所以我这样做的方式是否正确?我该如何解决这个问题?

【问题讨论】:

  • 作为一般说明,UserControl 永远不应在其 DataContext 中拥有自己的私有视图模型。这种私有视图模型不能从控件外部访问,因此不能成为应用程序范围的视图模型结构的一部分。只是不要显式设置控件的 DataContext,而是传递一个“外部”视图模型对象,例如通过&lt;local:UserControlClientes DataContext="{Binding ClientsViewModel}"/&gt;。或者通过类似地绑定在其 ContentTemplate 中具有控件的 ContentControl 的内容。
  • 您对这种行为有何期望?它应该如何表现?您的验证规则确保文本不为空,因此当您逐个删除每个字符时,在框为空之前剩余的最后一个字符仍然有效。之后该框为空并且验证规则失败,因此不会更新源。从您显示的行为来看,它按预期工作。
  • @Clemens 谢谢你的提示,我会这样做的。
  • @thatguy 我期待即使验证规则失败,源也会更新(为空)。但是,按照你所说的,我想这不是验证的工作方式,对吧?无论如何我可以验证文本框是否为空(来自主代码)?
  • @Clemens 我在执行您所说的操作时遇到了问题。我只需要在父级(在我的情况下是一个窗口)上声明 DataContext,子级(UserControl)将收到相同的 DataContext?在 xaml 或构造函数中声明数据上下文的正确方法是什么?

标签: c# wpf


【解决方案1】:

您可以更改 ValidationRule 的行为,使其在运行验证之前首先更新源属性,方法是将其 ValidationStep 属性设置为 UpdatedValue

<local:RequiredField
     ValidatesOnTargetUpdated="True"
     ValidationStep="UpdatedValue"/>

除此之外,我还建议通过公开一个或多个可绑定属性来移除 UserControl 对特定视图模型的依赖,例如ClientName 属性。在下面的示例中,属性已注册,因此默认情况下它是双向绑定的。

public partial class ClientControl : UserControl
{
    public ClientControl()
    {
        InitializeComponent();
    }

    public string ClientName
    {
        get { return (string)GetValue(ClientNameProperty); }
        set { SetValue(ClientNameProperty, value); }
    }

    public static readonly DependencyProperty ClientNameProperty =
        DependencyProperty.Register(
            nameof(ClientName), typeof(string), typeof(ClientControl),
            new FrameworkPropertyMetadata(
                null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
}

控件的 XAML 中的元素将通过RelativeSource Bindings 绑定到它自己的属性:

<UserControl x:Class="YourNamespace.ClientControl" ...>
    <Grid>
        <TextBox Margin="10">
            <TextBox.Text>
                <Binding Path="ClientName"
                         RelativeSource="{RelativeSource AncestorType=UserControl}"
                         UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <local:RequiredField
                            ValidatesOnTargetUpdated="True"
                            ValidationStep="UpdatedValue"/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
    </Grid>
</UserControl>

在 MainWindow(或您使用控件的任何地方)中,您可以将其属性绑定到任意视图模型的属性:

<local:ClientControl ClientName="{Binding NamePropertyInViewModel}"/>

ValidationRule 也可以稍微改进一下:

public class RequiredField : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        return string.IsNullOrWhiteSpace(value as string) // here
            ? new ValidationResult(false, "Required field")
            : ValidationResult.ValidResult;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-12
    • 2010-11-11
    • 2011-03-31
    • 2011-12-17
    • 1970-01-01
    • 1970-01-01
    • 2011-06-25
    相关资源
    最近更新 更多