如何绑定到本地ValidationRule类中的TextBox.Text

时间:2018-02-06 05:27:21

标签: c# wpf xaml data-binding

这是我的XAML文件:

<Window x:Class="WpfListView.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:local="clr-namespace:WpfListView"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

    <Grid>
        <ListView Margin="10" Name="lvUsers">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="150" />
                            <ColumnDefinition Width="20" />
                        </Grid.ColumnDefinitions>
                        <TextBox Grid.Column="0" Margin="0,0,5,0">
                            <Binding Path="Mail" Mode="TwoWay">
                                <Binding.ValidationRules>
                                    <local:NameValidation>
                                        <local:NameValidation.Params>
                                            <local:NameValidationParameters 
                                                    OriginalTree="{Binding Source={x:Reference lvUsers}}"
                                                    OriginalName="{Binding RelativeSource={RelativeSource Self}}"/>   <!-- I want the OriginalName to be TextBox.Text-->
                                        </local:NameValidation.Params>
                                    </local:NameValidation>
                                </Binding.ValidationRules>
                            </Binding>
                        </TextBox>
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>


</Window>

这是我的MainWindow课程:

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        List<User> items = new List<User>();
        items.Add(new User() { Name = "John Doe", Age = 42, Mail = "john@doe-family.com" });
        items.Add(new User() { Name = "Jane Doe", Age = 39, Mail = "jane@doe-family.com" });
        items.Add(new User() { Name = "Sammy Doe", Age = 7, Mail = "sammy.doe@gmail.com" });
        lvUsers.ItemsSource = items;
    }
}

这是我的ValidationRule课程:

   public class NameValidationParameters : DependencyObject
    {
        public ListView OriginalTree
        {
            get { return (ListView)this.GetValue(OriginalTreeProperty); }
            set { this.SetValue(OriginalTreeProperty, value); }


        }

        public string OriginalName
        {
            get { return (string)this.GetValue(OriginalNameProperty); }
            set { this.SetValue(OriginalNameProperty, value); }


        }

        public static readonly DependencyProperty OriginalTreeProperty
        = DependencyProperty.Register(nameof(OriginalTree), typeof(ListView),
            typeof(NameValidationParameters));

        public static readonly DependencyProperty OriginalNameProperty
= DependencyProperty.Register(nameof(OriginalName), typeof(string),
    typeof(NameValidationParameters));


    }

    public class NameValidation : ValidationRule
    {
        public string ErrorMessage
        { get; set; }

        public NameValidationParameters Params { get; set; }



        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {

            ValidationResult lResult = null;

            return lResult;
        }
    }

正如您所看到的,当调用NameValidation.Validate时,我希望NameValidation.Params填充正确的变量:

  1. NameValidation.Params.OriginalTree应为原始ListView。我可以得到。
  2. NameValidation.Params.OriginalName应该是当前TextBox的文本。在这种情况下,它绑定到Mail,因此我希望看到它是john@doe-family.com之类的电子邮件地址。但是,我无法解决这个问题。我只能看到NameValidation.Params.OriginalName是&#34; WpfListView.NameValidationParameters&#34;
  3. 此外,我希望能够访问NameValidation.Validate方法中列表中的当前索引。我怎么能得到它?

1 个答案:

答案 0 :(得分:2)

如其他地方所述, 验证规则不保留或继承DataContext,如果您尝试内联声明所有内容,这将阻止绑定按预期工作。而是将可绑定规则选项声明为资源,并使用StaticBinding在自定义规则上设置属性。

我修改了TextBox以添加资源参数:

  <TextBox Grid.Column="0" Margin="0,0,5,0" x:Name="box1">
        <TextBox.Resources>
             <local:NameValidationParameters OriginalName="{Binding Mail}" OriginalTree="{Binding Source={x:Reference lvUsers}}" x:Key="Parameters"/>
        </TextBox.Resources>
        <TextBox.Text>
           <Binding Path="Mail" Mode="TwoWay">
               <Binding.ValidationRules>
                  <local:NameValidation Params="{StaticResource Parameters}"/>     
               </Binding.ValidationRules>
            </Binding>
         </TextBox.Text>
  </TextBox>

和NameValidationParameters实现Freezable

 public class NameValidationParameters : Freezable
{
    public ListView OriginalTree
    {
        get { return (ListView)this.GetValue(OriginalTreeProperty); }
        set { this.SetValue(OriginalTreeProperty, value); }


    }

    public string OriginalName
    {
        get { return (string)this.GetValue(OriginalNameProperty); }
        set { this.SetValue(OriginalNameProperty, value); }


    }

