用代码扩展和自定义应用程序模型 |
|
eXpressApp Framework > Concepts > Application Model > Extend and Customize the Application Model in Code
You can customize the information loaded to the Application Model via the Model Editor. It allows you to edit the information at design time, forming Application Model layers in each project. The layers are superimposed onto one another in a certain order. Values from every successive layer override the corresponding values from the previous layer (see the Application Model Basics topic). However, there is one more approach you can use to customize the information stored in the Application Model. In most cases, this approach is less suitable because the changes to be made in the Application Model are performed in code. But, this approach is useful when the feature that you implement in a module demands the modification of the information generated in the Application Model, by default. When implementing a new feature, you may need to add custom nodes to the Application Model. You may also need to customize an existing node by adding custom properties to it. In this way, you can allow other developers or end-users to change the behavior of your code by modifying your custom nodes and properties in the Model Editor. This topic explains how to extend and customize the Application Model in Code.
你可以用模型编辑器自定义应用程序模型信息。你可以在设计时候编辑它,在每个项目形成应用模型层。每层按照一定的顺序叠加。从每一个连续的值将覆盖层从上一层的相应值(见应用模型基础主题)。然而你可以用另外一种方式存储应用程序模型自定义信息。在大多数情况下,这种方法不太合适,因为将要在应用模型中所做的更改执行的代码。但是,这种方法是非常有用的功能,您在一个模块的需求落实在应用模型产生的信息的修改,默认情况下。当实现一个新功能,需要在应用程序模型中添加自定义节点。你也还需要为已有的自定义节点添属性。通过这种方式,允许其它开发者或者终端用户更改你的代码用模型编辑器中自定义节点和属性的操作。这个主题说明如何用代码扩展和自定义应用程序模型。
Several typical scenarios are provided in this topic. You can select one of them or combine them, to handle more complex customizations:
这个主题包含几个典型案例。你可以选择其中一个或者几个用来处理更复杂的自定义。
- Add a Custom Property to the Existing Node
- Extend The Application Model with New Nodes
- Customize the Application Model via the Nodes Generator Class
- Implement a Property Having a List of Predefined Values
- Use the Domain Logic to Implement the Calculated Property
- Add the Generator Updaters
- 为已有的节点添加一个自定义属性
- 用新节点扩展应用程序模型
- 通过自定义应用程序模型节点生成类
- 实现一个有预定义值列表的属性
- 用域逻辑实现可计算属性
- Add the Generator Updaters(找不到合适描述)
If it is required to access properties exposed by an existing node, use the approach described in the Access the Application Model in Code topic.
如果需要访问已有节点的公共属性,请参考用代码访问应用程序模型
Add a Custom Property to the Existing Node
为应用节点添加一个自定义属性
To add new properties to the existing node, you should first define an interface derived from the IModelNode interface, and exposing the required properties:
为已有节点添加新的属性,首先定义一个继承IModelNode接口的接口,并公开所需属性。
| ||||||
using DevExpress.ExpressApp.Model; // ... public interface IModelMyModelExtension : IModelNode { string MyCustomProperty { get; set; } }
|
Then, extend the required node in your Module or Controller. Override the ModuleBase.ExtendModelInterfaces method or implement the IModelExtender interface, respectively.
然后用模块或者控件扩展需求的节点。分别覆写ModuleBase.ExtendModelInterfaces方法或实现IModelExtender接口。
| ||||||
public sealed partial class CustomizeModelExampleModule : ModuleBase { // ... public override void ExtendModelInterfaces(ModelInterfaceExtenders extenders) { base.ExtendModelInterfaces(extenders); extenders.Add<IModelApplication, IModelMyModelExtension>(); } // ... }
|
Rebuild your solution and open the Model Editor, to see how the code above affects the Application Model.
重新编译项目并打开模型编辑器,观察以上代码在应用程序模型中的实现效果。
The IModelApplication node now exposes the MyCustomProperty property. To add the property to another node, substitute the interface corresponding to the required node, instead of the IModelApplication (see Application Model Structure).
IModelApplication节点现在公开MyCustomProperty属性。要添加另一个节点的熟悉,代替接口对应所需节点,而不是IModelApplication(见应用程序模型结构)
You can decorate nodes' properties with the following attributes:
Attribute(特性) |
Description(描述) |
Browsable |
Indicates that a property is visible in the Model Editor. All properties added via the approach described above are visible, by default. To hide a property, decorate it with the Browsable attribute and pass false as the attribute's parameter. 在模型编辑器中指出属性是否显示。默认情况下,所以属性通过这种方式标示是否显示。隐藏用特性Browsable设置参数为false |
Category(分类) |
Specifies the target property category. The properties of each node are grouped by their categories in the Model Editor's property grid. The default category is "Misc". To assign a category to the property, decorate the property with the Category attribute and pass a category name as the attribute's parameter. If the specified category does not exist, it will be added. 指定目标属性分类。每个属性节点分组在模型编辑器属性表格分类中。默认分类是“Misc”。标示属性分类,用一个分类名称参数修饰属性。如果指定的分类不存在,将被添加。 |
DataSourceProperty |
Specifies a name of the property which exposes a list of the target property's possible values. You will be able to choose a value via the drop-down list in the Model Editor's property grid. When the current property's node exposes a list of child nodes (implements the IModelList<ChildNodeType> interface, as illustrated in the Extend The Application Model with New Nodes section), you can pass "this" as the DataSourceProperty attribute parameter. As the result, the child nodes list will be the current property possible values list. 指定一个属性可选的值列表。在模型编辑器中通过下拉列表选择一个值,当当前属性节点公开了子节点列表(实现了IModelList<ChildNodeType>接口,如用新节点扩展应用程序模型图所示部分),你可以用“this”作为DataSourceProperty特性的参数。返回结果,子节点列表将用作当前属性可选值列表。 |
DefaultValue |
Specifies the target property's default value. |
Editor(编辑) |
Binds a custom editor to the property (see EditorAttribute). 为属性绑定一个自定义编辑(见EditorAttribute)。 |
Description(描述) |
Specifies the target property's description displayed in the Model Editor. 指定在模型编辑器中显示目标属性的描述。 |
Localizable(本地化) |
Specifies that the target property can be localized. To define a localizable property, decorate it with the Localizable attribute and pass true as the attribute's parameter. 是否本地化目标属性。如果需要本地化目标属性,则设置Localizable特性参数为“true”. |
ReadOnly(只读) |
To prohibit property value modification via the Model Editor, decorate the property with the ReadOnly attribute and pass true as the attribute's parameter. Alternatively, you can omit the property's setter. But, the ReadOnly attribute affects only the Model Editor, and does not restrict the property modification in code. 禁止用模型编辑器修改属性值,用ReadOnly特性修饰,并设置特性参数为“true”。或者你省略属性的写操作。但是,ReadOnly只影响模型编辑器,不作用于代码修改目标属性。 |
Required(必须) |
Indicates that a target property should have a value. The Model Editor will not allow you to save changes until you supply this property value. 表明一个目标属性有一个值。模型编辑器不允许属性值为空。 |
Examples of some of these attributes in use will be provided later in this topic.
下面用一些特性的列子.
|
Do not create the property named "Application". Otherwise, this property will hide the node's inherited IModelNode.Application property. Hiding the Application property causes the "Cannot compile the generated code" exception. 不能用名称为”Application”的属性。否则,这个属性将隐藏继承IModelNode.Application属性。隐藏应用程序属性会导致“不能编译生成的代码”的异常。 |
Extend The Application Model with New Nodes(用新节点扩展应用程序模型)
The extension of the Application Model with new nodes is performed similarly to adding new properties. First, you should define interfaces representing your custom nodes and their properties:
用新节点扩展应用程序模型跟添加新属性相似。首先,定义接口描述你的自定义节点和他们的属性。
| ||||||
using System.ComponentModel; // ... [KeyProperty("Name")] public interface IModelMyChildNode : IModelNode { string Name { get; set; } [Localizable(true)] string MyStringProperty { get; set; } int MyIntegerProperty { get; set; } } public interface IModelMyNodeWithChildNode : IModelNode { IModelMyChildNode MyChildNode { get; } } public interface IModelMyNodeWithChildNodes : IModelNode, IModelList<IModelMyChildNode> { }
|
As you can see, each custom node is derived from the IModelNode interface. The IModelMyNodeWithChildNode node exposes a single IModelMyChildNode child node.
正如你所看到的,每个自定义节点都继承于IModelNode接口。IModelMyNodeWithChildNode公开了一个IModelMyChildNode子节点。
|
When determining whether a property will be a child node or value, the existence of the setter in its declaration is taken into account. If the property has a setter, then it will not automatically be filled with values, and vice versa. So, the MyChildNode property has no setter (marked as ReadOnly in VB). 在判断一个属性是否是一个子节点或值,要考虑定义写属性。如果这个属性有一个可写,那么他的值不会自动填充,反之亦然。因此,MyChildNode属性没有可写(在VB中标志用ReadOnly) |
The IModelMyNodeWithChildNodes node exposes the list of IModelMyChildNode nodes, as it supports the IModelList<IModelMyChildNode> interface.
IModelMyNodeWithChildNodes公开了IModelMyChildNode节点列表,因为他支持IModelList<IModelMyChildNode> 接口。
You can decorate interfaces, representing nodes, with the following attributes:
你可以用下列特性修饰接口,节点。
Attribute(特性) |
Description(描述) |
DisplayName |
Specifies the node's display name, which is used as the node's caption in the Model Editor. 指定一个节点的显示名称,这是用作模型编码器中用节点的标题。 |
DisplayProperty |
Specifies the node's property name, whose value is used as the node's caption in the Model Editor. |
ImageName |
Specifies the node's image, which is used in the Model Editor. See the ImageNameAttribute topic for additional information. |
KeyProperty |
Specifies the node's key property. The key property is used to identify nodes. Only a string property can be a key property. If the KeyProperty attribute is omitted, the Id key property is generated automatically. The use of this attribute is illustrated in the code above. 指定节点的关键属性。关键属性是用来识别节点。只用字符串属性才能用作关键属性。如果忽略,ID关键属性自动生成。上面代码演示了这个作用。 |
To add these nodes to a certain parent node in the Application Model, extend the required node's interface, as described in the Add a Custom Property to the Existing Node section of this topic.
在应用程序模型中为某一个父节点添加这些节点,扩展需要的节点的接口,见为存在的节点添加自定义属性。
| ||||||
public interface IModelMyModelExtension : IModelNode { // ... IModelMyNodeWithChildNode MyNodeWithOneChildNode { get; } IModelMyNodeWithChildNodes MyNodeWithSeveralChildNodes { get; } } // ... public override void ExtendModelInterfaces(ModelInterfaceExtenders extenders) { base.ExtendModelInterfaces(extenders); extenders.Add<IModelApplication, IModelMyModelExtension>(); }
|
Rebuild your solution and open the Model Editor to see how the code above affects the Application Model.
重新不要你的项目,并打开模型编辑器,看到如下结果。
You can add child nodes to the MyNodeWithSeveralChildNodes node via the context menu.
你可以通过上下文菜单为MyNodeWithSeveralChildNodes添加子节点。
Customize the Application Model via the Nodes Generator Class
通过自定义应用程序模型节点生成类
In some scenarios, it is required to implement logic to be executed when the custom node is added to the Application Model. The so-called Generator classes serve this purpose. The Generator is the ModelNodesGeneratorBase descendant. The ModelNodesGeneratorBase class exposes the GenerateNodesCore virtual method. To implement the required logic, override this method. To access the node for which the generator is invoked, use the method's node parameter. The following snippet illustrates Generator implementation.
在某些情况下,当给应用程序模型添加自定义节点,它需要逻辑实现。所谓生成器类能达到这个目的。生成器是ModelNodesGeneratorBase的后代。ModelNodesGeneratorBase公开了虚方法GenerateNodesCore。覆写该虚方法,实现需求逻辑。用生成器访问节点,用方法的节点参数。下面演示生成器的实现。
| ||||||
using DevExpress.ExpressApp.Model.Core; // ... public class MyChildNodesGenerator : ModelNodesGeneratorBase { protected override void GenerateNodesCore(ModelNode node) { for (int i = 0; i < 10; i++) { string childNodeName = "MyChildNode " + i.ToString(); node.AddNode<IModelMyChildNode>(childNodeName); node.GetNode(childNodeName).Index = i; } } }
|
|
The node parameter exposes the Application property, providing you with access to the whole Application Model. |
This Generator creates ten IModelMyChildNode nodes and assigns indexes to them. To apply the Generator to the required node, use the ModelNodesGenerator attribute:
这个生成器建立十个IModelMyChildNode节点并标出索引。要应用生成所需节点,请用ModelNodesGenerator 特性
| ||||||
[ModelNodesGenerator(typeof(MyChildNodesGenerator))] public interface IModelMyNodeWithChildNodes : IModelNode, IModelList<IModelMyChildNode> { }
|
Each Node can have only one generator. However, additional customizations can be performed via the Generator Updaters
每个节点只有一个生成器。然而,通过Generator Updaters可以实现另外的自定义。
Rebuild your solution and open the Model Editor, to see how the code above affects the Application Model.
|
The Generator affects the Application Model zero layer. So, the GenerateNodesCore method will be automatically executed on demand, when the data stored in the MyNodeWithSeveralChildNodes is required for the first time. For details on the Model layers, refer to the Application Model Basics topic. To ensure that the generator is executed only once, you can do the following: 生成器作用于应用程序模型零层。因此,GenerateNodesCore方法将被按需执行,第一次必须存储MyNodeWithSeveralChildNodes数据。有关模型层的详细情况,请参考应用程序模型基础主题。为了确保生成器只被生成一次,你可以按照以下方法做:
|
Implement a Property Having a List of Predefined Values
实现一个拥有预定义值列表的属性
Let's assume that the IModelMyNodeWithChildNodes node should expose the SelectedChildNode property, representing the "selected" child node. The user-friendly approach is to provide a drop-down, allowing you to choose one of the valid values in the Model Editor. So, the property should have a list of predefined values. This property can be implemented in the following manner:
假设IModelMyNodeWithChildNodes节点应公开SelectedChildNode属性,代表选择的子节点。为用户提供最好的方法是下列,在模型编辑器中允许选择也该可用值。因此,这个属性应有一个预定义的值列表。这个属性的实现方式如下:
| ||||||
using DevExpress.Persistent.Base; // ... public interface IModelMyNodeWithChildNodes : IModelNode, IModelList<IModelMyChildNode> { [DataSourceProperty("this")] IModelMyChildNode SelectedChildNode { get; set; } }
|
Generally the DataSourceProperty attribute can be used to specify a name of the current node's property which exposes a list of the target property's possible values. Additionally, you can pass "this" as the DataSourceProperty attribute parameter (as illustrated in the code above). In this case, a list of the target property's possible values will be filled with the current node's child nodes.
一般来说DataSourceProperty特性可被用于给节点属性提供可用值列表。另外你可用“this”作为DataSourceProperty特性的参数(如图上代码所示)。这样,当前节点的子节点填充到目标属性的可用值列表。
Rebuild your solution, and open the Model Editor to see how the code above affects the Application Model.
重新编译项目并打开模型编辑器,看到如下结果。
You can add and remove child nodes, and the drop-down list will reflect changes instantly.
|
To define a more complex logic for generating the list or predefined values, implement the supplementary property holding the required list, as described in the text section of this topic. Then, make this property hidden, by applying the Browsable(false) attribute to it. Finally, decorate your target property with the DataSourceProperty attribute and pass the supplementary hidden property name as the parameter. Note that the list items must be of the same type as the target property. 要定义一个更复杂的生成列表或预定义值逻辑,实现属性持有所需的补充列表,参考本主题文本所述。然后,使用Browsable(false)特性隐藏这个属性。最后,用DataSourceProperty修饰目标属性并作为参数补充隐藏属性名称。注意作为目标属性列表属性必须是相同类型。 |
Use the Domain Logic to Implement the Calculated Property
用域逻辑实现可计算属性
To customize the node's properties behavior, the Domain Logic (which is a part or the Domain Components technology) can be used. The common use case of Domain Logic is implementing calculated properties. The following code illustrates how to implement the NumberOfChildNodes property, which gets the number of current node's child nodes.
用域逻辑(域组件)实现自定义节点的属性行为。通常域逻辑实现可计算属性。下面代码演示如何实现NumberOfChildNodes属性,计算当前节点的子节点个数。
| ||||||
using DevExpress.ExpressApp.DC; // ... public interface IModelMyNodeWithChildNodes : IModelNode, IModelList<IModelMyChildNode> { // ... int NumberOfChildNodes { get; } } // ... [DomainLogic(typeof(IModelMyNodeWithChildNodes))] public class MyNodeWithChildNodesLogic { public static int Get_NumberOfChildNodes( IModelMyNodeWithChildNodes modelMyNodeWithChildNodes) { return modelMyNodeWithChildNodes.NodeCount; } }
|
The DomainComponentAttribute in the code above indicates that the MyNodeWithMultipleChildNodesLogic class represents the Domain Logic for the IModelMyNodeWithChildNodes node. This class exposes the Get_NumberOfChildNodes method, executed when getting the NumberOfChildNodes property value. This method returns the number of IModelMyChildNode child nodes exposed by the IModelMyNodeWithChildNodes node. For details on Domain Logic, refer to the Domain Components Basics topic.
在上面的代码DomainComponentAttribute表明MyNodeWithMultipleChildNodesLogic类代表的IModelMyNodeWithChildNodes节点域逻辑。这个类公开Get_NumberOfChildNodes方法,执行时获取NumberOfChildNodes属性值。此方法返回IModelMyChildNode子节点的情况,IModelMyNodeWithChildNodes节点暴露节点的数目。有关域逻辑的细节,请参考域组件基本主题。
Rebuild your solution, and open the Model Editor to see how the code above affects the Application Model.
重新编译项目并打开模型编辑器,看到如下结果。
You can add and remove child nodes and the NumberOfChildNodes property value will reflect changes instantly.
你可用添加或者移除子节点,下列列表立即发生改变。
Each node can have a single Generator assigned. However, you can "attach" one or more Updaters to the Generator. The Generator Updater is the ModelNodesGeneratorUpdater<T> class' descendant. The ModelNodesGeneratorUpdater<T> class exposes the UpdateNode virtual method. To implement the required logic, override this method. To access the node for which the Updater is invoked, use the method's node parameter. The following snippet illustrates the implementation of two Generator Updaters.
每个节点有一个生成器分配。然而,你可以附加一个或多个Updaters给生成器。这个生成器Updater是ModelNodesGeneratorUpdater<T>类的后代。ModelNodesGeneratorUpdater<T> 类公开虚方法UpdateNode。覆写这个方法实现需求逻辑。要访问该更新被调用时,使用该方法的节点参数节点。下面的代码片段说明了两个生成器更新者的执行情况。
| ||||||
public class MyChildNodesUpdater1 : ModelNodesGeneratorUpdater<MyChildNodesGenerator> { public override void UpdateNode(ModelNode node) { foreach (IModelMyChildNode childNode in ((IModelMyNodeWithChildNodes)node)) { childNode.MyIntegerProperty = childNode.Index + 1; } } } public class MyChildNodesUpdater2 : ModelNodesGeneratorUpdater<MyChildNodesGenerator> { public override void UpdateNode(ModelNode node) { ((IModelMyNodeWithChildNodes)node).SelectedChildNode = "MyChildNode 9"; } }
|
|
The node parameter exposes the Application property, providing you with access to the whole Application Model. |
The first Updater sets the IModelMyChildNode.MyIntegerProperty values. The second sets the IModelMyNodeWithChildNodes.SelectedChildNode property value. All the Updaters should be registered in the overridden ModuleBase.AddGeneratorUpdaters method:
第一次更新设置IModelMyChildNode.MyIntegerProperty值。第二次设置IModelMyNodeWithChildNodes.SelectedChildNode属性值。所有的更新都需覆写ModuleBase.AddGeneratorUpdaters方法注册。
| ||||||
public sealed partial class CustomizeModelExampleModule : ModuleBase { // ... public override void AddGeneratorUpdaters(ModelNodesGeneratorUpdaters updaters) { base.AddGeneratorUpdaters(updaters); updaters.Add(new MyChildNodesUpdater1()); updaters.Add(new MyChildNodesUpdater2()); } // ... }
|
Rebuild your solution and open the Model Editor, to see how the code above affects the Application Model.
重新编译项目并打开模型编辑器,看到如下结果。
|
If you previously modified these property values in the Model Editor, you should reset differences for the MyNodeWithSeveralChildNodes node to see the changes. To do this,right-click this node and select Reset Differences. The reason for this is that the Updaters operate at the Application Model zero layer, and changes can be overridden in higher layers. |
To see another example of a custom Generator Updater, refer to the EnumDescriptor.GenerateDefaultCaptions method description.
看其他自定义生成器更新者的例子,请参考EnumDescriptor.GenerateDefaultCaptions方法描述。
欢迎转载,转载请注明出处:http://www.cnblogs.com/Tonyyang/