ValidationRule から継承するクラスがあるとします。
public class MyValidationRule : ValidationRule
{
public string ValidationType { get; set; }
public override ValidationResult Validate(object value, CultureInfo cultureInfo) {}
}
XAML では次のように検証します。
<ComboBox.SelectedItem>
<Binding Path="MyPath" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
<Binding.ValidationRules>
<qmvalidation:MyValidationRule ValidationType="notnull"/>
</Binding.ValidationRules>
</Binding>
</ComboBox.SelectedItem>
それは機能し、すべてが正常です。
しかし、今度は、 がどこから来たValidationType="{Binding MyBinding}"
のかを知りたいとします。MyBinding
DataContext
MyValidationRule
この目的のためには、 を作成しDependencyObject
、 を追加する必要があります。依存関係プロパティ。
であるクラスを書いて、それをバインドしようとしましたDependencyObject
。ただし、2 つの問題があります。 には、Combobox / Item からのValidationRule
がありません。DataContext
それを解決する方法について何かアイデアはありますか?
ベストアンサー1
ValidationRule
は を継承しないため、カスタム検証クラスで をDependencyObject
作成することはできません。DependecyProperty
しかし、このリンク検証クラスに から継承する型の通常のプロパティを設定しDependecyObject
、DependencyProperty
そのクラスに を作成できます。
たとえば、ValidationRule
バインド可能なプロパティをサポートするカスタム クラスを次に示します。
[ContentProperty("ComparisonValue")]
public class GreaterThanValidationRule : ValidationRule
{
public ComparisonValue ComparisonValue { get; set; }
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
string s = value?.ToString();
int number;
if (!Int32.TryParse(s, out number))
{
return new ValidationResult(false, "Not a valid entry");
}
if (number <= ComparisonValue.Value)
{
return new ValidationResult(false, $"Number should be greater than {ComparisonValue}");
}
return ValidationResult.ValidResult;
}
}
ComparisonValue
DependencyObject
は を継承し、 を持つ単純なクラスですDependencyProperty
。
public class ComparisonValue : DependencyObject
{
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
nameof(Value),
typeof(int),
typeof(ComparisonValue),
new PropertyMetadata(default(int));
これにより、元の問題は解決されますが、残念ながらさらに 2 つの問題が発生します。
はビジュアル ツリーの一部ではないため、バインドされたプロパティを正しく取得できず、バインディングは正しく機能しません
ValidationRules
。たとえば、この単純なアプローチは機能しません。<TextBox Name="TextBoxToValidate"> <TextBox.Text> <Binding Path="ViewModelProperty" UpdateSourceTrigger="PropertyChanged"> <Binding.ValidationRules> <numbers:GreaterThanValidationRule> <numbers:ComparisonValue Value="{Binding Text, ElementName=TextBoxToValidate}"/> </numbers:GreaterThanValidationRule> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox>
代わりに、プロキシオブジェクトを使用する必要があります。これ答え:
<TextBox Name="TextBoxToValidate"> <TextBox.Resources> <bindingExtensions:BindingProxy x:Key="TargetProxy" Data="{Binding Path=Text, ElementName=TextBoxToValidate}"/> </TextBox.Resources> <TextBox.Text> <Binding Path="ViewModelProperty" UpdateSourceTrigger="PropertyChanged"> <Binding.ValidationRules> <numbers:GreaterThanValidationRule> <numbers:ComparisonValue Value="{Binding Data, Source={StaticResource TargetProxy}}"/> </numbers:GreaterThanValidationRule> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox>
BindingProxy
単純なクラスです:public class BindingProxy : Freezable { protected override Freezable CreateInstanceCore() { return new BindingProxy(); } public object Data { get { return GetValue(DataProperty); } set { SetValue(DataProperty, value); } } public static readonly DependencyProperty DataProperty = DependencyProperty.Register(nameof(Data), typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null)); }
カスタムのプロパティ
ValidationRule
が別のオブジェクトのプロパティにバインドされている場合、その他のオブジェクトのプロパティが変更されても、元のプロパティの検証ロジックは実行されません。この問題を解決するには、 のバインドされたプロパティが更新されたときにバインディングを更新する必要があります
ValidationRule
。まず、そのプロパティをComparisonValue
クラスにバインドする必要があります。次に、プロパティが変更されたときにバインディングのソースを更新できますValue
。public class ComparisonValue : DependencyObject { public int Value { get { return (int)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( nameof(Value), typeof(int), typeof(ComparisonValue), new PropertyMetadata(default(int), OnValueChanged)); private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ComparisonValue comparisonValue = (ComparisonValue) d; BindingExpressionBase bindingExpressionBase = BindingOperations.GetBindingExpressionBase(comparisonValue, BindingToTriggerProperty); bindingExpressionBase?.UpdateSource(); } public object BindingToTrigger { get { return GetValue(BindingToTriggerProperty); } set { SetValue(BindingToTriggerProperty, value); } } public static readonly DependencyProperty BindingToTriggerProperty = DependencyProperty.Register( nameof(BindingToTrigger), typeof(object), typeof(ComparisonValue), new FrameworkPropertyMetadata(default(object), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); }
最初のケースと同じプロキシ問題がここでも発生します。そのため、別のプロキシ オブジェクトを作成する必要があります。
<ItemsControl Name="SomeCollection" ItemsSource="{Binding ViewModelCollectionSource}"/> <TextBox Name="TextBoxToValidate"> <TextBox.Resources> <bindingExtensions:BindingProxy x:Key="TargetProxy" Data="{Binding Path=Items.Count, ElementName=SomeCollection}"/> <bindingExtensions:BindingProxy x:Key="SourceProxy" Data="{Binding Path=Text, ElementName=TextBoxToValidate, Mode=TwoWay}"/> </TextBox.Resources> <TextBox.Text> <Binding Path="ViewModelProperty" UpdateSourceTrigger="PropertyChanged"> <Binding.ValidationRules> <numbers:GreaterThanValidationRule> <numbers:ComparisonValue Value="{Binding Data, Source={StaticResource TargetProxy}}" BindingToTrigger="{Binding Data, Source={StaticResource SourceProxy}}"/> </numbers:GreaterThanValidationRule> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox>
この場合、
Text
のプロパティは のプロパティTextBoxToValidate
に対して検証されます。リスト内の項目数が変わると、プロパティの検証がトリガーされます。Items.Count
SomeCollection
Text