Эта статья демонстрирует, как можно создать правило для проверки данных, заданных через Binding. В статье так же рассматривается проблема передачи дополнительных параметров в метод проверки.
Предположим, что мы хотим создать простое приложение, которое позволяет нам выбрать некоторое число с помощью Slider, затем ввести некоторое другое число в TextBox.
Приложение не должно допускать ввод числа не кратного тому, что выбрано с помощью Slider’а.
Пользовательский интерфейс может быть похож на этот:
2 числа, введенные пользователем, будут храниться в классе DivisionNumbers, который реализует следующий интерфейс:
interface IDivisionNumbers : INotifyPropertyChanged
{
int Dividend { get; set; }
int Divisor { get; set; }
}
В конструкторе класса MainWindow сконфигурируем объект данного класса и установим свойство DataContext для того, чтобы можно было использовать Binding для элементов UI.
public MainWindow()
{
InitializeComponent();
var nums = new DivisionNumbers();
nums.Divisor = 1;
nums.Dividend = 1;
this.DataContext = nums;
}
Логику проверки данных, введенных в TextBox, вынесем в подкласс ValidationRule. Объект данного класса добавим в свойство ValidationRules ассоциированного с TextBox’ом Binding’а. Когда пользователь вводит некоторое число в текстовом поле, наше правило будет его проверять на кратность заданному с помощью Slider’а числу.<br />Вот XAML описывающий вышесказанное:
<TextBox>
<TextBox.Text>
<Binding Path="Dividend" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:IsMultipleOfValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Незавершенная реализация нашего правила может выглядеть так:
public class IsMultipleOfValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
try
{
int dividend = (int)Convert.ChangeType(value, typeof(int));
int divisor = // Каким-то образом получаем делитель...
if (dividend % divisor == 0)
return ValidationResult.ValidResult;
return new ValidationResult(
false,
"The number is not a multiple of " + divisor);
}
catch
{
return new ValidationResult( false, "Not a number." );
}
}
}
И тут проявляется следующая проблема. Как получить значение делителя, которое выбрал пользователь, в методе Validate?
Мы не можем добавить свойство Divisor в класс IsMultipleOfValidationRule и использовать для него Binding, так как класс ValidationRule не является подклассом класса DependencyObject.
Каким образом мы можем преодолеть эту, казалось бы, невозможную из-за технического барьера, цель наиболее элегантно?
Добавим следующий фрагмент XAML кода в описание нашего окна:
<Window.Resources>
<FrameworkElement x:Key="DataContextBridge" />
</Window.Resources>
<Window.DataContext>
<Binding Mode="OneWayToSource" Path="DataContext" Source="{StaticResource DataContextBridge}" />
</Window.DataContext>
Таким образом, мы создали некоторый виртуальный элемент DataContextBridge, к которому будет привязан DataContext нашего окна.
Создадим новый класс, который будет хранить некоторое число:
public class IntegerContainer : FrameworkElement
{
public int Value
{
get { return (int)GetValue( ValueProperty ); }
set { SetValue( ValueProperty, value ); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
"Value",
typeof( int ),
typeof( IntegerContainer ),
new UIPropertyMetadata( 0 ) );
}
Теперь обновим наше правило для проверки данных с использованием IntegerContainer:
public class IsMultipleOfValidationRule : ValidationRule
{
public IntegerContainer DivisorContainer { get; set; }
public override ValidationResult Validate(
object value, CultureInfo cultureInfo )
{
try
{
int dividend = (int)Convert.ChangeType( value, typeof( int ) );
int divisor = this.DivisorContainer.Value;
if( dividend % divisor == 0 )
return ValidationResult.ValidResult;
return new ValidationResult(
false,
"The number is not a multiple of " + divisor );
}
catch
{
return new ValidationResult( false, "Not a number." );
}
}
}
Теперь осталось только настроить наше правило в XAML представлении.
<TextBox>
<TextBox.Text>
<Binding Path="Dividend" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:IsMultipleOfValidationRule>
<local:IsMultipleOfValidationRule.DivisorContainer>
<!-- This IntegerContainer is the "child node" of
the DataContextBridge element, in the virtual
branch attached to the Window's logical tree. -->
<local:IntegerContainer
DataContext="{Binding Source={StaticResource DataContextBridge}, Path=DataContext}"
Value="{Binding Divisor}" />
</local:IsMultipleOfValidationRule.DivisorContainer>
</local:IsMultipleOfValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Готово!