一、Data Binding
Data Binding在MVVM模式中起到了重要的作用,WPF提供了強大的數據綁定功能,因此在設計view和model時應該充分利用這些能力,這意味着你必須實現正確的接口。
1)WPF支持one-way binding和two-way binding,two-way binding會將用戶對界面數據的修改自動更新到底層數據對象。
2)爲了將view model或者model中的數據更新通知到view,需要實現INotifyPropertyChanged接口或者INotifyCollectionChanged接口(如果model是一個集合)。
3)此外,ICollectionView接口在view和view model/model底層集合對象之間提供了排序、過濾、分組以及選擇元素的跟蹤操作。WPF的ListCollectionView實現了ICollectionView接口。
二、Commands
在WPF中,用戶通過UI進行的操作被定義爲Commands。Commands爲操作和UI上的控件進行綁定提供了一種便利的方式。
WPF的一些控件提供了Command屬性,這個屬性可以綁定到viewModel中實現了ICommand接口的對象,例如:
public class QuestionnaireViewModel
{
public QuestionnaireViewModel()
{
this.SubmitCommand = new DelegateCommand<object>(
this.OnSubmit, this.CanSubmit );
}
public ICommand SubmitCommand { get; private set; }
private void OnSubmit(object arg) {...}
private bool CanSubmit(object arg) { return true; }
}
<Button Command="{Binding Path=SubmitCommand}" CommandParameter="SubmitOrder"/>
<Button Content="Submit" IsEnabled="{Binding CanSubmit}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding SubmitCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
三、數據驗證
在MVVM模式中,數據驗證可以通過在view model / model實現IDataErrorInfo接口和INotifyDataErrorInfo接口來實現,這些接口允許view model / model對一個或多個屬性進行數據驗證,並且向view返回errir message。
IDataErrorInfo接口提供了基本的數據驗證和錯誤報告的功能,它包含兩個屬性,一個索引器屬性(索引器使用屬性名作爲參數)和一個Error屬性。索引器屬性根據傳遞的屬性名返回錯誤消息,如果返回值爲Empty或者null,表示屬性change合法,Error屬性爲整個對象提供error message,但是現在的WPF和Silverlight引擎都沒有使用這個屬性。
IDataError的索引器屬性將會在第一次數據綁定或者屬性的每次改變被調用,因爲索引器屬性在每個屬性發生改變的時候都會被調用,所以要保證這個數據驗證函數要儘可能的高效。
<TextBox
Text="{Binding Path=CurrentEmployee.Name, Mode=TwoWay, ValidatesOnDataErrors=True,
NotifyOnValidationError=True }"
/>
public class Agent : NotificationObject, IDataErrorInfo
{
/// <summary>
/// Name of Agent
/// </summary>
[DisplayOrder(1)]
[DisplayName("Agent Name")]
public string DisplayName
{
get
{
return displayName;
}
set
{
if (displayName != value)
{
displayName = value;
this.isSelfChanged = true;
RaisePropertyChanged<string>(() => this.DisplayName);
RaisePropertyChanged<string>(() => this.Error);
}
}
}
/// <summary>
/// the address client can connect to the agent
/// </summary>
[DisplayOrder(2)]
[DisplayName("EndPoint Address")]
public string Address
{
get
{
return address;
}
set
{
address = value;
this.isSelfChanged = true;
RaisePropertyChanged<string>(() => this.Address);
RaisePropertyChanged<string>(() => this.Error);
}
}
#region IDataErrorInfo Members
string IDataErrorInfo.Error
{
get { throw new NotImplementedException(); }
}
string IDataErrorInfo.this[string columnName]
{
get
{
if (!string.IsNullOrEmpty(columnName))
{
Error = string.IsNullOrEmpty(Error) ? Error : (Error + Environment.NewLine);
switch (columnName)
{
case "DisplayName":
if (string.IsNullOrEmpty(DisplayName))
return "Display name is required.";
case "Address":
if (string.IsNullOrEmpty(Address))
return "Address is required.";
default:
return string.Empty;
}
}
return string.Empty;
}
}
#endregion
}
INotifyDataErrorInfo接口比IDataErrorInfo更靈活,它支持一個屬性的多個錯誤描述,異步數據驗證以及在錯誤狀態改變時通知view的功能。然而,INotifyDataError當前只在Silverlight4中支持,WPF4尚不支持。public abstract class DomainObject : INotifyPropertyChanged,
INotifyDataErrorInfo
{
private ErrorsContainer<ValidationResult> errorsContainer =
new ErrorsContainer<ValidationResult>(
pn => this.RaiseErrorsChanged( pn ) );
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public bool HasErrors
{
get { return this.ErrorsContainer.HasErrors; }
}
public IEnumerable GetErrors( string propertyName )
{
return this.errorsContainer.GetErrors( propertyName );
}
protected void RaiseErrorsChanged( string propertyName )
{
var handler = this.ErrorsChanged;
if (handler != null)
{
handler(this, new DataErrorsChangedEventArgs(propertyName) );
}
}
...
}