共用方式為


處理網域屬性值變更的程序

在 Visual Studio 網域特定語言中,當網域屬性的值變更時,會在網域屬性處理常式中叫用 和 OnValueChanging()OnValueChanged() 方法。 若要回應變更,您可以覆寫這些方法。

覆寫屬性處理常式方法

網域特定語言的每個網域屬性都由其父網域類別內嵌套的類別來處理。 其名稱遵循 PropertyNamePropertyHandler 格式。 您可以在檔案 Dsl\Generated Code\DomainClasses.cs 中檢查此屬性處理常式類別。 在類別中, OnValueChanging() 在值變更之前立即呼叫, OnValueChanged() 在值變更後立即呼叫。

例如,假設您有一個名為 Comment 的網域類別,其字串網域屬性 Text 名為 ,以及一個名為 TextLengthCount的整數屬性。 若要讓 TextLengthCount always 包含字串的 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

  • 您無法將變更處理常式新增至代表關聯性角色的屬性。 相反地,請在關係類別上定義 an AddRule 和 a DeleteRule 。 這些規則會在建立或變更連結時觸發。 如需詳細資訊,請參閱 規則在 模型內傳播變更

店內和店外的變化

屬性處理常式方法會在起始變更的交易內呼叫。 因此,您可以在商店中進行更多更改,而無需打開新交易。 您的變更可能會導致額外的處理程序被呼叫。

當交易被還原、重做或回滾時,您不應該在存放區中進行變更,也就是不要對模型元素、關係、形狀、連接器圖表或其各自的屬性進行改動。

此外,當您從檔案載入模型時,您通常不會更新值。

因此,對模型的更改應通過如下測試來保護:

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() 將值從一個網域屬性傳播到另一個網域屬性。 每個屬性都有自己的儲存值。

相反地,您可以考慮將衍生屬性定義為計算屬性。 在此情況下,屬性沒有自己的儲存體,而且每當需要其值時,都會評估定義函數。 如需詳細資訊,請參閱 計算和自訂儲存屬性

您可以在 DSL 定義中將 Kind 欄位 TextLengthCount 設定為 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);
      }
    }
  }
}