Как реализовать INotifyPropertyChanged в С# 6.0?
Ответ на этот вопрос был отредактирован, чтобы сказать, что в С# 6.0, INotifyPropertyChanged может быть реализована со следующей процедурой OnPropertyChanged:
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Однако из этого ответа неясно, каково должно быть соответствующее определение свойства. Что представляет собой полная реализация INotifyPropertyChanged в С# 6.0 при использовании этой конструкции?
Ответы
Ответ 1
После включения различных изменений код будет выглядеть следующим образом. Я выделил с комментариями те части, которые изменились и как каждый из них помогает
public class Data : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
//C# 6 null-safe operator. No need to check for event listeners
//If there are no listeners, this will be a noop
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// C# 5 - CallMemberName means we don't need to pass the property name
protected bool SetField<T>(ref T field, T value,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
private string name;
public string Name
{
get { return name; }
//C# 5 no need to pass the property name anymore
set { SetField(ref name, value); }
}
}
Ответ 2
Я использую ту же логику в своем проекте. У меня есть базовый класс для всех моделей просмотров в моем приложении:
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class PropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Каждая модель просмотра наследуется от этого класса. Теперь, в настройщике каждого свойства, мне просто нужно вызвать OnPropertyChanged()
.
public class EveryViewModel : PropertyChangedBase
{
private bool initialized;
public bool Initialized
{
get
{
return initialized;
}
set
{
if (initialized != value)
{
initialized = value;
OnPropertyChanged();
}
}
}
Почему это работает?
[CallerMemberName]
автоматически заполняется компилятором с именем члена, который вызывает эту функцию. Когда мы вызываем OnPropertyChanged
из Initialized
, компилятор ставит nameof(Initialized)
в качестве параметра OnPropertyChanged
Еще одна важная деталь, о которой нужно помнить
Для фреймворка требуется, чтобы PropertyChanged
и все свойства, к которым вы привязываетесь, являются public
.
Ответ 3
Я знаю, что этот вопрос старый, но вот моя реализация
Bindable использует словарь как хранилище свойств. Достаточно легко добавить необходимые перегрузки для подкласса для управления собственным полем поддержки с использованием параметров ref.
- Никакая магическая строка
- Отсутствие отражения
- Может быть улучшено для подавления поиска по умолчанию по умолчанию
Код:
public class Bindable : INotifyPropertyChanged
{
private Dictionary<string, object> _properties = new Dictionary<string, object>();
/// <summary>
/// Gets the value of a property
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <returns></returns>
protected T Get<T>([CallerMemberName] string name = null)
{
object value = null;
if (_properties.TryGetValue(name, out value))
return value == null ? default(T) : (T)value;
return default(T);
}
/// <summary>
/// Sets the value of a property
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="name"></param>
protected void Set<T>(T value, [CallerMemberName] string name = null)
{
if (Equals(value, Get<T>(name)))
return;
_properties[name] = value;
OnPropertyChanged(name);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
используется как
public class Item : Bindable
{
public Guid Id { get { return Get<Guid>(); } set { Set<Guid>(value); } }
}