域属性值更改处理程序

在 Visual Studio 的领域特定语言中,当域属性的值发生更改时,OnValueChanging()OnValueChanged() 方法会在域属性处理程序中被调用。 若要响应更改,可以重写这些方法。

重写属性处理程序方法

特定于域的语言的每个域属性由嵌套在其父域类中的类进行处理。 其名称遵循 PropertyNamePropertyHandler 格式。 可以在文件 Dsl\Generated Code\DomainClasses.cs 中检查此属性处理程序类。 在类中, OnValueChanging() 在值更改之前立即调用,并在 OnValueChanged() 值更改后立即调用。

例如,假设你有一个名为Comment的域类,该类具有一个名为Text的字符串域属性和一个名为TextLengthCount的整数属性。 若要使 TextLengthCount 始终包含 Text 字符串的长度,可以在 Dsl 项目中的独立文件中编写如下代码:

// Domain Class "Comment":
public partial class Comment
{
  // Domain Property "Text":
  partial class TextPropertyHandler
  {
    protected override void OnValueChanging(CommentBase element, string oldValue, string newValue)
    {
      base.OnValueChanging(element, oldValue, newValue);

      // To update values outside the Store, write code here.

      // Let the transaction manager handle undo:
      Store store = element.Store;
      if (store.InUndoRedoOrRollback || store.InSerializationTransaction) return;

      // Update values in the Store:
      this.TextLengthCount = newValue.Length;
    }
  }
}

请注意有关属性处理程序的以下几点:

  • 当用户更改域属性时,以及程序代码向属性分配不同的值时,将调用属性处理程序方法。

  • 仅当值实际更改时,才会调用这些方法。 如果程序代码分配的值等于当前值,则不会调用处理程序。

  • 计算和自定义存储域的属性不包含 OnValueChanged 和 OnValueChanging 方法。

  • 不能使用更改处理程序修改新值。 如果要执行此作,例如,若要将值限制为特定范围,请定义一个 ChangeRule

  • 不能将更改处理程序添加到表示关系角色的属性。 而是在关系类上定义 AddRuleDeleteRule。 创建或更改链接时会触发这些规则。 有关详细信息,请参阅 规则在模型中传播更改

商店内外的变更

属性处理程序方法在启动更改的事务内调用。 因此,你可以在商店中进行更多更改,而无需打开新事务。 您的更改可能会导致额外的处理程序调用。

当事务被撤消、重做或回滚时,不应在存储中进行更改,即对模型元素、关系、形状、连接线关系图或其属性的更改。

此外,从文件加载模型时,通常不会更新值。

因此,模型的更改应通过类似这样的测试来保护。

if (!store.InUndoRedoOrRollback && !store. InSerializationTransaction)
{
   this.TextLength = ...; // in-store changes
}

相比之下,如果属性处理程序将更改传播到存储区外(例如文件、数据库或非存储变量),则应始终进行这些更改,以便在用户调用撤消或重做时更新外部值。

取消更改

如果要阻止更改,可以回滚当前事务。 例如,你可能希望确保属性保留在特定范围内。

if (newValue > 10)
{
   store.TransactionManager.CurrentTransaction.Rollback();
   System.Windows.Forms.MessageBox.Show("Value must be less than 10");
}

替代技术:计算属性

前面的示例演示如何使用 OnValueChanged()将值从一个域属性传播到另一个域属性。 每个属性都有自己的存储值。

相反,可以考虑将派生属性定义为计算属性 (Calculated property)。 在这种情况下,该属性没有自己的存储,并且定义函数是在需要函数值时计算的。 有关详细信息,请参阅 “计算”和“自定义存储属性”。

在 DSL 定义中,可以将TextLengthCountKind字段设置为Calculated,而不是采用之前的示例。 你将为此域属性提供自己的 Get 方法。 Get 方法将返回字符串的Text当前长度。

但是,计算属性的潜在缺点是每次使用值时都会计算表达式,这可能会导致性能问题。 此外,计算属性上没有 OnValueChanging() 和 OnValueChanged()。

替代技术:更改规则

如果定义了 ChangeRule,那么它将在属性值更改的事务结束时执行。 有关详细信息,请参阅 规则在模型中传播更改

如果在一个事务中进行了多个更改,则 ChangeRule 会在全部完成时执行。 相反,当某些更改尚未执行时,将执行 OnValue... 方法。 根据要实现的目标,这可能会使 ChangeRule 更合适。

还可以使用 ChangeRule 调整属性的新值,使其保持在特定范围内。

警告

如果规则对存储内容进行更改,可能会触发其他规则和属性处理程序。 如果规则更改触发它的属性,则会再次调用它。 必须确保规则定义不会导致无限触发。

using Microsoft.VisualStudio.Modeling;
...
// Change rule on the domain class Comment:
[RuleOn(typeof(Comment), FireTime = TimeToFire.TopLevelCommit)]
class MyCommentTrimRule : ChangeRule
{
  public override void
    ElementPropertyChanged(ElementPropertyChangedEventArgs e)
  {
    base.ElementPropertyChanged(e);
    Comment comment = e.ModelElement as Comment;

    if (comment.Text.StartsWith(" ") || comment.Text.EndsWith(" "))
      comment.Text = comment.Text.Trim();
    // If changed, rule will trigger again.
  }
}

// Register the rule:
public partial class MyDomainModel
{
 protected override Type[] GetCustomDomainModelTypes()
 { return new Type[] { typeof(MyCommentTrimRule) };
 }
}

Example

Description

以下示例重写了一个域属性的属性处理程序,并在 ExampleElement 域类的属性发生更改时通知用户。

Code

using DslModeling = global::Microsoft.VisualStudio.Modeling;
using DslDesign = global::Microsoft.VisualStudio.Modeling.Design;

namespace msft.FieldChangeSample
{
  public partial class ExampleElement
  {
    internal sealed partial class NamePropertyHandler
    {
      protected override void OnValueChanged(ExampleElement element,
         string oldValue, string newValue)
      {
        if (!this.Store.InUndoRedoOrRollback)
        {
           // make in-store changes here...
        }
        // This part is called even in undo:
        System.Windows.Forms.MessageBox.Show("Value Has Changed");
        base.OnValueChanged(element, oldValue, newValue);
      }
    }
  }
}