“你们开发的系统如果能够像Excel那样支持全选、反选、多选、撤销、导入、导出等命令;不要每次增加一条记录都必须按一下‘新增’,完了以后再按一下‘保存’,取而代之的是我想保存的时候一按‘保存’,就把我前面所有的操作全都保存,如果我的操作有误,可以撤销刚刚所做的操作。那真是爽死了。”很多的用户这样感慨。用户体验非常重要,很多的时候,如果用户觉得你的系统不方便,他就会拒绝使用。这样,系统也就没有什么意义了。如果在给客户做演示的时候,能够充分展现你系统在易用性方面的优势,那么中标几率就会大大的增加。

DataTableDataGridViewCommand设计模式完全可以前面用户所提到的功能。我们先分析一下,用户所提到的功能,实际上就是希望能够对界面层表示的数据方便的发出各种命令,这些命令可以通过一个个的按钮提供给用户调用。如果我们用DataTable来容纳业务数据,用DataGridView来表示这些数据,那么对“全选、反选、多选”等,就是针对DataGridView来说的,而对于“撤销、导入、批量保存”等则是对业务数据来说的,而DataTable则支持这些操作。下面是DataTableDataRowDataGridView、按钮和数据操作命令之间的关系简图。

可以看出,数据操作命令就是Command模式中的Command,而按钮就是Invoker,而DataTableDataGridView则是Receiver,在WinForm中,实际上我们主要是通过BindingSource来操作DataTable,所以对于DataTable这个间接的Receiver来说,其直接Receiver就是一个BindingSource。但是,不管是DataTable还是BindingSource都不能全部符合我们的要求,所以我们必须自己实现一个符合我们要求的Receiver,假设我们叫他DataCommandReceiverBase,为了能够进行可是化设计,我们让他继承自Component,其原型如下:

    public abstract class DataCommandReceiverBase:Component

    {

        public abstract void New();

        public abstract void Delete();

        public abstract void UnDo();

        public abstract void Save();

}

 

  对于“新增、删除、保存、撤销”等命令,我们把它们抽象为一个公共的类,叫做DataCommand,其原型如下:

    public abstract class DataCommand : Component

    {

        public abstract void Do();

        DataCommandReceiverBase _Receiver;

        public DataCommandReceiverBase Receiver

        {

            get { return _Receiver; }

            set { _Receiver = value; }

        }

}

然后,我们对每一个基本的操作,比如“新增”建立一个对应的DataCommand的子类,并重写Do函数,在该函数中调用Receiver相应的功能。如下所示:

    public class NewCommand : DataCommand

    {

        protected override void Do()

        {

            Receiver.New();

        }

    }

 

    public class DeleteCommand : DataCommand

    {

        protected override void Do()

        {

            Receiver.Delete();         

        }

    }

 

    public class SaveCommand : DataCommand

    {

        protected override void Do()

        {

            Receiver.Save();     

        }       

    }

 

    public class UndoCommand : DataCommand

    {

        protected override void Do()

        {

            Receiver.UnDo();

        }

}

 

对于给用户提供数据操作命令的按钮,我们叫他CommandButton,他包含有一个DataCommand的实例Command以便在用户按下按钮时执行用户的命令。其原型如下:

public class CommandButton:Button

    {

        DataCommand _Command;

        public DataCommand Command

        {

            get { return _Command; }

            set

            {

                if (_Command == value)   return;                  

                _Command = value;               

            }

        }

 

        protected override void OnClick(EventArgs e)

        {

            if (_Command != null)

                _Command.Do();

            base.OnClick(e);

        }       

 }

 

现在,只要实现一个DataCommandReceiverBase的子类,我们的目的就达到了。要实现一个这样的类也非常的简单,稍微难一点的地方就是Undo方法的实现,如果用其他的平台我不知道,但是用.net 下面的DataTable,真的很容易,只要在目标DataTableRowChanged事件和RowDeleted事件中把操作过的DataRow记录下来,在Undo的时候,取出最近的记录,然后调用DataRowRejectChanges方法就可以搞定一切。其代码如下:

