EPR类企业管理系统

在我们现有系统基础上或全新开发,提供定制服务
为您的企业高效快速实施ERP,WMS,MES,CRM管理系统
全面管控物料仓库、销售业务、采购业务、仓库业务
生产过程、质量检验、组织架构、业务报表


定制
QQ:460-3528

开发
QQ群:3360-90194

源码
微信:136-3650-3721

扩展和自定义代码中的应用程序模型

您可以通过模型编辑器来自定义加载到应用程序模型的信息。它使您可以在设计时编辑信息,从而在每个项目中形成“应用程序模型”层。这些层以一定顺序彼此叠加。每个连续层中的值都将覆盖上一层中的相应值(请参见“应用程序模型基础”话题)。您可以使用另一种方法来自定义应用程序模型中存储的信息。在大多数情况下,此方法不太合适,因为在应用程序模型中进行的更改是用代码执行的。但是,当您在模块中实现的功能默认需要修改“应用程序模型”中生成的信息时,此方法很有用。实施新功能时,可能需要向应用程序模型添加自定义节点。您可能还需要通过向现有节点添加自定义属性来对其进行自定义。这样,您可以允许其他开发人员或最终用户通过在模型编辑器中修改自定义节点和属性来更改代码的行为。本主题说明如何在代码中扩展和自定义应用程序模型。要了解“应用程序模型”应用程序模型结构主题。

本主题提供了几种典型方案。您可以选择其中之一,也可以组合使用它们来处理更复杂的自定义。

提示

如果需要访问现有节点公开的属性,请使用在代码中访问应用程序模型主题中描述的方法。

重点

始终使用节点生成器生成器更新程序来自定义当前节点及其子节点。要更新当前节点层次结构之外的节点,请创建单独的Node GeneratorsGenerator Updaters

将自定义属性添加到现有节点

要将新属性添加到现有节点,应首先定义一个接口并公开所需的属性:

using DevExpress.ExpressApp.Model;
// ...
public interface IModelMyModelExtension : IModelNode {
    string MyCustomProperty { get; set; }
}

然后,在ModuleController中扩展所需的节点。重写ModuleBase.ExtendModelInterfaces方法或分别实现IModelExtender接口。

using DevExpress.ExpressApp.Model;
// ...
public sealed partial class CustomizeModelExampleModule : ModuleBase {
    // ...
    public override void ExtendModelInterfaces(ModelInterfaceExtenders extenders) {
        base.ExtendModelInterfaces(extenders);
        extenders.Add<IModelApplication, IModelMyModelExtension>();
    }
    // ...
}

重建您的解决方案并打开“模型编辑器”,以查看上面的代码如何影响应用程序模型。

CustomizeApplicationModelInCode_1

IModelApplication节点现在公开的MyCustomProperty财产。要将属性添加到另一个节点,请替换与所需节点相对应的接口,而不是IModelApplication(请参见Application Model Structure)。

您可以使用以下属性装饰节点属性。

属性 描述
可浏览 指示属性在模型编辑器中可见。默认情况下,通过上述方法添加的所有属性都是可见的。要隐藏属性,请使用Browsable属性对其进行装饰,然后将false用作该属性的参数。
类别 指定目标属性类别。每个节点的属性在“模型编辑器”的属性网格中按类别进行分组。默认类别为“其他”。要将类别分配给属性,请使用Category属性装饰属性,并传递类别名称作为该属性的参数。如果指定的类别不存在,则将其添加。
数据源属性 指定属性的名称,该名称公开目标属性的可能值的列表。您将可以通过“模型编辑器”的属性网格中的下拉列表选择一个值。当当前属性的节点公开子节点列表(实现IModelList < ChildNodeType >接口,如使用新节点扩展应用程序模型部分中所示)时,可以将“ this”作为DataSourceProperty属性参数传递。结果,子节点列表将是当前属性可能值列表。
默认值 指定目标属性的默认值。
编辑 将自定义编辑器绑定到属性(请参见EditorAttribute)。
描述 指定在模型编辑器中显示的目标属性的描述。
可本地化 指定可以对目标属性进行本地化。要定义可本地化的属性,请使用Localizable属性对其进行修饰,然后将true用作该属性的参数。
只读 要禁止通过模型编辑器修改属性值,请使用ReadOnly属性装饰属性,并将true作为属性的参数传递。或者,您可以省略属性的设置器。但是,ReadOnly属性仅影响模型编辑器,而不限制代码中的属性修改。
必需的 指示目标属性应具有值。在提供此属性值之前,“模型编辑器”将不允许您保存更改。

