简体   繁体   English

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

[英]How to bind to TextBox.Text in local ValidationRule class

This is my XAML file: 这是我的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>

This is my MainWindow class: 这是我的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;
    }
}

And this is my ValidationRule class: 这是我的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;
        }
    }

As you can see, when the NameValidation.Validate is called, I want NameValidation.Params to be populated with correct variables: 如您所见,当调用NameValidation.Validate时,我希望使用正确的变量填充NameValidation.Params

  1. NameValidation.Params.OriginalTree should be the original ListView . NameValidation.Params.OriginalTree应该是原始的ListView This I can get. 我能得到。
  2. NameValidation.Params.OriginalName should be the current TextBox's Text. NameValidation.Params.OriginalName应该是当前TextBox的Text。 In this case, it is bound to Mail , so I would expect to see that it is the email address like john@doe-family.com . 在这种情况下,它绑定到Mail ,因此我希望看到它是类似于john@doe-family.com的电子邮件地址。 However I can't get this out; 但是我无法解决这个问题。 all I can see is that NameValidation.Params.OriginalName is "WpfListView.NameValidationParameters" 我所看到的是NameValidation.Params.OriginalName是“ WpfListView.NameValidationParameters”

Also, I want to be able to access the current index in the list inside the NameValidation.Validate method. 另外,我希望能够访问NameValidation.Validate方法内列表中的当前索引。 How can I get it? 我怎么才能得到它?

As mentioned elsewhere, Validation rules do not hold or inherit a DataContext this will prevent binding from working as expected if you try to declare everything inline. 如在其他地方提到的,验证规则不保存或继承DataContext,如果您尝试内联声明所有内容,这将阻止绑定按预期工作。 Instead, declare your bindable rule options as a resource and set the property on your custom rule using a StaticBinding. 而是将可绑定规则选项声明为资源,并使用StaticBinding在自定义规则上设置属性。

I've modified your TextBox to add a resource parameter: 我已修改您的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>

And NameValidationParameters to implement Freezable 和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();
    }
}

However, Here is an example of how i implemented INotifyDataErrorInfo in my project. 但是,这是我如何在项目中实现INotifyDataErrorInfo的示例。 This is a very clean way to trigger validation errors and xaml allows you to configure the display of errors 这是触发验证错误的非常干净的方法,而xaml允许您配置错误的显示

Model class: 型号类别:

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

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

ModelWrapper Generic: 通用ModelWrapper:

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);
        }
    }
}

Generic Implementation: 通用实现:

    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";
        }
    }
}

ViewModel: ViewModel:

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

View : 查看:

<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}"/>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM