Silverlight: Mvvm, INotifyPropertyChanged и свойства
За последнее время я увидел много постов, расказывающих о том как упростить процес создания свойств которые вызывают событие PropertyChanged. Это и codesnippet'ы и атрибуты для генерации кода... Решил написать о своем варианте который не использует ни то ни другое (намного проще).
И так, стандартный код свойства в MVVM:
private string _name; public string Name { get { return _name; } set { if ( _name != value) { _name = value; RaisePropertyChanged("Name"); } } }
А теперь если представить что таких полей у вас должно быть 10 - это приведет к тому, что в коде будет очень много лишнего, может даже намного больше чем самой логики... да можно отнести эти все свойства в partial класс, но для каждой модели создавать отдельный partial класс - не рационально (по моему мнению).
В своих проектах я использую следующие решение (я не говорю что это самое лутше/оптимальное решение - но для меня оно самое удобное)
1. Для начала создадим класс DataValue:
public class DataValue : INotifyPropertyChanged { protected object _value; public DataValue(object value) { _value = value; } public object Value { get { return _value; } set { if ( _value != value) { _value = value; RaisePropertyChanged("Value"); } } } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) { var handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } }
Думаю это код понятен....
2. Создадим безовый класс для модели - ModelBase:
public class ModelBase { public ModelBase() { Values = new Dictionary<string, DataValue>(10, StringComparer.OrdinalIgnoreCase); } public Dictionary<string, DataValue> Values { get; private set; } protected void SetValue<T>(string key, T value) { DataValue dataValue; if (!Values.TryGetValue(key, out dataValue)) Values.Add(key, new DataValue(value)); else dataValue.Value = value; } protected T GetValue<T>(string key) { return (T) Values[key].Value; } }
Словарь Values - будет хранить все наши значения в одном месте, доступ к определенному значению будет воспроизводиться по строковому ключу. Так как этот словарь не typesafe, создали два метода SetValue и GetValue для простоты. Также обратите внимание при создании словаря, мы указываем StringComparer.OrdinalIgnoreCase - поэтому нам не придеться задумываться о регистре ключа значения.
3. Создаем модель для нашей главной странице - MainPage
public class MainPageModel : ModelBase { internal const string VALUENAME = "name"; internal const string VALUEAGE = "age"; public MainPageModel() { Initialize(); } private void Initialize() { SetValue(VALUENAME, "Boris"); SetValue(VALUEAGE, 19); } }
Модель MainPageModel наследуеться от ранее созданого класса ModelBase.
Создали две константы - ключи значений. Обязательно нижно проинициализировать значения во время создания класса, так как если это сделать пожже, в UI они не подхватяться, так как словарь Values это не ObservableDictionary (стандартного нету).
4. Xaml - MainPage.xaml
Биндинг осуществляется так:
- Только для чтения - {Binding Values[value_key].Value}
- Для чтения и записи - {Binding Values[value_key].Value, Mode=TwoWay}
Основная часть xaml разметки примера:
<StackPanel VerticalAlignment="Top" Orientation="Horizontal" Margin="96,125,84,0" Background="White"> <TextBlock Text="{Binding Values[name].Value}" Width="150" Margin="2"/> <TextBlock Text=", " Margin="2"/> <TextBlock Text="{Binding Values[age].Value}" Width="50" Margin="2"/> </StackPanel> <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" Margin="77,0,61,112"> <TextBlock Text="name:"/> <TextBox Text="{Binding Values[name].Value, Mode=TwoWay}" Width="150"/> <TextBlock Text="age:"/> <TextBox Text="{Binding Values[age].Value, Mode=TwoWay}" Width="50"/> </StackPanel>
Выглядеть оно будет так:
Плюсы и минусы:
- "+" Чистота кода, все значения храняться в одном месте
- "+" Возможность добавить дополнительные поля для каждого значения. Например, добавить в класс DataValue такое поле как IsValid.
- "-" Невозможность контролировать (легким способом) значение которое меняеться в DataValue, только когда будет вызвана какая-то функция в модели.
Скачать проект можно тут.
Спасибо, критика и пожелания приветствуються.