信息系统开发平台OpenExpressApp - Command扩展机制

  下图为OpenExpressApp的系统架构图,其中在业务层中Command是作为一种系统内部提供以及可供外部扩展的一种机制。OpenExpressApp框架对功能的主要扩展之一就是Command机制,OEA提供的Command可以实现用户交互,更好的分离业务逻辑,带来更好的维护性和 可扩展性。

Command位于架构图业务层

信息系统开发平台OpenExpressApp - Command扩展机制

Command的由来

  信息系统开发平台OpenExpressApp - Command扩展机制

MVC是一种经典的架构模式,如上图所示:

模型(Model)用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。“模型 ”不依赖“视图”和“控制器”,也就是说,模型不关心它会被如何显示或是如何被操作(OEA现在的类库上有UI相关信息只是基于属性的一种元模型实现,它和类模型不是一个概念,大家需要区分开来),但是模型中数据的变化一般会通过一种通知机制被公布(例如:WPF中绑定属性内部实现了通知机制)。

视图(View) 能够展现数据。在视图中一般没有业务上的逻辑。

控制器(Controller) 控制器可以访问视图和模型,用于控制应用程序的业务流程。控制器提高了应用程序的灵活性和可配置性,它可以用来连接不同的模型和视图去完成用户的需求。给 定一些可重用的模型和视图,控制器可以根据用户的需求选择适当的模型机型处理,然后选择适当的的视图将处理结果显示给用户。

OEA中的Command就是控制器,通过Command可以访问视图和模型,控制应用程序的业务。

主要类图

信息系统开发平台OpenExpressApp - Command扩展机制

CommandBase

CommandBase作为所有命令类的基类,主要定义了CanVisible、CanExecute和Execute几个方法, Execute执行方法在子类中实现。

信息系统开发平台OpenExpressApp - Command扩展机制信息系统开发平台OpenExpressApp - Command扩展机制Code
    public abstract class CommandBase
    {
        
public CommandBase()
        {信息系统开发平台OpenExpressApp - Command扩展机制 }

        
public virtual bool CanVisible(object param)
        {
            
return true;
        }

        
public virtual bool CanExecute(object param)
        {
            
return true;
        }
        
public abstract void Execute(object param);
    }


ViewCommand、WPFViewCommand

ViewCommand、WPFViewCommand在CanVisible、CanExecute和Execute三个方法中传入的参数是ObjectView类型,通过ObjectView可以访问视图和模型。

信息系统开发平台OpenExpressApp - Command扩展机制信息系统开发平台OpenExpressApp - Command扩展机制Code
    public abstract class WPFViewCommand : ViewCommand
    {
        
public override bool CanVisible(object param)
        {
            
return CanVisible(param as ObjectView);
        }

        
public override bool CanExecute(object param)
        {
            
return CanExecute(param as ObjectView);
        }

        
public override void Execute(object param)
        {
            Execute(param 
as ObjectView);
        }

        
public virtual bool CanVisible(ObjectView view)
        {
            
return true;
        }

        
public virtual bool CanExecute(ObjectView view)
        {
            
return true;
        }

        
public abstract void Execute(ObjectView view);
    }

WPFListViewCommand

WPFListViewCommand在CanVisible、CanExecute和Execute三个方法中传入的参数是ListObjectView类型。由于对ListObjectView进行扩展的比较多,所以单独实现了一个类。

WPFAutoCommand

WPFAutoCommand在生成窗体的View后自动调用的Command,可以在这个命令中挂接事件等。如以下代码示例:

信息系统开发平台OpenExpressApp - Command扩展机制信息系统开发平台OpenExpressApp - Command扩展机制Code
    [Command("合同预算导航", ToolbarType = ToolbarType.Manual, TargetObjectType = typeof(Contract), Label = "合同预算导航", ToolTip = "设置值合同预算导航")]
    
