AVEVA .Net 菜单自定义
AVEVA .Net Command and Menu Customisation
摘要Abstract:以一个具体实例详细介绍AVEVA .Net中自定义菜单的原理及方法。掌握了自定义菜单的方法就可以对二次开发的程序进行分类和整理,便于用户使用。
关键字Key words:AVEVA .Net、Addin Commands、Menu Customisation、PDMS、二次开发
一、概述 Introduction
通过《AVEVA .Net Quick Start Guide》这篇文章,大家对AVEVA .Net的二次开发有了个大概认识,但是这样并不能编写出实际有用的二次开发插件(Addin)。本文再深入一步,介绍自定义菜单方法,在菜单中调用二次开发的插件,便于用户操作。
用户通过菜单、右键菜单命令、工具条来调用开发的插件。通用程序框架CAF(The Common Application Framework)通过类CommandBarManager来提供了创建菜单、工具条等的接口。也提供了与这些工具交互事件的接口,这种方法CAF虽然支持但不推荐。
通用程序框架CAF还支持通过UIC(用户界面自定义文件User Interface Customisation)文件来自定义菜单、工具条等,UIC文件是XML格式的。
基于XML定义的菜单及工具条需要有个机制将用户界面实体与需要调用的相关功能关联上。为了实现这人目的,插件(Addins)功能通过一些命令对象(command objects)暴露出来。插件通过类CommandManager来加载这些命令对象。XML定义中可以包含用户界面实体如菜单入口、工具条的按钮及与其相关的命令对象。选择相应的菜单或点击工具条上的按钮都可以导致相关联的命令被执行。
基于命令模型的一个好处就是将用户界面或显示层与逻辑层解耦。程序逻辑层就与显示层没有什么直接关系了。若程序的状态需要对用户界面上的操作做出反映,程序只需要修改命令的状态即可。命令对象知道它与哪个用户界面实体关联,所以确保其内部状态反映相应的用户界面。这对一些状态是很简单的,如“enabled”、“visible、“checked”;但对动态程序状态就有点复杂了,如“combo-box”。
二、编写命令类 Writing a Command Class
如下所示的代码是一个简单的命令类示例,用来管理一个可停靠窗口的可见性,代码中是控制AttributeBrowser 可停靠窗口。通用程序框架CAF提供了一个抽象的命令类Command,所有的命令类都必须派生自Command类。在其构造函数中应该设置属性Key,在UIC文件中是通过Key这个属性来找到相应的命令,后面将会以一个实例来说明。
2
3 using System.Collections.Generic;
4
5 using System.Text;
6
7 using Aveva.ApplicationFramework.Presentation;
8
9 namespace Aveva.Presentation.AttributeBrowserAddin
10
11 {
12
13/// <summary>
14
15/// Class to manage the visibility state of the AttributeBrowser docked window
16
17/// This command should be associated with a StateButtonTool.
18
19/// </summary>
20
21public class ShowAttributeBrowserCommand : Command
22
23{
24
25private DockedWindow _window;
26
27/// <summary>
28
29/// Constructor for ShowAttributeBrowserCommand
30
31/// </summary>
32
33/// <param name="window">The docked window whose visibilty state will be managed.</param>
34
35public ShowAttributeBrowserCommand(DockedWindow window)
36
37{
38
39// Set the command key
40
41this.Key = "Aveva.ShowAttributeBrowserCommand";
42
43// Save the docked window
44
45 _window = window;
46
47// Create an event handler for the window closed event
48
49 _window.Closed += new EventHandler(_window_Closed);
50
51// Create an event handler for the WindowLayoutLoaded event
52
53 WindowManager.Instance.WindowLayoutLoaded += new EventHandler(Instance_WindowLayoutLoaded);
54
55}
56
57void Instance_WindowLayoutLoaded(object sender, EventArgs e)
58
59{
60
61// Update the command state to match initial window visibility
62
63this.Checked = _window.Visible;
64
65}
66
67void _window_Closed(object sender, EventArgs e)
68
69{
70
71// Update the command state when the window is closed
72
73this.Checked = false;
74
75}
76
77 /// <summary>
78
79 /// Override the base class Execute method to show and hide the window
80
81 /// </summary>
82
83 public override void Execute()
84
85 {
86
87 if (this.Checked)
88
89 {
90
91 _window.Show();
92
93 }
94
95 else
96
97 {
98
99 _window.Hide();
100
101 }
102
103 }
104
105 }
106
107}
108
109
抽象基类Command提供了以下方法,其派生类中可以重载:
l void Execute():必须被重载,在执行命令时调用这个函数;
l CommandState GetState():这个函数由通用程序框架CAF调用,以便更新用户界面或上下文菜单。返回值是一个CommandState枚举值,反应了命令的状态。枚举值用来位来表示命令状态,这些状态可以使用位的或OR操作。
l String Description:命令的描述;
l void Refrest(string context):当CommandManager.ApplicationContext的属性更改时都会调用这个方法。这也就给了一个更新其Enabled或visible状态的机会。
抽象基类Command也有一些属性可以用来更新用户界面上的状态:
l bool Checked:若命令与像StateButtonTool这样的用户界面关联,则此属性值与用户界面上的状态会同步;
l bool Enabled:此属性值的改变将会影响所有与其关联的用户界面;
l ArrayList List:此属性允许命令可以与有字符列表的用户界面交流。如ComboBoxTool;
l int SelectedIndex:此属性表示被用记选中的列表中的索引值;
l object Value:此属性保存了当前关联的用户界面实体;
l bool ValueChanged:通用程序框架CAF在调用命令类的Execute方法之前,若用户界面的值改变时会设置此属性的值。当命令执行结束后此值会被清除;
l bool Visible:此属性值的改变将会影响所有与其关联的用户界面;
在通用程序框架CAF中注册一个命令是通过向CommanManagers.Commands的集合中添加一个命令实例来实现的。程序代码如下所示:
2
3 // Get the CommandManager
4
5 CommandManager commandManager = (CommandManager)serviceManager.GetService( typeof (CommandManager));
6
7 ShowAttributeBrowserCommand showCommand = new ShowAttributeBrowserCommand(attributeListWindow);
8
9 commandManager.Commands.Add(showCommand);
10
三、命令事件 Command Events
命令基类Command提供了两个事件BeforeCommandExecute和CommandExecuted给程序员,用来响应命令的执行,即在命令执行前后做些处理。示例代码如下:
2
3 anotherCommand.BeforeCommandExecute += new System.ComponentModel.CancelEventHandler(anotherCommand_BeforeCommandExecute);
4
5 anotherCommand.CommandExecuted += new EventHandler(anotherCommand_CommandExecuted);
6
7 // The BeforeCommandExecute event handler is of type
8
9 // CancelEventHandler and is passed a CancelEventArgs object
10
11 // which enables the command execution to be cancelled by setting
12
13 // the Cancel property to true.
14
15 void anotherCommand_BeforeCommandExecute( object sender, System.ComponentModel.CancelEventArgs e)
16
17 {
18
19 e.Cancel = true ;
20
21 }
22
23
四、菜单自定义 Menu Customisation
用户使用插件的方式通常都是通过使用菜单或命令栏上的按钮。如前文所述,通用程序框架CAF提供了自定义菜单和命令栏的机制,即通过配置“User Interface Customisation”(UIC)文件。下面将会对UIC文件的细节进行描述,如怎样配置通用程序框架CAF来加载UIC文件,通过用户交互的自定义工具来编辑UIC文件等等。
五、配置要加载UIC文件的模块 Configuring a Module to Load a UIC File
每个基于通用程序框架CAF的插件都有一个XML格式的配置文件,包含了程序启动时需要加载的一系列UIC文件。此文件的默认路径就是安装目录,文件名的命令方式为<模块名>Customisation.xml。例如AVEVA Marine模块Hull Design就有一个名为HullDesignCustomisation.xml的配置文件,文件内容如下所示。默认情况下UIC文件也希望在相同的目录下。标专“$1”表示UIC文件的路径将会由当前工程名代替。
< UICustomizationSet xmlns:xsd ="http://www.w3.org/2001/XMLSchema" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" >
< UICustomizationFiles >
< CustomizationFile Name ="Hull General" Path ="AVEVA.Marine.UI.HullGeneral.uic" />
< CustomizationFile Name ="Hull Design" Path ="AVEVA.Marine.UI.HullDesign.uic" />
< CustomizationFile Name ="Project" Path ="$1.uic" />
< CustomizationFile Name ="StatusControllerAddin" Path ="StatusController.uic" />
</ UICustomizationFiles >
</ UICustomizationSet >
配置文件中UIC文件的顺序是很重要的,它表示了程序加载这些自定义界面的顺序。一个新的UIC文件可以加载到一个模块中,通过在其配置文件中简单添加一行即可。
与在模块的配置文件中添加UIC文件的功能类似的方法是在程序中编码实现对UIC文件的添加,通过CommandBarManager的方法AddUICustomisationFile来编码实现UIC文件的添加。程序代码如下所示:
2
3 CommandBarManager commandBarManager = (CommandBarManager)serviceManager.GetService( typeof (CommandBarManager));
4
5 commandBarManager.AddUICustomizationFile( " AttributeBrowser.uic " , " AttributeBrowser " );
6
通过具体实例将会看出,通过自定义对话框,配置文件中的UIC文件将会被管理,不用自己手动更改UIC文件中的内容。
六、UIC文件的编辑 Editing UIC File
UIC文件是通过通用程序框架CAF的内置交互工具来创建与编辑的,不需要亲自更其中内容。通过在工具栏上右键,选择Customize来启动自定义对话框,如下图所示:
自定义对话框主要由七个部分组成:
① CommandBar Preview Area
② Active Customisation File
③ Tree showing CommandBars
④ List of tools
⑤ Property grid
⑥ Action buttons
⑦ Resource Editor
以下将会以具体实例来介绍相应的用法。更多细节请参考《AVEVA .NET Customisation User Guide》。通过自定义对话框,UIC文件中的内容将会被AVEVA管理,所以创建自定义的菜单或工具栏按钮,都从这个自定义对话框中编辑,还是很方便。
七、程序实例 Codes
本节以一个具体实例来说明AVEVA .Net的菜单的自定义及与相关命令的关联方法。步骤如下:
1. 创建一个C#的类库(Class Library),工程名为HelloWorld,并选择.NET Framework 3.5,如下图所示:
2. 添加引用Aveva.ApplicationFramework.dll和 Aveva.ApplicationFramework.Presentation.dll,如下图所示:
添加完后的引用库如下图所示:
3. 编写一个插件类HelloAddin,派生自IAddin,并实现那四个虚函数,程序代码如下所示:
2
3 using System.Collections.Generic;
4
5 using System.Text;
6
7 using Aveva.ApplicationFramework;
8
9 using Aveva.ApplicationFramework.Presentation;
10
11 namespace HelloWorld
12
13 {
14
15 public class HelloAddin : IAddin
16
17 {
18
19 #region IAddin Members
20
21 public string Description
22
23 {
24
25 get
26
27 {
28
29 return " AVEVA .Net Menu Customisation Demo. " ;
30
31 }
32
33 }
34
35 public string Name
36
37 {
38
39 get
40
41 {
42
43 return " HelloAddin " ;
44
45 }
46
47 }
48
49 public void Start(ServiceManager serviceManager)
50
51 {
52
53 // Stop for attaching to the process.
54
55 System.Windows.Forms.MessageBox.Show( " Debug " );
56
57 }
58
59 public void Stop()
60
61 {
62
63 }
64
65 #endregion
66
67 }
68
69 }
70
71
4. 添加一个用户控件(User Control)而不是一个窗口(Windows Form),如下图所示:
5. 自定义用户控件上内容,本例仅在其上添加最简单的label为例,添加后并将其内容改为“Hello World”,如下图所示:
6. 添加命令类HelloWorldCommand,派生自抽象基类Command,并实现几个关键的方法。如在其构造函数中添加Key属性,Key属性在后面添加菜单时很重要。还有必须实现的Execute方法。程序代码如下所示:
2
3 using System.Collections.Generic;
4
5 using System.Text;
6
7 using Aveva.ApplicationFramework.Presentation;
8
9 namespace HelloWorld
10
11 {
12
13 class HelloWorldCommand : Command
14
15 {
16
17 private DockedWindow myWindow;
18
19 // Default Constructor.
20
21 public HelloWorldCommand(DockedWindow window)
22
23 {
24
25 // Set the command key.
26
27 // This is very import!!!
28
29 this .Key = " Aveva.HelloWorldCommand " ;
30
31 // Save the docked window.
32
33 myWindow = window;
34
35 // Create an event handler for the window closed event.
36
37 myWindow.Closed += new EventHandler(_window_Closed);
38
39 // Create an event handler for the windowLayoutLoaded event.
40
41 WindowManager.Instance.WindowLayoutLoaded += new EventHandler(Instance_WindowLayoutLoaded);
42
43 }
44
45 void _window_Closed( object sender, EventArgs e)
46
47 {
48
49 this .Checked = false ;
50
51 }
52
53 void Instance_WindowLayoutLoaded( object sender, EventArgs e)
54
55 {
56
57 // Update the command state to match initial window visibility.
58
59 this .Checked = myWindow.Visible;
60
61 }
62
63 // This method must be overriden to provide the command execution functionality.
64
65 public override void Execute()
66
67 {
68
69 // Always show the DockedWindow.
70
71 myWindow.Show();
72
73 }
74
75 }
76
77 }
78
79
7. 在派生自IAddin的类中的Start方法中添加创建可停靠窗口的代码,并将命令添加通用程序框架的命令集合中去,程序如下所示:
2
3 {
4
5 // Stop for attaching to the process.
6
7 System.Windows.Forms.MessageBox.Show( " Debug " );
8
9 // Get the WindowManager service.
10
11 WindowManager windowManger = (WindowManager)serviceManager.GetService( typeof (WindowManager));
12
13 // Create a docked window to host and Hello World User control.
14
15 DockedWindow myWindow = windowManger.CreateDockedWindow( " Aveva.HelloAddin.UserControl " ,
16
17 " HelloWorld " ,
18
19 new HelloAddinControl(),
20
21 DockedPosition.Right);
22
23 // Create and register Addin's command.
24
25 CommandManager commandManager = (CommandManager)serviceManager.GetService( typeof (CommandManager));
26
27 HelloWorldCommand showCommand = new HelloWorldCommand(myWindow);
28
29 commandManager.Commands.Add(showCommand);
30
31 }
32
33
8. 编译工程(Build HelloWorld),并将生成的动态库(HelloWorld.dll)复制到AVEVA的安装目录;
9. 修改配置文件,使其加载该插件,修改方法如下图所示:
10. 添加自定义菜单,方便插件的调用。在工具栏的任意位置点击鼠标右键,选择Customize,出现自定义的对话框,并在Active Customization File 的选项框中选择Module,如下图所示:
在Menubar中添加自定义菜单项,将其名称改为MenuTest,如下图所示:
在对话框中间任意位置点鼠标右键,添加一个按钮(button),如下图所示:
修改按钮的标题为ButtonTest,修改其命令(Command),在Core Command中选择我们创建的命令类的Key,如下图所示:
将按钮拖到左边的菜单下后,Apply结束。如下图所示:
11. 自定义后的菜单如下图所示:
八、结论 Conclusion
通过实例对AVEVA .Net的二次开发有了便全面的认识,可以通过自定义菜单方便调用二次开发的插件。自定义菜单或者工具栏按钮主要是通过自定义对话框来实现,不需要程序员手动修改UIC文件,交互操作使用方便。
若有术语用词不当之处,敬请指出。eryar@163.com
九、参考资料 Bibliography
1. AVEVA .Net Customisation User Guide 本文基本上是对这个Guide的部分翻译。
2. GoF book: Design Patterns Elements of Reusable Object-Oriented Software 对Command模式感兴趣的可以一读。
PDF Version: AVEVA .Net Command and Menu Customisation