INotifyPropertyChanged wrapper

本文关键字:wrapper INotifyPropertyChanged | 更新日期: 2023-09-27 18:12:11

我找到了INotifyPropertyChanged包装的一些解决方案,但它不做任何事情。我做错了什么?名称异步更新,但windows中的值不变。为什么?* *

namespace WpfApplication1.ViewModel
{
class CustomerViewModel : INotifyPropertyChanged, IWeakEventListener
{
    private readonly Customer _customer;
    internal CustomerViewModel(Customer customer)
    {
        if (customer == null)
        {
            throw new ArgumentNullException("personModel");
        }
        _customer = customer;
        NotifyPropertyChangedEventManager.AddListener(_customer, this);
        Action Start = new Action(UpdateAsync);
        IAsyncResult result = Start.BeginInvoke(null, null);
    }
    private void UpdateAsync()
    {
        int i = 0;
        while (true)
        {
            System.Threading.Thread.Sleep(1000);
            _customer.Name = (++i).ToString();
        }
    }
    public string Name
    {
        get { return _customer.Name; }
        set { _customer.Name = value; }
    }
    public string JobTitle
    {
        get { return _customer.Work; }
        set { _customer.Work = value; }
    }
    #region INotifyPropertyChanged Members
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
    #region IWeakEventListener Members
    public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
    {
        PropertyChangedEventArgs pcArgs = e as PropertyChangedEventArgs;
        if (pcArgs != null)
        {
            OnPropertyChanged(pcArgs.PropertyName);
            return true;
        }
        return false;
    }
    #endregion
}
public class NotifyPropertyChangedEventManager : WeakEventManager
{
    public static NotifyPropertyChangedEventManager CurrentManager
    {
        get
        {
            var manager_type = typeof(NotifyPropertyChangedEventManager);
            var manager = WeakEventManager.GetCurrentManager(manager_type) as NotifyPropertyChangedEventManager;
            if (manager == null)
            {
                manager = new NotifyPropertyChangedEventManager();
                WeakEventManager.SetCurrentManager(manager_type, manager);
            }
            return manager;
        }
    }
    public static void AddListener(INotifyPropertyChanged source, IWeakEventListener listener)
    {
        CurrentManager.ProtectedAddListener(source, listener);
        return;
    }
    public static void RemoveListener(INotifyPropertyChanged source, IWeakEventListener listener)
    {
        CurrentManager.ProtectedRemoveListener(source, listener);
        return;
    }
    protected override void StartListening(object source)
    {
        ((INotifyPropertyChanged)source).PropertyChanged += Source_PropertyChanged; return;
    }
    protected override void StopListening(object source)
    {
        ((INotifyPropertyChanged)source).PropertyChanged -= Source_PropertyChanged;
        return;
    }
    void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        { CurrentManager.DeliverEvent(sender, e); };
    }
}

}

客户

    public class Customer:INotifyPropertyChanged
{
    public string Name { get; set; }
    public string Number { get; set; }
    public string Work { get; set; }
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

和Xaml代码

<Grid>
    <Label Content="{Binding Name}"></Label>
</Grid>

INotifyPropertyChanged wrapper

除了自己做PropertyChanged之外,您还可以使用Fody.PropertyChanged。(https://github.com/Fody/PropertyChanged)

你可以在Visual Studio中通过Nuget安装它。

它是如何在编译时自动添加PropertyChanged实现的?

代码:

using PropertyChanged;
[ImplementPropertyChanged]
public class Person 
{        
    public string GivenNames { get; set; }
    public string FamilyName { get; set; }
    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }
}

编译的内容:

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    string givenNames;
    public string GivenNames
    {
        get { return givenNames; }
        set
        {
            if (value != givenNames)
            {
                givenNames = value;
                OnPropertyChanged("GivenNames");
                OnPropertyChanged("FullName");
            }
        }
    }
    string familyName;
    public string FamilyName
    {
        get { return familyName; }
        set 
        {
            if (value != familyName)
            {
                familyName = value;
                OnPropertyChanged("FamilyName");
                OnPropertyChanged("FullName");
            }
        }
    }
    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }
    public virtual void OnPropertyChanged(string propertyName)
    {
        var propertyChanged = PropertyChanged;
        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

还有其他功能,请在wiki上阅读:https://github.com/Fody/PropertyChanged/wiki

我知道这个问题很老了,并没有完全回答你的问题。我想这对你来说是一个更好的解决方案。

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); } }
}

您必须调用OnPropertyChanged("Name");最好的地方是Name属性setter。

实现INotifyPropertyChanged的非常方便的方法是在您的类中实现以下方法:

protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
    if (EqualityComparer<T>.Default.Equals(storage, value))
        return false;
    storage = value;
    this.OnPropertyChanged(propertyName);
    return true;
}

然后像这样使用:

private string _foo;
public string Foo 
{
    get { return this._foo; }
    set { SetProperty(ref this._foo, value); }
}

然而,因为你正在制作其他类的包装器,你不能在你的CustomerViewModel类中使用对属性的引用。这是可以解决的,但它将导致编写大量的代码,只是为了正确实现INotifyPropertyChanged

不要把你的Customer类包装成其他的,而是把它变成一个公共属性:

private Customer _customer;
public Customer Customer 
{
    get { return this._customer; }
    private set { SetProperty(ref this._customer, value); }
}

在XAML中:

<Grid>
    <Label Content="{Binding Customer.Name}"></Label>
</Grid>

除了上面的好答案之外,您可能还可以使用DynamicObject包装器实现这种行为,包装被访问的类,并代表它更改其属性,触发属性更改事件。

我还没有尝试过(我可能会去Fody),但是像这样的pseudo可能会起作用:

public class InpcWrapperViewModel<T> : DynamicObject, INotifyPropertyChanged
{
    public T Original { get; }
    public InpcWrapperViewModel(T original)
    {
      Original = original;
    }
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
      //set property of Original with reflection etc.
      //raise property changed 
    }
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        //set with reflection etc.
        return true;
    }
}

用法:

dynamic x = new InpcWrapperViewModel<TEntity>(entity);
window.DataContext = x;
x.FirstName = "Hello"; //triggers INPC

如果你使用Prism或其他DI等,你可以创建一个ViewModel工厂来自动包装项目。

同样,以上是从未测试或尝试过的伪代码。