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>
除了自己做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工厂来自动包装项目。
同样,以上是从未测试或尝试过的伪代码。