public class SetPBSBudgetNavigateCriteriaCommand : WPFAutoCommand
    {
        
public override void Execute(ObjectView view)
        {
            view.SelectedItemChanged 
+= new SelectedItemChangedEventHandler(view_SelectedItemChanged);
        }
        
void view_SelectedItemChanged(object sender, ObjectView view, SelectedItemChangedEventArgs e)
        {
            Contract contract 
= view.CurrentObject as Contract;
            
if ((null != contract) && (!contract.IsNew))
            {
                ContractPBSBudgetNavigateCriteria contractPBSBudgetNavigateCriteria 
= ((view as ListObjectView).DetailView.GetChildView(typeof(ContractPBSBudget), trueas ListObjectView).NavigateQueryView.CurrentObject as ContractPBSBudgetNavigateCriteria;
                
if (null != contract)
                {
                    contractPBSBudgetNavigateCriteria.ContractId 
= (Guid)view.CurrentID;
                    contractPBSBudgetNavigateCriteria.PBSs 
= contract.PBSList;
                }
                
else
                {
                    contractPBSBudgetNavigateCriteria.PBSs 
= null;
                }
            }
        }
    }

PropertyEditorCommand

PropertyEditorCommand将属性编辑器PropertyEditor作为参数,框架内部的附件操作命令就是这类命令。

代码目录结构

Command在OEA中的目录结构如下图:

信息系统开发平台OpenExpressApp - Command扩展机制

Pattern

Pattern目录下为codeproject上的一个扩展WPF Command的一个实现,我用它来作为OEA的command应用模式。这里就不做进一步介绍了,感兴趣的可以去那个网站看看信息系统开发平台OpenExpressApp - Command扩展机制

CommandAdapter

OEA是通过CommandAdapter来把CommandBase适配到Pattern

信息系统开发平台OpenExpressApp - Command扩展机制信息系统开发平台OpenExpressApp - Command扩展机制Code
    public class CommandAdapter : Itenso.Windows.Input.Command
    {
        
private readonly CommandBase coreCommand;

        
public CommandAdapter(CommandBase coreCommand, Type ownerType, CommandDescription description) :
            
base(coreCommand.Name, ownerType, description)
        {
            
if (coreCommand == null)
            {
                
throw new ArgumentNullException("coreCommand");
            }

            
this.coreCommand = coreCommand;
        }

        
public CommandBase CoreCommand
        {
            
get { return this.coreCommand; }
        }
        
public override bool IngoreIsExecuting
        {
            
get { return coreCommand.IngoreIsExecuting; }
        }

        
protected override bool OnCanExecute(CommandContext commandContext)
        {
            
return this.coreCommand.CanExecute(commandContext.CommandParameter);
        }
        
protected override void OnExecute(CommandContext commandContext)
        {
            
this.coreCommand.Execute(commandContext.CommandParameter);
        }
    }

ObjectEditCommand、TreeEditCommand、FileAttachmentCommand、LookupCommand、ViewExtCommand

这些Command都是OEA内置对对象、树形、附件、下拉列表、View的基本操作的支持,具体代码可以去看看源码,这里就不一一列出来了。

信息系统开发平台OpenExpressApp - Command扩展机制信息系统开发平台OpenExpressApp - Command扩展机制Code
    public class CommandNames
    {
        
public const string Add = "Add";
        
public const string CopyAndNew = "CopyAndNew";
        
public const string AddChild = "AddChild";
        
public const string DeleteBillObject = "DeleteBillObject";
        
public const string DeleteListObject = "DeleteListObject";
        
public const string DeleteChildObject = "DeleteChildObject";
        
public const string Cancel = "Cancel";
        
public const string Refresh = "Refresh";
        
public const string Save_Bill = "Save_Bill";
        
public const string Save_List = "Save_List";

        
public const string InsertBefore = "InsertBefore";
        
public const string InsertFollow = "InsertFollow";
        
public const string MoveDown = "MoveDown";
        
public const string MoveUp = "MoveUp";
        
public const string LevelUp = "LevelUp";
        
public const string LevelDown = "LevelDown";
        
public const string ExpandAll = "ExpandAll";
        
public const string CollapseAll = "CollapseAll";

        
public const string ClearQueryCondition = "ClearQueryCondition";
        
public const string QueryObject = "QueryObject";
        
        
//Lookup
        public const string ClearLookupPropertyValue = "ClearLookupPropertyValue";

        
//view
        public const string MaxShowView = "MaxShowView";

        
//select
        public const string SelectAll = "SelectAll";

        
//FileAttachment
        public const string AddFile = "AddFile";
        
public const string OpenFile = "OpenFile";
        
public const string FileSaveAs = "FileSaveAs";
        
public const string ClearFile = "ClearFile";
    }

如何使用

  1. 增加Command继承类,实现CanVisible、CanExecute和Execute方法
  2. 在类定义上增加属性标记,让OEA注册这个Command到资源库中
  3. 运行时由AutoUI自动生成对应的Command的按钮或者菜单项

Command类

  如果我们需要对树形操作增加一个命令全部展开,那么首先需要生成一个类ExpandAllCommand,由于这个命令是对树形列表视图进行操作,所以选择从WPFListViewCommand继承过来。生成类后再在类上加上Command属性来注册命令。更多的示例可以查看OEA内部支持的一些Command。

信息系统开发平台OpenExpressApp - Command扩展机制信息系统开发平台OpenExpressApp - Command扩展机制Code
    [Command(CommandNames.ExpandAll, Label = "全部展开", ToolTip = "全部展开")]
         public class ExpandAllCommand WPFListViewCommand
        {
            
public override bool CanVisible(ListObjectView view)
            {
                
return (null != view) && ((view is ListObjectView) && ((ListObjectView)view).IsTree);
            }

            
public override void Execute(ListObjectView view)
            {
                (view.Control 
as MultiObjectTreeView).ExpandAll();
            }
        }

  在类定义上增加了Command属性后,OEA会把这个Command到资源库中,以下将介绍以下属性以及OEA如何生成Command对应的按钮或者菜单项。

基于属性的模型支持

信息系统开发平台OpenExpressApp - Command扩展机制

  OEA目前对UI模型和Command模型的支持都是基于属性来定义的,Command通过CommandAttribute来定义

信息系统开发平台OpenExpressApp - Command扩展机制信息系统开发平台OpenExpressApp - Command扩展机制Code
    [AttributeUsage(AttributeTargets.Class)]
    
public class CommandAttribute : Attribute
    {
        
public CommandAttribute(string name)
        {
            
this.Name = name;
            
this.CommandCategory = CommandCategory.RecordEdit;
            
this.ModuleType = ModuleType.Unspecified;
        }

        
public string Name { getset; }
        
public string Label { getset; }

        
public string ToolTip { getset; }
        
public string ImageName { getset; }
        
public ViewType TargetViewType { getset; }
        
/// <summary>
        
/// 这个命令显示在哪个类型的工具条上
        
/// </summary>
        public ToolbarType ToolbarType { getset; }
        
public ModuleType ModuleType { getset; }
        
public Type TargetObjectType { getset; }
        
public bool IngoreIsExecuting { getset; }
        
public CommandCategory CommandCategory { getset; }
    }

  另外为了方便的支持控制系统Command的是否可以使用,增加了NotAllowRemoveNotAllowNewNotAllowEdit三 个属性。如果类定义上标识了这些属性,OEA在生成Command对应按钮时会进行判断是否使用这些Command。在 OpenExpressApp.MetaModel项目ApplicationModel.cs中的业务对象元信息BusinessObjectInfo 对象中有以下方法:

信息系统开发平台OpenExpressApp - Command扩展机制信息系统开发平台OpenExpressApp - Command扩展机制Code
       /// <summary>
        
/// 为业务模型对象加入默认不可见命令
        
/// </summary>
        
/// <param name="bOInfo"></param>
        public void AddNotVisibleCommand()
        {
            
if (NotAllowNew)
            {
                ApplicationModel.AddNotVisibleCommand(CommandNames.Add, 
this);
                ApplicationModel.AddNotVisibleCommand(CommandNames.AddChild, 
this);
                ApplicationModel.AddNotVisibleCommand(CommandNames.CopyAndNew, 
this);
                ApplicationModel.AddNotVisibleCommand(CommandNames.InsertBefore, 
this);
                ApplicationModel.AddNotVisibleCommand(CommandNames.InsertFollow, 
this);

            }

            
if (NotAllowRemove)
            {
                ApplicationModel.AddNotVisibleCommand(CommandNames.DeleteChildObject, 
this);
                ApplicationModel.AddNotVisibleCommand(CommandNames.DeleteListObject, 
this);
                ApplicationModel.AddNotVisibleCommand(CommandNames.DeleteBillObject, 
this);
            }

            
if (NotAllowEdit)
            {
                ApplicationModel.AddNotVisibleCommand(CommandNames.MoveDown, 
this);
                ApplicationModel.AddNotVisibleCommand(CommandNames.MoveUp, 
this);
            }

            
if (NotAllowNew && NotAllowRemove && NotAllowEdit)
            {
                ApplicationModel.AddNotVisibleCommand(CommandNames.Cancel, 
this);
                ApplicationModel.AddNotVisibleCommand(CommandNames.Save_Bill, 
this);
                ApplicationModel.AddNotVisibleCommand(CommandNames.Save_List, 
this);
                ApplicationModel.AddNotVisibleCommand(CommandNames.Refresh, 
this);
            }
        }

注册Command

在OpenExpressApp.Module项目BaseModule初始化时遍历程序集类型注册Command

信息系统开发平台OpenExpressApp - Command扩展机制信息系统开发平台OpenExpressApp - Command扩展机制Code
    /// <summary>
    
/// 所有模块的基类
    
/// 
    
/// 加入业务模型 命令等
    
/// </summary>
    public class BaseModule : IModule
    {
          /// 加入Module类在程序集内的 业务模型 命令等
        public virtual void Initialize()
        {
            
//如果是继承自这个类的类
            if (this.GetType() != typeof(BaseModule))
            {
                var allTypes 
= this.GetType().Assembly.GetTypes();
                
foreach (var item in allTypes.Where(t => t.HasMarked<BusinessObjectAttribute>()))
                {
                    
//加入业务模型
                    BusinessObjectInfo bOInfo = ApplicationModel.AddBusinessObject(item);
                    
//为业务模型加入默认命令
                    bOInfo.AddNotVisibleCommand();
                }

                
foreach (var item in allTypes.Where(t => t.HasMarked<CommandAttribute>()))
                {
                    
//加入自定义命令
                    ApplicationModel.Commands.Add(Activator.CreateInstance(item) as CommandBase);
                }
            }
        }
    }

AutoUI使用Command生成按钮

  Command定义完后需要通过按钮或者菜单项在UI中表现出来,OEA通过AutoUI功能进行自动生成按钮来实现,在《AutoUI自动生成界面》中介绍的CreateMainToolBar和CreateChildToolBar中会基于Command的一些属性来过滤可以使用的命令CreateMainToolBar代码如下:

信息系统开发平台OpenExpressApp - Command扩展机制信息系统开发平台OpenExpressApp - Command扩展机制Code
        /// <summary>
        
/// 生成主工具栏
        
/// </summary>
        
/// <param name="mainToolbar"></param>
        
/// <param name="boType"></param>
        
/// <param name="view"></param>
        
/// <param name="moduleType"></param>
        public static void CreateMainToolBar(ToolBar mainToolbar, Type boType, ObjectView view, ModuleType moduleType)
        {
            view.ToolBar 
= mainToolbar;

            
//找到对应这个toolbar的所有command
            var commands = ApplicationModel.Commands.Where(
                c 
=> ((c.TargetObjectType == boType) || (c.TargetObjectType == null)) &&
                    ((c.ModuleType 
== ModuleType.Unspecified) || (c.ModuleType == moduleType)) &&
                    ((c.ToolbarType 
== ToolbarType.Any) || (c.ToolbarType == ToolbarType.Main)));

            
//CommandCategory.Filter类型下的命令需要在一个下拉列表中显示
            Panel filterPanel = null;
            
//ComboBox cb = null;
            foreach (var c in commands)
            {
                
bool notVisible = ApplicationModel.IsNotVisibleCommand(c.Name, ApplicationModel.GetBusinessObjectInfo(boType));
                
if (notVisible == false && (c.CanVisible(view)))
                {
                    Button btn 
= new Button()
                    {
                        Name 
= "btn" + c.Name,
                    };

                    
//第一个
                    if ((null == filterPanel) && (CommandCategory.Filter == c.CommandCategory))
                    {
                        filterPanel 
= new StackPanel() { Orientation = Orientation.Horizontal };
                        mainToolbar.Items.Add(filterPanel);
                    }

                    btn.CommandParameter 
= view;
                    ButtonCommand.SetCommand(btn, CommandRepository.Commands[c.Name]);

                    
if (CommandCategory.Filter == c.CommandCategory)
                    {
                        filterPanel.Children.Add(btn);
                    }
                    
else
                    {
                        mainToolbar.Items.Add(btn);
                    }
                }
            }
            
if (0 == mainToolbar.Items.Count)
            {
                mainToolbar.Visibility 
= Visibility.Collapsed;
            }
        }

 

更多内容:信息系统开发平台OpenExpressApp - Command扩展机制 开源信息系统开发平台之OpenExpressApp框架.pdf

 

欢迎转载,转载请注明:转载自周金根 [ http://zhoujg.cnblogs.com/ ]

转载于:https://www.cnblogs.com/zhoujg/archive/2009/11/16/1603826.html