Silverlight: Mvvm, INotifyPropertyChanged и свойства

понедельник, 25 апреля 2011, bobasoft

За последнее время я увидел много постов, расказывающих о том как упростить процес создания свойств которые вызывают событие PropertyChanged. Это и codesnippet'ы и атрибуты для генерации кода... Решил написать о своем варианте который не использует ни то ни другое (намного проще).

И так, стандартный код свойства в MVVM:

1
2
3
4
5
6
7
8
9
10
11
12
13
private string _name;
public string Name
{
    get { return _name; }
    set
    {
        if ( _name != value)
        {
            _name = value;
            RaisePropertyChanged("Name");
        }
    }
}

А теперь если представить что таких полей у вас должно быть 10 - это приведет к тому, что в коде будет очень много лишнего, может даже намного больше чем самой логики... да можно отнести эти все свойства в partial класс, но для каждой модели создавать отдельный partial класс - не рационально (по моему мнению).

В своих проектах я использую следующие решение (я не говорю что это самое лутше/оптимальное решение - но для меня оно самое удобное)

1. Для начала создадим класс DataValue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
     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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 разметки примера:

1
2
3
4
5
6
7
8
9
10
11
12
<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>

Выглядеть оно будет так:

ui

Плюсы и минусы:

  • "+" Чистота кода, все значения храняться в одном месте
  • "+" Возможность добавить дополнительные поля для каждого значения. Например, добавить в класс DataValue такое поле как IsValid.
  • "-" Невозможность контролировать (легким способом) значение которое меняеться в DataValue, только когда будет вызвана какая-то функция в модели.

Скачать проект можно тут.

Спасибо, критика и пожелания приветствуються.


Ищите нас в интернетах!

Комментарии

Свежие вакансии