本主题的稍后部分将提供一些使用中的某些属性的示例。

注意

不要创建名为“应用程序”的属性。否则,此属性将隐藏节点的继承的IModelNode.Application属性。隐藏Application属性会导致“无法编译生成的代码”异常。

用新节点扩展应用程序模型

  • 使用新节点扩展应用程序模型的过程与添加新属性的过程类似。首先,您应该定义代表您的自定义节点及其属性的接口。请注意,在定义指定单独节点的接口时,应从IModelNode接口派生。

    using System.ComponentModel;
    // ...
    [KeyProperty(nameof(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> {
    }
    

    如您所见,每个自定义节点都是从IModelNode接口派生的。该IModelMyNodeWithChildNode节点显示了单一IModelMyChildNode子节点。

    注意

    在确定某个属性将是子节点还是值时,要考虑到其声明中setter的存在。如果属性具有设置器,则不会自动将其填充值,反之亦然。因此,MyChildNode属性没有设置器(在VB中标记为ReadOnly)。

    所述IModelMyNodeWithChildNodes节点显示的列表中IModelMyChildNode节点,因为它支持IModelList <IModelMyChildNode>接口。

    您可以使用以下属性来装饰代表节点的接口。

    属性 描述
    描述 指定在模型编辑器中显示的目标节点的描述。
    显示名称 指定节点的显示名称,该名称在模型编辑器中用作节点的标题。
    显示属性 指定节点的属性名称,其值在模型编辑器中用作节点的标题。
    图片名称 指定在模型编辑器中使用的节点图像。有关其他信息,请参见ImageNameAttribute主题。
    关键属性 指定节点的key属性。key属性用于标识节点。只有字符串属性可以是键属性。如果省略KeyProperty属性,则将自动生成Id键属性。上面的代码中说明了此属性的用法。

    要将这些节点添加到应用程序模型中的某个父节点,请扩展所需节点的接口,如本主题的“向现有节点添加自定义属性”部分中所述。

    public interface IModelMyModelExtension : IModelNode {
        // ...
        IModelMyNodeWithChildNode MyNodeWithOneChildNode { get; }
        IModelMyNodeWithChildNodes MyNodeWithSeveralChildNodes { get; }
    }
    // ...
    public override void ExtendModelInterfaces(ModelInterfaceExtenders extenders) {
        base.ExtendModelInterfaces(extenders);
        extenders.Add<IModelApplication, IModelMyModelExtension>();
    }
    

    重建您的解决方案并打开“模型编辑器”,以查看上面的代码如何影响应用程序模型。

    CustomizeApplicationModelInCode_2

    您可以通过上下文菜单将子节点添加到MyNodeWithSeveralChildNodes节点。

    CustomizeApplicationModelInCode_3

通过节点生成器类自定义应用程序模型

在某些情况下,需要实现将自定义节点添加到应用程序模型时要执行的逻辑。所谓的生成器类用于此目的。生成器是ModelNodesGeneratorBase后代。所述ModelNodesGeneratorBase类公开GenerateNodesCore虚拟方法。要实现所需的逻辑,请重写此方法。要访问为其调用生成器的节点,请使用该方法的node参数。以下代码段说明了Generator的实现。

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;
        }
    }
}
注意

节点参数暴露了应用性能,为您提供访问整个应用模式。

该生成器创建十个IModelMyChildNode节点,并为其分配索引。要将Generator应用于所需的节点,请使用ModelNodesGeneratorAttribute属性。

[ModelNodesGenerator(typeof(MyChildNodesGenerator))]
public interface IModelMyNodeWithChildNodes : IModelNode, IModelList<IModelMyChildNode> {
}

每个节点只能有一个生成器。但是,可以通过生成器更新程序执行其他自定义

重建您的解决方案并打开“模型编辑器”,以查看上面的代码如何影响应用程序模型。

CustomizeApplicationModelInCode_4

注意

生成器影响应用程序模型零层。因此,当首次需要存储在MyNodeWithSeveralChildNodes中的数据时,GenerateNodesCore方法将根据需要自动执行。有关模型层的详细信息,请参阅“应用程序模型基础”主题。为确保生成器仅执行一次,您可以执行以下操作。

  • GenerateNodesCore方法中设置断点。
  • 运行Windows窗体应用程序。
  • 调用运行时模型编辑器。应用程序的执行将被中断。
  • 继续执行应用程序,然后关闭“模型编辑器”。
  • 再次调用运行时模型编辑器。应用程序的执行不会被中断。此时,应用程序模型的零层已包含所有必需的信息,因此无需再次调用Generator。

