Очередной
вопрос на MSDN. Стоит задача при редактировании записи сначала проверить данные, а только потом применить изменения к объекту модели. Я всю конструкцию MVVM воспроизводить не буду и покажу на примере в котором будет только один объект модели, а все остальное будет в лоб. Начнем.
Создаем пустой проект и добавляем в него два класса. Первый класс для модели:
class Person : DependencyObject
{ public string LastName { get { return (string)GetValue(LastNameProperty); } set { SetValue(LastNameProperty, value); } } // Using a DependencyProperty as the backing store for LastName. This enables animation, styling, binding, etc... public static readonly DependencyProperty LastNameProperty = DependencyProperty.Register("LastName", typeof(string), typeof(Person), new PropertyMetadata("")); public string FirstName { get { return (string)GetValue(FirstNameProperty); } set { SetValue(FirstNameProperty, value); } } // Using a DependencyProperty as the backing store for FirstName. This enables animation, styling, binding, etc... public static readonly DependencyProperty FirstNameProperty = DependencyProperty.Register("FirstName", typeof(string), typeof(Person), new PropertyMetadata("")); } Для проверки добавляем класс чекающий строку на пустоту:
class NotEmptyValidation : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
var result = new ValidationResult(false, "Не допустима пустая строка");
if (value is string && !string.IsNullOrWhiteSpace(value.ToString()))
{
result = new ValidationResult(true, null);
}
return result;
}
}
Разметка формы:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Text="{Binding Person.LastName}" />
<TextBox Text="{Binding Person.FirstName}" Grid.Row="1" />
<TextBox Grid.Column="2" x:Name="tbLastName">
<TextBox.Text>
<Binding Path="Person.LastName" UpdateSourceTrigger="Explicit">
<Binding.ValidationRules>
<local:NotEmptyValidation />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBox Grid.Row="1" Grid.Column="2" x:Name="tbFirstName">
<TextBox.Text>
<Binding Path="Person.FirstName" UpdateSourceTrigger="Explicit">
<Binding.ValidationRules>
<local:NotEmptyValidation />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<Button Content="Принять" Click="Button_Click" Grid.Column="2" Grid.Row="2" />
</Grid>
</Window>
Код формы:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
public Person Person
{
get { return (Person)GetValue(PersonProperty); }
set { SetValue(PersonProperty, value); }
}
// Using a DependencyProperty as the backing store for Person. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PersonProperty =
DependencyProperty.Register("Person", typeof(Person), typeof(MainWindow), new PropertyMetadata(null));
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Person = new Person() { LastName = "Иванов" };
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
BindingExpression beLastName = tbLastName.GetBindingExpression(TextBox.TextProperty);
BindingExpression beFirstName = tbFirstName.GetBindingExpression(TextBox.TextProperty);
if (beLastName.ValidateWithoutUpdate() && beFirstName.ValidateWithoutUpdate())
{
beLastName.UpdateSource();
beFirstName.UpdateSource();
// Вот здесь можно закрывать View, не забыв уведомить ViewModel
}
}
}
Несколько комментариев:
1. Не забываем на форме подключить пространство имен где описан класс валидатора:
xmlns:local="clr-namespace:WpfApplication3"2. Для того, чтобы Binding не срабатывал сам, а только по кнопке, прописываем в нем:
UpdateSourceTrigger="Explicit"3. Перед принудительным применением Binding проверяем, а все ли нормально:
if (beLastName.ValidateWithoutUpdate() && beFirstName.ValidateWithoutUpdate())
4. Работает вот так. Запускаем:
Вводим слева значение и переводим фокус ввода, значение отображается справа:
Теперь вводим значение в правый TextBox и меняем фокус ввода:
Как видим, значение слева не обновилось. Нажимаем Принять:
Значение обновилось. Теперь значение меняем на некорректное (пустую строку) и нажимаем принять:
Видим подсветку ошибки и то, что в модель пустое значение не скопировалось.