在XAF开发中,常常要用到一个方法Frame.GetController<ControllerType>(),该方法的作用是从Frame.Controllers集合中获取某个Controller,但通常我们可能并不只是为了得到该Controller后,而是去访问它拥有的Action或者使用其类包含的方法。大部分时候,直接从Controller中使用该方法都能正常工作,但并不是全部时候。比如在一个DetailView的Controller中,调用Frame.GetController<ControllerType>()方法获取某个ListViewController的Action,并执行该Action.DoExecute()方法时就会出错,运行时会报告该Action并未激活,原因是"Controller active",这实际上就是该ListViewController未激活造成的。所以,问题来了,怎样理解Frame和Controller的关系呢?怎样正确地使用Frame.GetController<ControllerType>()方法?
我在之前一篇博客
XAF之Window, Frame和Template
中已经讲到过Frame和Template的关系。在XAF的每种视图中,都包含了Frame和Template,它们构成了视图的全部定义,而完成的功能则留给了Frame.Controllers中包含的各种Controller。当创建Frame时,它会加载XAF中所有合适的Controller到Frame.Controllers集合中,即为这些Controller调用各自的构造函数创建了新实例,然后就过滤掉不符合条件的Controller,过滤条件有很多,比如我们在VS中设计Controller时的,TargetViewId,TargetViewType, TargetViewNesting, TargetObjectType等等。过滤完成后的Controller就是当前Frame可用的Controller,自然也就可用Frame.GetController<ControllerType>()获取该Controller并可靠访问其成员。而且,也只有通过了过滤的Controller,XAF才会去触发其Actived等等后续事件,注意,FrameAssigned事件是在过滤时触发的,所以要考虑到FrameAssigned中的代码是否会做多余的事情。下面以一个简单例子,讲述Frame创建到显示的整个过程:为XAF添加一个DockPanel,在DockPanel中显示当前ListView选中的的Human的地址详情。如图1所示:
图1
代码如下:
private void CreatePanelAction_Execute(object sender, SimpleActionExecuteEventArgs e)
{
ListView listView = View as ListView;
Human man = listView.CurrentObject as Human;
//要访问XAF中的控件,就需要使用Template
//Template保存了视图的控件实际信息
MainForm form = (Application.MainWindow.Template as MainForm);
//MainForm form = (Frame.Template as MainForm);
DockManager dockManager = form.DockManager;
DockPanel panel = dockManager.AddPanel(DockingStyle.Bottom);
/*
* Frame类似于一个风筝的骨架
* 向该骨架中填入Template及View就类似于给风筝糊上纸浆
* 当CreateFrame创建Frame后,也同时为该Frame新创建了(说明调用了Controller构造函数)
* 全部的Controller,只不过只有部分被激活(Active)了,实际上,创建Frame的Controller
* 时还触发了Controller的FrameAssigned事件
*/
Frame cFrame = Application.CreateFrame(TemplateContext.NestedFrame);
cFrame.CreateTemplate();
IObjectSpace objectSpace = Application.CreateObjectSpace();
DetailView view = Application.CreateDetailView(objectSpace, "Address_DetailView", false, objectSpace.GetObject(man.Address1));
/*为Frame附加View后,继续过滤Controller
*将不符合条件的Controller禁用(Deactive)掉
*不要将这里的激活和禁用和Controller的Actived事件相混淆,
*整个Controller过滤的过程都没有执行到Actived事件
*/
cFrame.SetView(view);
panel.ControlContainer.Controls.Clear();
//这里也印证了Template is Control
System.Windows.Forms.Control address = cFrame.Template as System.Windows.Forms.Control;
address.Dock = System.Windows.Forms.DockStyle.Fill;
panel.Controls.Add(address);
}
了解了Frame对Controller的新建和过滤的过程,那么也就能理解和使用Frame.GetController方法了。比如,我们现在在一个弹出的DetailView中,点击某个Action做了某些事情后,需要更新主界面状态栏的某些信息,而这些状态信息需要用到适合于主界面的一个MyStatusController,那么获取该Controller的正确方式为:
MyStatusController status=Application.MainWindow.GetController<MyStatusController>();
而不是在DetailView的某个Controller中直接使用:
MyStatusController status=Frame.GetController<MyStatusController>(); //错误