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

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

За последнее время я увидел много постов, расказывающих о том как упростить процес создания свойств которые вызывают событие 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>

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

ui

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

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

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

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


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

Комментарии

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