Очередной вопрос на MSDN. Стоит задача при редактировании записи сначала проверить данные, а только потом применить изменения к объекту модели. Я всю конструкцию MVVM воспроизводить не буду и покажу на примере в котором будет только один объект модели, а все остальное будет в лоб. Начнем.
Создаем пустой проект и добавляем в него два класса. Первый класс для модели:
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(""));
}
Для проверки добавляем класс чекающий строку на пустоту:
1. Не забываем на форме подключить пространство имен где описан класс валидатора:
xmlns:local="clr-namespace:WpfApplication3"
2. Для того, чтобы Binding не срабатывал сам, а только по кнопке, прописываем в нем:
UpdateSourceTrigger="Explicit"
3. Перед принудительным применением Binding проверяем, а все ли нормально:
if (beLastName.ValidateWithoutUpdate() && beFirstName.ValidateWithoutUpdate())
4. Работает вот так. Запускаем:
Вводим слева значение и переводим фокус ввода, значение отображается справа:
Теперь вводим значение в правый TextBox и меняем фокус ввода:
Как видим, значение слева не обновилось. Нажимаем Принять:
Значение обновилось. Теперь значение меняем на некорректное (пустую строку) и нажимаем принять:
Видим подсветку ошибки и то, что в модель пустое значение не скопировалось.
Создаем пустой проект и добавляем в него два класса. Первый класс для модели:
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 и меняем фокус ввода:
Как видим, значение слева не обновилось. Нажимаем Принять:
Значение обновилось. Теперь значение меняем на некорректное (пустую строку) и нажимаем принять:
Видим подсветку ошибки и то, что в модель пустое значение не скопировалось.
А почему бы просто не реализовать INotifyPropertyChanged вместо DependencyObject. код получится проще, а результат, насколько я понимаю, тот же. Здесь http://channel9.msdn.com/Shows/Visual-Studio-Toolbox/MVVM-Best-Practices подобную реализацию называют антипаттерном.
ОтветитьУдалитьДобрый день.
УдалитьУ INotifyPropertyChanged есть ряд преимуществ, например, если ваш класс уже имеет предков, то альтернатив нет, вы будите использовать именно его. Также, использование INotifyPropertyChanged немного экономит память. Но вот максимальное быстродействие достигается при использовании DependencyObject. Смотрите табличку сравнения здесь: http://msdn.microsoft.com/ru-ru/library/vstudio/bb613546(v=vs.100).aspx
Поэтому, я не очень понимаю, в чем заключается антипаттерн?
Этот комментарий был удален автором.
ОтветитьУдалить