您的位置:首页 > 其它

Prism应用开发(五)——MVVM模式关键技术

2016-08-12 09:13 621 查看
一、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接口的对象,例如:

[csharp]
view plain
copy

print?

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



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


[html]
view plain
copy

print?

<Button Command="{Binding Path=SubmitCommand}" CommandParameter="SubmitOrder"/>  



<Button Command="{Binding Path=SubmitCommand}" CommandParameter="SubmitOrder"/>


[html]
view plain
copy

print?

<Button Content="Submit" IsEnabled="{Binding CanSubmit}">  
<i:Interaction.Triggers>  
<i:EventTrigger EventName="Click">  
<i:InvokeCommandAction Command="{Binding SubmitCommand}"/>  
</i:EventTrigger>  
</i:Interaction.Triggers>  
</Button>  



<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的索引器属性将会在第一次数据绑定或者属性的每次改变被调用,因为索引器属性在每个属性发生改变的时候都会被调用,所以要保证这个数据验证函数要尽可能的高效。

[html]
view plain
copy

print?

<TextBox  
Text="{Binding Path=CurrentEmployee.Name, Mode=TwoWay, ValidatesOnDataErrors=True,  
NotifyOnValidationError=True }"  
/>  



<TextBox
Text="{Binding Path=CurrentEmployee.Name, Mode=TwoWay, ValidatesOnDataErrors=True,
NotifyOnValidationError=True }"
/>


[csharp]
view plain
copy

print?

 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       
    }   



 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尚不支持。

[csharp]
view plain
copy

print?

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) );  
}  
}  
...  
}  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: