zoukankan      html  css  js  c++  java
  • SilverLight4:在MVVM架构下实现模式窗口

      要在MVVM架构下实现模式窗口(Modal Dialogs),首先,我们需要实现怎么显示模式窗口。幸运的是,不管是SilverLight3还是SilverLight4都提供了ChildWindow。当然,我们也可以使用第三方控件来实现。但是最重要的问题是怎么在MVVM架构中去实现模式窗口,即怎么在ViewMode中实现,同时要实现View层和ViewModel的松耦性,另外依照MVVM架构思想,ViewModel层不必知道View的样式,所以我们必须需要一个接口,例如下图

    从上图可以知道必须定义一个属性DialogResult,其作用就是确定View层上确定或者取消按钮是否被点击。还必须定义两个方法:Close和ShowDialog,两个方法的作用是关闭和显示模式窗口。当模式窗口被关闭时,还需要一个事件Closed来处理后续的操作。最后,还需要一个属性Content,这个属性非常的特殊,为什么这样说呢?可以有两种方法来实现。

    第一种方法,我可以把属性Content与属性DataContext交换数据。或者换句话说,我们必须为每个实体创建一个模式窗口(Child Window),然后我们仅需要把目标实体赋值给模式窗口的属性DataContext即可,如果下图

      第二个方法。我们需要做的事情比较多,首先把每个实体替换成单独的Child Window,你可以使用UserControls显示实体信息,然后把只要把UserControl赋值给模式窗口的Content即可。具体过程看下图

      

      在这个例子中,我将使用第二种方法实现在MVVM架构中显示模式窗口。在接口IModalView中定义一个UserControl的属性DataContext,如下图:

      最后,定义一个接口 IModalDialogWorker,此接口只提供了一个方法---显示模式窗口的方法。如下图

      从上图你可能发现,当显示一个模式窗口时,必须得传递的参数有接口IModalDialog、接口IModalView、DataContext(可以是任意实体,比如:Person、Customer等),最后,还需要传递一个Action,当模式窗口被关闭时它被执行。

      ModalDialogWorker 的实现非常的简单,仅仅是几行代码,如下:

    代码
    public class ModalDialogWorker : IModalDialogWorker{    
    public void ShowDialog<T>

    IModalDialog modalDialog, 
    IModalView modalView, 
    T dataContext, 
    Action
    <T> onClosed )   
     {        
    if ( modalDialog == null )            
    throw new ArgumentNullException( "modalDialog""Cannot be null" );       
     
    if ( modalView == null )            
    throw new ArgumentNullException( "modalView""Cannot be null" );         
    EventHandler onDialogClosedHandler 
    = null;        EventHandler<ModalViewEventArgs> onViewClosedHandler = null;        
     
    if ( onClosed != null )        
    {            onDialogClosedHandler 
    = ( s, a ) =>            
    {                
    modalDialog.Closed 
    -= onDialogClosedHandler;                
    onClosed( dataContext );           
     };            
     onViewClosedHandler 
    = ( s, a ) =>            {                
    modalDialog.Closed 
    -= onDialogClosedHandler;                modalView.Closed -= onViewClosedHandler;                modalDialog.DialogResult = a.DialogResult;                //modalDialog.Close();                
    onClosed( dataContext );           
     };             
    modalDialog.Closed 
    += onDialogClosedHandler;            
    modalView.Closed 
    += onViewClosedHandler;       
     }         
    modalDialog.Content 
    = modalView;        modalView.DataContext = dataContext;        modalDialog.ShowDialog();    
    }
    }

      下面来实现接口 IModalDialog和IModalView.ModalDialog的实现非常简单,只要继承Child Window和接口IModalDialog即可。请看下面代码

      

    public class ExtendedChildWindow : ChildWindow, IModalDialog
    {    
    public void ShowDialog()    
    {        
    this.Show();   
    }
    }

      上面这个类ExtendedChildWindow 是通用的,你只需要向属性Content赋值即可。例如,如果你需要创建一个修改实体Person的窗口,你就必须创建一个新的UserControl和实现接口IModalView。

    代码
    public partial class EditPersonControl : UserControl, IModalView{    public EditPersonControl()    {        InitializeComponent();    }     public event EventHandler<ModalViewEventArgs> Closed;     protected virtual void OnClosed( ModalViewEventArgs e )    {        if ( this.Closed != null )            this.Closed( this, e );    }     private void btnOkClick( object sender, RoutedEventArgs e )    {        this.OnClosed( new ModalViewEventArgs( true ) );    }     private void btnCancelClick( object sender, RoutedEventArgs e )    {        this.OnClosed( new ModalViewEventArgs( false ) );    }}

      你仅仅需要做的事情就是在后台代码中实现接口IModalView即可:若确定按钮被按下时设置属性DialogResult为true,若取消按钮被按下时,设置为false。说明:因为这段代码不是业务逻辑,是UI设计,所以可以写在后台。

    最后在 ModalDialogWorker再写入以下三句代码即可

    modalDialog.Content = modalView;
    modalView.DataContext 
    = dataContext;
    modalDialog.ShowDialog();

      最后一个问题是怎么把接口IModalDialog, IModalVIew 和IModalDialogWorker的实现潜入到ViewModel中,看下图

      

       在ViewModel层,需要引入Imports,然后使用类ModalDialogWorker,此类有四个参数,主要用来显示模式窗口,其中具体的实现方法已经在上面说明过。下面的代码就是怎么调用此接口。

    代码
    private void OnEditPersonCommandExecute()
    {    
    this.ModalDialogWorker.ShowDialog<Person>
    (        
    this.ModalDialog, this.EditPersonControl, this.SelectedPerson, p =>       
     {            
    if ( this.ModalDialog.DialogResult.HasValue &&                this.ModalDialog.DialogResult.Value 
    )            
    {      
              
    // OK           
     }           
     
    else            
    {                
    //Cancel            
    }        

    );
    }

      如果随笔到此为知,基本上已经可以实现弹出模式窗口的效果了,但是还有一个重要的问题,怎么修改对象的值。比如:修改一条数据时,你如果把数据传到模式窗口,赋值到指定的位置(TextBox,DateTime等等)。而且当在模式窗口输入数据并点击确定或者取消按钮时,怎么提交这些数据,怎么恢复对象原始状态。

      我们必须通过使用BeginEdit、EndEdit和CancelEdit方法来实现提交或者回滚这些改变。实现这些功能非常的容易,我们可以使用接口IEditableObject.其中最重要的一个问题是怎么保存对象的状态。我们可以使用Memento模式,看下图

     通过上图,我们可以创建一个基类,命名为Memento<T>,代码如下

    public class Memento<T>
    {
        private Dictionary<PropertyInfo, object> storedProperties = new Dictionary<PropertyInfo, object>();
     
        public Memento( T originator )
        {
            this.InitializeMemento( originator );
        }
     
        public T Originator
        {
            get;
            protected set;
        }
     
        public void Restore( T originator )
        {
            foreach ( var pair in this.storedProperties )
            {
                pair.Key.SetValue( originator, pair.Value, null );
            }
        }
     
        private void InitializeMemento( T originator )
        {
            if ( originator == null )
                throw new ArgumentNullException( "Originator", "Originator cannot be null" );
     
            this.Originator = originator;
            IEnumerable<PropertyInfo> propertyInfos = typeof( T ).GetProperties( BindingFlags.Public | BindingFlags.Instance )
                                            .Where( p => p.CanRead && p.CanWrite );
     
            foreach ( PropertyInfo property in propertyInfos )
                this.storedProperties[ property ] = property.GetValue( originator, null );
        }
    }

      这段代码比较简单,首先你要传一个参数originator,在方法InitiallizeMemento中,你需要把对象的状态,所有的属性以及他们的值存入一个简单的Dictionary<string,object>

    this.Originator = originator;

    IEnumerable<PropertyInfo> propertyInfos = typeof( T ).GetProperties( 

                                    BindingFlags.Public | BindingFlags.Instance )

                                    .Where( p => p.CanRead && p.CanWrite );

     

    foreach ( PropertyInfo property in propertyInfos )

        this.storedProperties[ property ] = property.GetValue( originator, null );

      最后,在方法RestoreState中,我们把原始数据还原取对象的初始状态  

    public void Restore( T originator )
    {
        foreach ( var pair in this.storedProperties )
        {
            pair.Key.SetValue( originator, pair.Value, null );
        }
    }

      最后是一个类Caretaker,此类是一个简单的封装,实现了接口IEditalbleOject以及引用了基类Memento<T>,代码如下

    public class Caretaker<T> : IEditableObject

    {

        private Memento<T> memento;

        private T target;

     

        public T Target

        {

            get

            {

                return this.target;

            }

            protected set

            {

                if ( value == null )

                {

                    throw new ArgumentNullException( "Target", "Target cannot be null" );

                }

     

                if ( Object.ReferenceEquals( this.Target, value ) )

                    return;

     

                this.target = value;

            }

        }

     

        public Caretaker( T target )

        {

            this.Target = target;

        }

     

        public void BeginEdit()

        {

            if ( this.memento == null )

                this.memento = new Memento<T>( this.Target );

        }

     

        public void CancelEdit()

        {

            if ( this.memento == null )

                throw new ArgumentNullException( "Memento", "BeginEdit() is not invoked" );

     

            this.memento.Restore( Target );

            this.memento = null;

        }

     

        public void EndEdit()

        {

            if ( this.memento == null )

                throw new ArgumentNullException( "Memento", "BeginEdit() is not invoked" );

     

            this.memento = null;

        }

    }

      到此为止,就可以按下面的方式调用Caretaker了

    Caretaker<Person> editableObject = new Caretaker<Person>( this.SelectedPerson );

    editableObject.BeginEdit();

    //.....

    this.SelectedPerson.Name = "Pesho";

    // Commit Changes

    editableObject.EndEdit();

    // -or CancelChanges

    // editableObject.CancelEdit();

      通过上面的方法,我们返回到ViewModel层,当更新对象的方法OnEditPersonCommandExecute中有如下代码

    private void OnEditPersonCommandExecute()

    {

        Caretaker<Person> editableObject = new Caretaker<Person>( this.SelectedPerson );

        editableObject.BeginEdit();

     

        this.ModalDialogWorker.ShowDialog<Person>(

            this.ModalDialog, this.EditPersonControl, this.SelectedPerson, p =>

            {

                if ( this.ModalDialog.DialogResult.HasValue &&

                    this.ModalDialog.DialogResult.Value )

                {

                    editableObject.EndEdit();

                }

                else

                {

                    editableObject.CancelEdit();

                }

            } );

    }

      到此为止,已经可以实现在MVVM模式中弹出模式窗口以及实现修改对象的值。希望对大家有帮忙,因为如果实现真正的MVVM模式时,实现弹出窗口并且可以把数据赋值给窗口的控件,并且可以修改数据库的值时真的不太容易

       原文地址:http://www.silverlightshow.net/items/ModalDialogs-IEditableObject-and-MVVM-in-Silverlight-4.aspx

      点击下载源程序

  • 相关阅读:
    測试AtomicInteger与普通int值在多线程下的递增操作
    《漫画线性代数》读书笔记 矩阵
    Android下雪动画的实现
    Live555实战之交叉编译live555共享库
    JAVA_SE基础——24.面向对象的内存分析
    Linux下利用signal函数处理ctrl+c等信号
    tomcat6url请求400错误(%2F与%5C)
    python的交互式shell-ipython体验
    1906月读书清单
    Linux对变量的截取替换
  • 原文地址:https://www.cnblogs.com/888h/p/1924758.html
Copyright © 2011-2022 走看看