实现具有预定义值列表的属性

假定IModelMyNodeWithChildNodes节点应公开SelectedChildNode属性,该属性表示“选定的”子节点。用户友好的方法是提供一个下拉菜单,允许您在“模型编辑器”中选择有效值之一。因此,该属性应具有预定义值的列表。可以按以下方式实现此属性。

using DevExpress.Persistent.Base;
// ...
public interface IModelMyNodeWithChildNodes : IModelNode, IModelList<IModelMyChildNode> {
    [DataSourceProperty("this")]
    IModelMyChildNode SelectedChildNode { get; set; }
}

通常,可以使用DataSourceProperty属性指定当前节点属性的名称,该名称公开目标属性的可能值的列表。此外,您可以将“ this”作为DataSourceProperty属性参数传递(如上面的代码所示)。在这种情况下,将使用当前节点的子节点填充目标属性的可能值列表。

重建您的解决方案,然后打开“模型编辑器”以查看上面的代码如何影响应用程序模型。

CustomizeApplicationModelInCode_5

您可以添加和删除子节点,下拉列表将立即反映更改。

注意

要定义用于生成列表或预定义值的更复杂的逻辑,请实现包含所需列表的补充计算属性,如本主题的文本部分所述。然后,通过将Browsable(false)属性应用于该属性来使其隐藏。最后,用DataSourceProperty属性装饰目标属性,并将补充的隐藏属性名称作为参数传递。请注意,列表项必须与目标属性具有相同的类型。

添加发电机更新器

每个节点可以分配一个发电机。但是,您可以将一个或多个更新程序“附加”到生成器。生成器更新程序是ModelNodesGeneratorUpdater <T>类的后代。此类公开IModelNodesGeneratorUpdater.UpdateNode虚拟方法。要实现所需的逻辑,请重写此方法。要访问为其调用Updater的节点,请使用方法的node参数。以下代码段说明了两个Generator Updaters的实现。

public class MyChildNodesUpdater1 : ModelNodesGeneratorUpdater<MyChildNodesGenerator> {
    public override void UpdateNode(ModelNode node) {
        foreach (IModelMyChildNode childNode in ((IModelMyNodeWithChildNodes)node)) {
            if (childNode.Index.HasValue) {
                    childNode.MyIntegerProperty = (int)childNode.Index   1;
            }
        }
    }
}
public class MyChildNodesUpdater2 : ModelNodesGeneratorUpdater<MyChildNodesGenerator> {
    public override void UpdateNode(ModelNode node) {
        ((IModelMyNodeWithChildNodes)node).SelectedChildNode =
            ((IModelMyNodeWithChildNodes)node)["MyChildNode 9"];
    }
}
注意

节点参数暴露了应用性能,为您提供访问整个应用模式。

第一个更新程序设置IModelMyChildNode.MyIntegerProperty值。第二个设置IModelMyNodeWithChildNodes.SelectedChildNode属性值。所有更新程序都应在重写的ModuleBase.AddGeneratorUpdaters方法中注册。

using DevExpress.ExpressApp.Model.Core;
// ...
public sealed partial class CustomizeModelExampleModule : ModuleBase {
    // ...       
    public override void AddGeneratorUpdaters(ModelNodesGeneratorUpdaters updaters) {
        base.AddGeneratorUpdaters(updaters);
        updaters.Add(new MyChildNodesUpdater1());
        updaters.Add(new MyChildNodesUpdater2());
    }
    // ...
}

重建您的解决方案并打开“模型编辑器”,以查看上面的代码如何影响应用程序模型。

CustomizeApplicationModelInCode_6

如果以前在模型编辑器中修改了这些属性值,则应重置MyNodeWithSeveralChildNodes节点的差异以查看更改。为此,请右键单击该节点,然后选择重置差异。这样做的原因是,更新程序在应用程序模型零层运行,而更改可以在较高层中覆盖。

重点

节点生成器和节点更新器在零层工作,因此无法访问驻留了克隆/复制的节点或其他模型差异的更高的应用程序模型层。另外,此时所做的所有自定义操作都不会保存在用户模型差异中。要根据模型差异定制节点,请使用“如何全局或针对多个节点更改默认应用程序模型”值中的方法。

上面的示例说明了如何自定义自定义节点生成器的行为。但是,您可以使用相同的方法将“生成器更新程序”“附加”到内置的Nodes Generators上。以下主题提供了为内置Generator实施Generator Updaters的完整示例。

相关文章

转载保留此链接,注明出处