public class DefaultDataCommandReceiver : DataCommandReceiverBase

    {

        DataTable _SourceTable = null;

        List<DataRow> _HistoryList = new List<DataRow>();

     

        BindingSource _BindingSource;       

        public BindingSource BindingSource

        {

            get { return _BindingSource; }

            set

            {

                if (_BindingSource == value)

                    return;

                if (_SourceTable != null)

                {

                    _SourceTable.RowChanged -= new DataRowChangeEventHandler(_SourceTable_RowChanged);

                    _SourceTable.RowDeleted -= new DataRowChangeEventHandler(_SourceTable_RowDeleted);

                }

 

                _SourceTable = null;

                if (value == null)

                {

                    _BindingSource = null;

                    return;

                }              

 

                if (IsValidDataSource(value,ref _SourceTable))

                {

                    _BindingSource = value;

 

                    if (_SourceTable != null)

                    {

                        _SourceTable.RowChanged += new DataRowChangeEventHandler(_SourceTable_RowChanged);

                        _SourceTable.RowDeleted += new DataRowChangeEventHandler(_SourceTable_RowDeleted);

                    }

 

                    return;

                }

                throw new Exception("BindingSourceDataSource必须是DataTable/DataSet/DataView");

            }

        }

 

        bool IsValidDataSource(BindingSource value, ref DataTable ReturnTable)

        {

            if ((value.DataSource is DataTable) || (value.DataSource is DataSet) || (value.DataSource is DataView))

            {

                if (value.DataSource is DataTable)

                    ReturnTable = value.DataSource as DataTable;

                else if (value.DataSource is DataSet)

                    ReturnTable = (value.DataSource as DataSet).Tables[value.DataMember];

                else

                    ReturnTable = (value.DataSource as DataView).Table;

 

                return true;

            }

 

            if (value.DataSource is BindingSource)

                return IsValidDataSource(value.DataSource as BindingSource, ref ReturnTable);

 

            return false;

        }

 

        void _SourceTable_RowDeleted(object sender, DataRowChangeEventArgs e)

        {

            if (!BeginLogUpdate)  return;

            LogRow(e.Row);

        }

 

        void _SourceTable_RowChanged(object sender, DataRowChangeEventArgs e)

        {

            if (!BeginLogUpdate)     return;

            if (e.Action == DataRowAction.Add || e.Action == DataRowAction.Change)

                LogRow(e.Row);

        }

 

        void LogRow(DataRow ARow)

        {

            if (_HistoryList.Contains(ARow))

                _HistoryList.Remove(ARow);

            _HistoryList.Insert(0,ARow);

        }

 

        bool _BeginLogUpdate = false;       

        public bool BeginLogUpdate

        {

            get { return _BeginLogUpdate; }

            set { _BeginLogUpdate = value; }

        }

 

        public override void New()

        {

            BindingSource.AddNew();

        }

 

        public override void Delete()

        {

            if (BindingSource.Count == 0)    return;          

 

            ((DataRowView)BindingSource.Current).Row.Delete(); 

        }

 

        public override void Save()

        {

            if (_HistoryList.Count == 0)   return;

 

            SaveDataToDb();

            _SourceTable.AcceptChanges();

            _HistoryList.Clear();     

        }

 

        protected virtual void SaveDataToDb()

        {

            //

            // Save the changed data into database

            //

        }

 

        public override void UnDo()

        {

            if (_HistoryList.Count == 0)  return;

            _HistoryList[0].RejectChanges();

            _HistoryList.RemoveAt(0);    

        }     

}

 

现在,这些类的关系如下图所示:




到现在为止,我们还没有实现针对DataGridView的“全选、反选”等功能,这个就留给大家来完成吧。

学了一段时间的设计模式,发现设计模式的概念不难理解,难在应用,希望这些文章能对各位特别是初学设计模式的朋友有用。也希望各位高人能不吝指点,以让大家能够在设计上更上一层楼。

      参考代码:Command.rar
posted on 2007-08-01 20:06  永红  阅读(4558)  评论(21编辑  收藏  举报