更新形状和连接线以反映模型

在 Visual Studio 中特定于域的语言中,你可以使形状的外观反映基础模型的状态。

本主题中的代码示例应添加到 .cs 项目的文件 Dsl 中。 每个文件中都需要以下指令:

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;

设置形状地图属性以控制修饰器的可见性

可以通过在 DSL 定义中配置形状和域类之间的映射来控制修饰器的可见性,而无需编写程序代码。 有关详细信息,请参阅 “如何定义 Domain-Specific 语言”。

将形状的颜色和样式公开为属性

在 DSL 定义中,右键点击形状类,指向 添加暴露项,然后单击某个项目,例如 填充颜色

该形状现在具有一个可以在程序代码中或由用户设置的域属性。 例如,若要在命令或规则的程序代码中设置它,可以编写:

shape.FillColor = System.Drawing.Color.Red;

如果只想在程序控制下而不是由用户设置属性变量,请在 DSL 定义关系图中选择新的域属性,例如 填充颜色 。 然后,在“属性”窗口中,将 Is Browsable 设置为 false 或将 Is UI Readonly 设置为 true

定义更改规则,使颜色、样式或位置取决于模型元素属性。

可以定义规则,更新形状的外观,这取决于模型其他部分的变化。 例如,可以在模型元素上定义更改规则,该规则更新其形状的颜色,具体取决于模型元素的属性。 有关更改规则的详细信息,请参阅 规则在模型中传播更改

应仅使用规则来更新在应用商店中维护的属性,因为执行撤消命令时不会调用规则。 这不包括一些图形功能,例如形状的大小和可见性。 若要更新形状的这些特性,请参阅 更新非存储图形特性

以下示例假定您已按上一部分所述,将 `FillColor` 公开为域属性。

[RuleOn(typeof(ExampleElement))]
  class ExampleElementPropertyRule : ChangeRule
  {
    public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e)
    {
      base.ElementPropertyChanged(e);
      ExampleElement element = e.ModelElement as ExampleElement;
      // The rule is called for every property that is updated.
      // Therefore, verify which property changed:
      if (e.DomainProperty.Id == ExampleElement.NameDomainPropertyId)
      {
        // There is usually only one shape:
        foreach (PresentationElement pel in PresentationViewsSubject.GetPresentation(element))
        {
          ExampleShape shape = pel as ExampleShape;
          // Replace this with a useful condition:
          shape.FillColor = element.Name.EndsWith("3")
                     ? System.Drawing.Color.Red : System.Drawing.Color.Green;
        }
      }
    }
  }
  // The rule must be registered:
  public partial class OnAssociatedPropertyExptDomainModel
  {
    protected override Type[] GetCustomDomainModelTypes()
    {
      List<Type> types = new List<Type>(base.GetCustomDomainModelTypes());
      types.Add(typeof(ExampleElementPropertyRule));
      // If you add more rules, list them here.
      return types.ToArray();
    }
  }

使用 OnChildConfigured 初始化形状的属性

若要在首次创建形状时设置形状的属性,请在图表类的部分定义中重写 OnChildConfigured() 。 关系图类在 DSL 定义中指定,生成的代码位于 Dsl\Generated Code\Diagram.cs中。 例如:

partial class MyLanguageDiagram
{
  protected override void OnChildConfigured(ShapeElement child, bool childWasPlaced, bool createdDuringViewFixup)
  {
    base.OnChildConfigured(child, childWasPlaced, createdDuringViewFixup);
    ExampleShape shape = child as ExampleShape;
    if (shape != null)
    {
      if (!createdDuringViewFixup) return; // Ignore load from file.
      ExampleElement element = shape.ModelElement as ExampleElement;
      // Replace with a useful condition:
      shape.FillColor = element.Name.EndsWith("3")
          ? System.Drawing.Color.Red : System.Drawing.Color.Green;
    }
    // else deal with other types of shapes and connectors.
  }
}

此方法可用于域属性和非存储功能,例如形状的大小。

使用 AssociateValueWith() 更新形状的其他功能

对于形状的某些功能(例如,它是否具有阴影或连接线的箭头样式),没有将该功能公开为域属性的内置方法。 对此类功能的更改不受事务系统的控制。 因此,使用规则进行更新是不合适的,因为当用户执行撤消命令时不会调用规则。

相反,可以使用 OnAssociatedPropertyChanged 更新此类功能。 在以下示例中,连接器的箭头样式由连接器显示的关系中的域属性的值控制:

public partial class ArrowConnector // My connector class.
{
    /// <summary>
    /// Called whenever a registered property changes in the associated model element.
    /// </summary>
    /// <param name="e"></param>
    protected override void OnAssociatedPropertyChanged(VisualStudio.Modeling.Diagrams.PropertyChangedEventArgs e)
    {
      base.OnAssociatedPropertyChanged(e);
      // Can be called for any property change. Therefore,
      // Verify that this is the property we're interested in:
      if ("IsDirected".Equals(e.PropertyName))
      {
        if (e.NewValue.Equals(true))
        { // Update the shape's built-in Decorator feature:
          this.DecoratorTo = LinkDecorator.DecoratorEmptyArrow;
        }
        else
        {
          this.DecoratorTo = null; // No arrowhead.
        }
      }
    }

    // OnAssociatedPropertyChanged is called only for properties
    // that have been registered using AssociateValueWith().
    // InitializeResources is a convenient place to call this.
    // This method is invoked only once per class, even though
    // it is an instance method.
    protected override void InitializeResources(StyleSet classStyleSet)
    {
      base.InitializeResources(classStyleSet);
      AssociateValueWith(this.Store, Wire.IsDirectedDomainPropertyId);
      // Add other properties here.
    }
}

AssociateValueWith() 对于要注册的每个域属性,应调用一次。 调用该属性后,对指定属性的任何更改都将在呈现该属性的模型元素的任何形状中调用 OnAssociatedPropertyChanged()

不需要为每个实例调用 AssociateValueWith() 。 尽管 InitializeResources 是一个实例方法,但每个形状类只调用一次。