我正在Silverlight2中開發一個應用程序並嘗試遵循Model-View-ViewModel模式。我將一些控件上的IsEnabled屬性綁定到ViewModel上的布爾屬性。PropertyChanged計算屬性的通知
我遇到了這些屬性從其他屬性派生出來的問題。比方說,我有一個保存按鈕,我只希望在可以保存時啓用(數據已加載,而且我們目前不忙於在數據庫中執行任何操作)。
所以我有這樣幾個屬性:
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; }
}
但需要注意的缺乏屬性更改通知。
所以,問題是:什麼是暴露我可以綁定到一個布爾值屬性的一個乾淨的方式,而是計算被明確設置,並提供通知,以便用戶界面能正確地更新?
編輯:感謝大家的幫助 - 我得到了它,並去了一個自定義的屬性。如果有人感興趣,我會在此發佈源代碼。我相信它可以以更清晰的方式完成,所以如果您發現任何缺陷,請添加評論或答案。
基本上我所做的就是作出定義鍵 - 值對的列表,持什麼樣的性質取決於其他屬性的接口:
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。(是的,這將導致事件射擊那裏他們沒有需要)
這將正常工作,但它意味着你將被無效CanSave超過必要的程度,可能會使應用程序的其他部分執行比所需更多的工作。 – KeithMahoney 2009-02-27 02:33:53
如果我沒有訪問該屬性的設置方法,該怎麼辦? – geofftnz 2009-02-27 02:34:23