    public static readonly DependencyProperty OriginalTreeProperty
         = DependencyProperty.Register(nameof(OriginalTree), typeof(ListView),
             typeof(NameValidationParameters));

    public static readonly DependencyProperty OriginalNameProperty
        = DependencyProperty.Register(nameof(OriginalName), typeof(object),
            typeof(NameValidationParameters));

    protected override Freezable CreateInstanceCore()
    {
        return new NameValidationParameters();
    }
}

但是,这是我在项目中实现INotifyDataErrorInfo的示例。这是一种非常干净的触发验证错误的方法,xaml允许您配置错误显示

模特课:

 public class AttributionInput
{
    [Required]
    public DateTime? StartDate { get; set; }

    [Required]
    public DateTime? EndDate { get; set; }
}

ModelWrapper Generic:

public class ModelWrapper<T> : NotifyDataErrorInfoBase
{
    public T Model { get; protected set; }

    public ModelWrapper(T model)
    {
        Model = model;
    }

    public ModelWrapper()
    {

    }
    protected virtual TValue GetValue<TValue>([CallerMemberName] string propertyName = null)
    {
        return (TValue)typeof(T).GetProperty(propertyName)?.GetValue(Model);
    }

    protected virtual void SetValue<TValue>(TValue value, [CallerMemberName] string propertyName = null)
    {
        typeof(T).GetProperty(propertyName)?.SetValue(Model, value);
        OnPropertyChanged(propertyName);
        ValidatePropertyInternal(propertyName, value);
    }

    private void ValidatePropertyInternal(string propertyName, object currentValue)
    {
        ClearErrors(propertyName);
        ValidateDataAnnotations(propertyName, currentValue);
        ValidateCustomErrors(propertyName);
    }

    protected virtual IEnumerable<string> ValidateProperty(string propertyName)
    {
        return null;
    }

    private void ValidateCustomErrors(string propertyName)
    {
        var errors = ValidateProperty(propertyName);
        if (errors != null)
        {
            foreach (var error in errors)
            {
                AddError(propertyName, error);
            }
        }
    }

    private void ValidateDataAnnotations(string propertyName, object currentValue)
    {
        var results = new List<ValidationResult>();
        var context = new ValidationContext(Model) { MemberName = propertyName };
        Validator.TryValidateProperty(currentValue, context, results);

        foreach (var result in results)
        {
            AddError(propertyName, result.ErrorMessage);
        }
    }
}

通用实施:

    public class AttributionInputWrapper : ModelWrapper<AttributionInput>
{
    public AttributionInputWrapper(AttributionInput model) : base(model)
    {
    }

    public DateTime? StartDate
    {
        get => GetValue<DateTime?>();
        set
        {
            SetValue(value);
            if (EndDate < StartDate) EndDate = StartDate;
        }
    }

    public DateTime? EndDate
    {
        get => GetValue<DateTime?>();
        set
        {
            SetValue(value);
            if (EndDate < StartDate) StartDate = EndDate;
        }
    }

    protected override IEnumerable<string> ValidateProperty(string propertyName)
    {
        if (propertyName == nameof(EndDate) || propertyName == nameof(StartDate))
        {
            //if (StartDate.Value.Date > EndDate.Value.Date) yield return "Start Date must be <= End Date";
            if (EndDate != null && (EndDate.Value.DayOfWeek == DayOfWeek.Saturday || EndDate.Value.DayOfWeek == DayOfWeek.Sunday))
                yield return "Please select a week day";
            if (StartDate != null && (StartDate.Value.DayOfWeek == DayOfWeek.Saturday || StartDate.Value.DayOfWeek == DayOfWeek.Sunday))
                yield return "Please select a week day";
        }
    }
}

视图模型:

public class QueryViewModel : DetailViewModelBase, ICommonViewModel
{
  private AttributionInputWrapper _attributionInput;
  public AttributionInputWrapper AttributionInput
    {
        get => _attributionInput;
        set
        {
            _attributionInput = value;
            OnPropertyChanged();
        }
    }
 }

查看:

<Style TargetType="DatePicker">
        <Setter Property="Margin" Value="{StaticResource MarginString}"/>
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <StackPanel>
                        <AdornedElementPlaceholder x:Name="Placeholder1"/>
                        <TextBlock Text="{Binding ElementName=Placeholder1, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" FontSize="9"/>
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <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>
<DatePicker x:Name="StartDateBox" SelectedDate="{Binding AttributionInput.StartDate, UpdateSourceTrigger=PropertyChanged}" DisplayDateStart="10/01/2017" DisplayDateEnd="{x:Static system:DateTime.Now}"/>