当这些属性从其他属性派生时,我遇到了问题.假设我有一个Save按钮,我只想在可以保存的情况下启用它(数据已加载,我们当前并没有忙于在数据库中执行操作).
private bool m_DatabaseBusy; public bool DatabaseBusy { get { return m_DatabaseBusy; } set { if (m_DatabaseBusy != value) { m_DatabaseBusy = value; OnPropertyChanged("DatabaseBusy"); } } } private bool m_IsLoaded; public bool IsLoaded { get { return m_IsLoaded; } set { if (m_IsLoaded != value) { m_IsLoaded = value; OnPropertyChanged("IsLoaded"); } } }
现在我想要做的是:
public bool CanSave { get { return this.IsLoaded && !this.DatabaseBusy; } }
所以问题是:什么是暴露我可以绑定的单个布尔属性的干净方式,但是计算而不是显式设置并提供通知,以便UI可以正确更新?
编辑:感谢大家的帮助 – 我得到了它,并开始制作自定义属性.我在这里发布消息来源以防任何人感兴趣.我确信它可以以更清洁的方式完成,所以如果您发现任何缺陷,请添加评论或答案.
基本上我所做的是创建了一个接口,它定义了一个键值对列表,用于保存依赖于其他属性的属性:
public interface INotifyDependentPropertyChanged { // key,value = parent_property_name,child_property_name,where child depends on parent. List<keyvaluePair<string,string>> DependentPropertyList{get;} }
[AttributeUsage(AttributeTargets.Property,AllowMultiple = true,Inherited = false)] public class NotifyDependsOnAttribute : Attribute { public string DependsOn { get; set; } public NotifyDependsOnAttribute(string dependsOn) { this.DependsOn = dependsOn; } public static void BuildDependentPropertyList(object obj) { if (obj == null) { throw new ArgumentNullException("obj"); } var obj_interface = (obj as INotifyDependentPropertyChanged); if (obj_interface == null) { throw new Exception(string.Format("Type {0} does not implement INotifyDependentPropertyChanged.",obj.GetType().Name)); } obj_interface.DependentPropertyList.Clear(); // Build the list of dependent properties. foreach (var property in obj.GetType().GetProperties()) { // Find all of our attributes (may be multiple). var attributeArray = (NotifyDependsOnAttribute[])property.GetCustomAttributes(typeof(NotifyDependsOnAttribute),false); foreach (var attribute in attributeArray) { obj_interface.DependentPropertyList.Add(new keyvaluePair<string,string>(attribute.DependsOn,property.Name)); } } } }
属性本身仅存储单个字符串.您可以为每个属性定义多个依赖项.属性的内容位于BuildDependentPropertyList静态函数中.你必须在你的类的构造函数中调用它. (任何人都知道是否有办法通过类/构造函数属性执行此操作?)在我的情况下,所有这些都隐藏在基类中,因此在子类中,您只需将属性放在属性上.然后修改OnPropertyChanged等效项以查找任何依赖项.这是我的viewmodel基类作为示例:
public class viewmodel : INotifyPropertyChanged,INotifyDependentPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyname) { if (PropertyChanged != null) { PropertyChanged(this,new PropertyChangedEventArgs(propertyname)); // fire for dependent properties foreach (var p in this.DependentPropertyList.Where((x) => x.Key.Equals(propertyname))) { PropertyChanged(this,new PropertyChangedEventArgs(p.Value)); } } } private List<keyvaluePair<string,string>> m_DependentPropertyList = new List<keyvaluePair<string,string>>(); public List<keyvaluePair<string,string>> DependentPropertyList { get { return m_DependentPropertyList; } } public viewmodel() { NotifyDependsOnAttribute.BuildDependentPropertyList(this); } }
最后,在受影响的属性上设置属性.我喜欢这种方式,因为派生属性包含它依赖的属性,而不是相反.
[NotifyDependsOn("Session")] [NotifyDependsOn("DatabaseBusy")] public bool SaveEnabled { get { return !this.Session.IsLocked && !this.DatabaseBusy; } }
这里最大的警告是它只在其他属性是当前类的成员时才有效.在上面的示例中,如果this.Session.IsLocked发生更改,则通知无法通过.我解决这个问题的方法是订阅this.Session.NotifyPropertyChanged并为“Session”触发PropertyChanged. (是的,这会导致事件发生在他们不需要的地方)
解决方法
public bool IsLoaded { get { return m_IsLoaded; } set { if (m_IsLoaded != value) { m_IsLoaded = value; OnPropertyChanged("IsLoaded"); OnPropertyChanged("CanSave"); } } }
这可能会有点混乱(例如,如果您在CanSave中的计算更改).
一个(更干净?我不知道)解决这个问题的方法是覆盖OnPropertyChanged并在那里进行调用:
protected override void OnPropertyChanged(string propertyName) { base.OnPropertyChanged(propertyName); if (propertyName == "IsLoaded" /* || propertyName == etc */) { base.OnPropertyChanged("CanSave"); } }
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。