【问题标题】:Master-Detail validation WPF主从验证 WPF
【发布时间】:2018-01-14 16:58:20
【问题描述】:

我有一个带有 Master-Detail 视图的项目,其中 Master 部分包含一个用于选择对象的列表,Detail 部分显示该对象的细节并允许对其进行编辑。

我的问题是我不能允许 2 个对象具有相同的名称,虽然这听起来很容易,但事实证明我知道的验证过程并不能很好地发挥作用。

这是一个简短的例子。

XAML

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:WpfApplication1="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="2*"/>
        </Grid.ColumnDefinitions>
        <ListView Grid.Column="0" ItemsSource="{Binding Path=People, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" Name="masterList"
                DisplayMemberPath="Name" SelectedItem="{Binding Path=SelectedPerson}" />

        <StackPanel Grid.Column="1" Margin="5" DataContext="{Binding Path=SelectedPerson}">
            <TextBlock>Name</TextBlock>
            <TextBox>
                <TextBox.Text>
                    <Binding Path="Name" Mode="TwoWay" ValidatesOnDataErrors="True" NotifyOnValidationError="True">
                    </Binding>
                </TextBox.Text>
            </TextBox>
            <TextBlock>Address</TextBlock>
            <TextBox Text="{Binding Path=Address}" />
            <TextBlock>Phone</TextBlock>
            <TextBox Text="{Binding Path=Phone}" />
        </StackPanel>
    </Grid>
</Window>

人物类

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;

public class Person {
    public string Name { get; set; }
    public string Address { get; set; }
    public string Phone { get; set; }
}

RegisteredPeople 类

public class RegisteredPeople {
    public ObservableCollection<Person> People { get; set; }
    public Person SelectedPerson { get; set; }
    public RegisteredPeople() {
        People = new ObservableCollection<Person>() {
            new Person() {Name = "Ramzi", Address = "A1", Phone = "1"},
            new Person() {Name = "Frank", Address = "A2", Phone = "12"},
            new Person() {Name = "Ihab", Address = "A3", Phone = "123"}
        };
    }
}

这没有任何验证,但它显示了我想要的基本机制。


我尝试在两个类上实现IDataErrorInfo,但没有成功:

Person 类的其他实现

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;

public class Person : System.ComponentModel.IDataErrorInfo, INotifyPropertyChanged {
    private string m_name;

    public string Name {
        get { return m_name; }
        set {
            m_name = value;
            OnPropertyChanged();
            OnPropertyChanged("People"); //Name of the list that the master list is bound to.
        }
    }

    public string Address { get; set; }
    public string Phone { get; set; }

    public string this[string columnName] {
        get {
            switch (columnName) {
                case "Name":
                    if (string.IsNullOrEmpty(Name)) {
            /** This one works, but from here I cannot compare with names of other Person objects. **/
                        return "The name cannot be empty.";
                    }
                    break;
                default:
                    return string.Empty;
            }
            return String.Empty;
        }
    }

    public string Error { get; }
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

RegisteredPeople 类的其他实现

public class RegisteredPeople : System.ComponentModel.IDataErrorInfo {
    public ObservableCollection<Person> People { get; set; }
    public Person SelectedPerson { get; set; }
    public RegisteredPeople() {
        People = new ObservableCollection<Person>() {
            new Person() {Name = "Ramzi", Address = "A1", Phone = "1"},
            new Person() {Name = "Frank", Address = "A2", Phone = "12"},
            new Person() {Name = "Ihab", Address = "A3", Phone = "123"}
        };
    }

    public string this[string columnName] {
        get {
            switch (columnName) {
                case "People":
                    foreach (Person person1 in People) {
                        foreach (Person person2 in People) {
                            if (person1 == person2) {
                                continue;
                            }
                            if (person1.Name == person2.Name) {
                                return "Error, 2 people cannot have the same name.";
                            }
                        }
                    }
                    break;
                default:
                    return string.Empty;
            }
            return string.Empty;
        }
    }

    public string Error { get; }
}

我也尝试过使用ValidationRule 界面,但没有成功。

这是我尝试过的:

XAML

我将名称的文本框替换为:

        <TextBox>
            <TextBox.Text>
                <Binding Path="Name" Mode="TwoWay"  ValidatesOnDataErrors="True" NotifyOnValidationError="True">
                    <Binding.ValidationRules>
                        <WpfApplication1:EventNameValidationRule EventList="{Binding ElementName=masterList, Path=ItemsSource}" />
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>

EventNameValidationRule

using System.Collections.ObjectModel;
using System.Globalization;
using System.Windows.Controls;

class EventNameValidationRule : ValidationRule {
    public ObservableCollection<Person> EventList { get; set; }
    public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
        return new ValidationResult(false, "Duplicate names are not allowd.");
    }
}

问题是这样抛出异常,说明绑定到EventList 不好。 (如果没有这份清单,我显然没有比较的起点。)


我的问题:当有两个人叫同一个名字时,如何将这个名字标记为无效?

【问题讨论】:

  • 也许您应该告诉我们您对 ValidationRule 的尝试。对我来说,使用 ValidationRule 似乎是可行的方法。您可以尝试将您的列表绑定到 ValidationRule(如何做到这一点:dedjo.blogspot.de/2007/05/fully-binded-validation-by-using.html),然后,当调用 Validate 方法时,只需检查您绑定到 ValidationRule 的列表,如果它包含多个带有给定的名字。
  • 感谢@Dr.Coconut 查看我的编辑。

标签: c# wpf xaml master-detail


【解决方案1】:

您需要在Person 类中实现您的验证逻辑,即实现IDataErrorInfo 接口的实际DataContext

例如,您可以在 RegisteredPeople 类中创建一个从 Person 类的索引器调用的方法,例如:

public class Person : IDataErrorInfo, INotifyPropertyChanged
{
    private readonly IValidator _validator;
    public Person(IValidator validator)
    {
        _validator = validator;
    }

    private string m_name;
    public string Name
    {
        get { return m_name; }
        set
        {
            m_name = value;
            OnPropertyChanged();
        }
    }

    public string Address { get; set; }
    public string Phone { get; set; }

    public string this[string columnName]
    {
        get
        {
            switch (columnName)
            {
                case "Name":
                    if (string.IsNullOrEmpty(Name))
                    {
                        /** This one works, but from here I cannot compare with names of other Person objects. **/
                        return "The name cannot be empty.";
                    }
                    else
                    {
                        return _validator.Validate(this);
                    }
                default:
                    return string.Empty;
            }
        }
    }

    public string Error { get; }
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

public interface IValidator
{
    string Validate(Person person);
}

public class RegisteredPeople : IValidator
{
    public ObservableCollection<Person> People { get; set; }
    public Person SelectedPerson { get; set; }

    public string Validate(Person person)
    {
        if (person != null && People.Any(x => x != person && x.Name == person.Name))
            return "Same name!";

        return null;
    }

    public RegisteredPeople()
    {
        People = new ObservableCollection<Person>() {
        new Person(this) {Name = "Ramzi", Address = "A1", Phone = "1"},
        new Person(this) {Name = "Frank", Address = "A2", Phone = "12"},
        new Person(this) {Name = "Ihab", Address = "A3", Phone = "123"}
    };
    }
}

【讨论】:

    猜你喜欢
    • 2011-08-21
    • 1970-01-01
    • 1970-01-01
    • 2012-12-09
    • 2013-02-18
    • 2014-08-18
    • 2011-01-06
    • 2010-12-06
    • 1970-01-01
    相关资源
    最近更新 更多