皆様こんにちは
AMDlabの秋山です。
前回に引き続きZeroTouch Nodeの第6弾です。
今回は、ZeroTouch Nodeを使用して、ドロップダウン付きのノードを作成していきます。
実はDSDropDownBase クラスを継承することで、DynamoノードのUIをカスタマイズすることが可能になります。
今回作成するノードは
1.固定値をドロップダウンにするノード
2.ドアファミリタイプをドロップダウンにするノード
の2つになります。
1.固定値をドロップダウンにするノード
ドロップダウンリストに A, B, C, D という選択肢を表示し、選択された値に応じて整数(0, 1, 2, 3)を出力するノードを作成していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
using System.Collections.Generic; using CoreNodeModels; using Dynamo.Graph.Nodes; using Dynamo.Utilities; using ProtoCore.AST.AssociativeAST; using Newtonsoft.Json; namespace SampleDrop { [NodeName("DropDown")] [NodeDescription("ドロップダウンノードの例")] [IsDesignScriptCompatible] public class DropDownExample : DSDropDownBase { public DropDownExample() : base("item") { } [JsonConstructor] public DropDownExample(IEnumerable<PortModel> inPorts, IEnumerable<PortModel> outPorts) : base("item", inPorts, outPorts) { } protected override SelectionState PopulateItemsCore(string currentSelection) { Items.Clear(); // DynamoDropDownItemオブジェクトを複数作成して、 // リストに表示するアイテムを保存します。 var newItems = new List<DynamoDropDownItem>() { new DynamoDropDownItem("A", 0), new DynamoDropDownItem("B", 1), new DynamoDropDownItem("C",2), new DynamoDropDownItem("D",3) }; Items.AddRange(newItems); // 選択インデックスをデフォルト値(-1)以外に設定します。 // これにより、リストに初期選択状態を設定できます。 SelectedIndex = 0; return SelectionState.Restore; } public override IEnumerable<AssociativeNode> BuildOutputAst(List<AssociativeNode> inputAstNodes) { var intNode = AstFactory.BuildIntNode((int)Items[SelectedIndex].Item); var assign = AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), intNode); return new List<AssociativeNode> { assign }; } } } |
上記のコードを簡単に解説していきます。
① クラスの宣言
ノードの名称はクラス名.メソッド名と説明してきましたが、下記の記述を行えば、クラス名、メソッド名によらずノード名を自由に設定できます。

|
[NodeName("DropDown")] [NodeDescription("ドロップダウンノードの例")] [IsDesignScriptCompatible] public class DropDownExample : DSDropDownBase |
NodeName(“DropDown”) : Dynamo の ノード名 を “DropDown” に設定
NodeDescription(“ドロップダウンノードの例”) : ノードの説明文
DSDropDownBaseクラスはDynamoでドロップダウンノードを作成するためにすでに用意されている基底クラスです。
このクラスを継承することで、ドロップダウンの機能(リストの作成・選択・出力)が自動的に使えるようになります。
② コンストラクタ
コンストラクタ は、クラス(ノード)の初期設定を行うための関数です。
|
public DropDownExample() : base("item") { } [JsonConstructor] public DropDownExample(IEnumerable<PortModel> inPorts, IEnumerable<PortModel> outPorts) : base("item", inPorts, outPorts) { } |
base(“item”) は 親クラス(DSDropDownBase)のコンストラクタを呼び出します。呼び出すことで、Dynamoのドロップダウンリストのアウトプットを “item” と設定し、ノードがDynamoに読み込まれるときにitemとアウトプットに表示されるようになります。

[JsonConstructor]は、ドロップダウンの選択情報や設定が消えてしまうことがあるので、DynamoのJSON形式からノードを復元する際に使用されます。Dynamoでノードを作成し.dyn ファイルに保存をし、Dynamoを再起動してファイルを開くと、JSONデータからノードが復元されます。

③ PopulateItemsCore メソッド(ドロップダウンのリストを作成)
|
protected override SelectionState PopulateItemsCore(string currentSelection) |
機能を実装するには、DSDropDownBaseクラスがもつメソッドを記述する必要があります。今回はドロップダウンのリストを作成するPopulateItemsCore()というメソッドを使用します。
Itemsはドロップダウンのリストになり、既存のリストをすべて削除し、新しいリストを作成できるようにします。
|
var newItems = new List<DynamoDropDownItem>() { new DynamoDropDownItem("A", 0), new DynamoDropDownItem("B", 1), new DynamoDropDownItem("C", 2), new DynamoDropDownItem("D", 3) }; Items.AddRange(newItems); |
ドロップダウンのリストの中身を作成します。
“A” を選択すると 0、”B” を選択すると 1 というように、各項目に数値を割り当てます。
作成したリストをドロップダウンのリストに追加します。
|
SelectedIndex = 0; return SelectionState.Restore; |
SelectedIndex = 0; で 最初の項目(”A”)をデフォルトの選択肢にします。
SelectionState.Restore で Dynamo にリストを更新します。
④ BuildOutputAst メソッド(選択された値を Dynamo に出力)
|
public override IEnumerable<AssociativeNode> BuildOutputAst(List<AssociativeNode> inputAstNodes) { var intNode = AstFactory.BuildIntNode((int)Items[SelectedIndex].Item); var assign = AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), intNode); return new List<AssociativeNode> { assign }; } |
このメソッドは、DSDropDownBaseクラスが持つメソッドで、選択した値をDynamoに出力するものです。
Items[SelectedIndex].Item で、現在選択されている項目の値(0, 1, 2, 3)を取得し、 AstFactory.BuildIntNode(…) でDynamo に数値を出力できる形式に変換します。AstFactory.BuildAssignment(…) で Dynamo に数値を出力するノードを作成し、returnでDynamoに値を渡します。
ユーザーが ドロップダウン上で”C” を選択→BuildOutputAst()
が実行される→"C"
に対応する 2
を Dynamo の出力にする。こんな流れになります。
コーディングが完了したら、Visual Studioでプロジェクトをビルドします。ビルドが成功すると、指定したプロジェクトフォルダ内の「bin\Debug」または「bin\Release」ディレクトリにDLLファイルが生成されます。
Dynamoを開き、[ファイル] タブから「ライブラリを読み込む」を選択し、生成したDLLファイルを選択してインポートします。
インポート後、新しいノード(カスタムノード)として「DropDown」が利用可能になりましたので、配置してみます。

ドロップダウンが作成されました。
2.ドアファミリタイプをドロップダウンにするノード
Revit にロードされているドアファミリとそのタイプ を取得し、ドロップダウンで選択できるようにします。
今回作成するノードになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
|
using System; using System.Collections.Generic; using System.Linq; using Autodesk.Revit.DB; using Autodesk.Revit.UI; using CoreNodeModels; using Dynamo.Graph.Nodes; using Dynamo.Utilities; using ProtoCore.AST.AssociativeAST; using Newtonsoft.Json; using RevitServices.Persistence; using RevitServices.Transactions; using Revit.Elements; // Dynamo の Element を扱うために追加 namespace SampleDrop { [NodeName("DoorFamilyTypesDropdown")] [NodeDescription("Revitのプロジェクト内のドアファミリとそのタイプをプルダウンで選択するノード")] [IsDesignScriptCompatible] public class DoorFamilyTypesDropdown : DSDropDownBase { public DoorFamilyTypesDropdown() : base("Door Family Types") { } [JsonConstructor] public DoorFamilyTypesDropdown(IEnumerable<PortModel> inPorts, IEnumerable<PortModel> outPorts) : base("Door Family Types", inPorts, outPorts) { } protected override SelectionState PopulateItemsCore(string currentSelection) { Items.Clear(); // Revitの現在のドキュメントを取得 Autodesk.Revit.DB.Document doc = DocumentManager.Instance.CurrentDBDocument; if (doc == null) return SelectionState.Restore; try { // ドアファミリシンボル(FamilySymbol)を取得 var doorSymbols = new FilteredElementCollector(doc) .OfClass(typeof(FamilySymbol)) .Cast<FamilySymbol>() .Where(fs => fs.Family.FamilyCategory.Id.IntegerValue == (int)BuiltInCategory.OST_Doors) .ToList(); if (!doorSymbols.Any()) return SelectionState.Restore; // "ファミリ名 - タイプ名" の形式でリスト化 foreach (var symbol in doorSymbols) { string displayName = $"{symbol.Family.Name} - {symbol.Name}"; Items.Add(new DynamoDropDownItem(displayName, symbol)); } SelectedIndex = 0; } catch (Exception ex) { Items.Add(new DynamoDropDownItem($"Error: {ex.Message}", null)); SelectedIndex = -1; return SelectionState.Restore; } return SelectionState.Restore; } public override IEnumerable<AssociativeNode> BuildOutputAst(List<AssociativeNode> inputAstNodes) { if (SelectedIndex < 0 || SelectedIndex >= Items.Count) { return new List<AssociativeNode> { AstFactory.BuildNullNode() }; } // 選択されたドアファミリタイプ(FamilySymbol)を取得 var selectedSymbol = (FamilySymbol)Items[SelectedIndex].Item; // ElementSelector.ByElementId() に long を渡す var elementNode = AstFactory.BuildFunctionCall( new Func<long, Revit.Elements.Element>(Revit.Elements.ElementSelector.ByElementId), new List<AssociativeNode> { AstFactory.BuildIntNode((long)selectedSymbol.Id.IntegerValue) } // long にキャスト ); var assign = AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), elementNode); return new List<AssociativeNode> { assign }; } } } |
上記のコードを簡単に解説していきます。
① クラスの宣言
|
[NodeName("DoorFamilyTypesDropdown")] [NodeDescription("Revitのプロジェクト内のドアファミリとそのタイプをプルダウンで選択するノード")] [IsDesignScriptCompatible] public class DoorFamilyTypesDropdown : DSDropDownBase |
② コンストラクタ
|
public DoorFamilyTypesDropdown() : base("Door Family Types") { } [JsonConstructor] public DoorFamilyTypesDropdown(IEnumerable<PortModel> inPorts, IEnumerable<PortModel> outPorts) : base("Door Family Types", inPorts, outPorts) { } |
ドロップダウンリストのアウトプットを “Door Family Types” と設定します。
③ PopulateItemsCore メソッド(ドロップダウンのリストを作成)
|
protected override SelectionState PopulateItemsCore(string currentSelection) |
Dynamoのドロップダウンリストを作成するためのメソッドです。
|
Autodesk.Revit.DB.Document doc = DocumentManager.Instance.CurrentDBDocument; if (doc == null) return SelectionState.Restore; |
RevitAPIを使用して、Revitの現在のドキュメントを取得します。
|
var doorSymbols = new FilteredElementCollector(doc) .OfClass(typeof(FamilySymbol)) .Cast<FamilySymbol>() .Where(fs => fs.Family.FamilyCategory.Id.IntegerValue == (int)BuiltInCategory.OST_Doors) .ToList(); |
プロジェクト内のドアのファミリシンボルを取得します。
|
foreach (var symbol in doorSymbols) { string displayName = $"{symbol.Family.Name} - {symbol.Name}"; Items.Add(new DynamoDropDownItem(displayName, symbol)); } |
ドロップダウンリストに “ファミリ名-タイプ名” を追加します。
④ BuildOutputAst メソッド(選択された値を Dynamo に出力)
|
public override IEnumerable<AssociativeNode> BuildOutputAst(List<AssociativeNode> inputAstNodes) |
このメソッドは、選択されたドアのファミリタイプをDynamoに出力します。
|
if (SelectedIndex < 0 || SelectedIndex >= Items.Count) { return new List<AssociativeNode> { AstFactory.BuildNullNode() }; } |
何も選択されていない場合は null を出力します。
|
var selectedSymbol = (FamilySymbol)Items[SelectedIndex].Item; |
選択されたFamilySymbolを取得します。
|
var elementNode = AstFactory.BuildFunctionCall( new Func<long, Revit.Elements.Element>(Revit.Elements.ElementSelector.ByElementId), new List<AssociativeNode> { AstFactory.BuildIntNode((long)selectedSymbol.Id.IntegerValue) } ); |
先ほど取得したFamilySymbolをDynamoに出力します。
|
var assign = AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), elementNode); return new List<AssociativeNode> { assign }; |
Dynamo に値を出力します。
コーディングが完了したら、Visual Studioでプロジェクトをビルドします。ビルドが成功すると、指定したプロジェクトフォルダ内の「bin\Debug」または「bin\Release」ディレクトリにDLLファイルが生成されます。
Dynamoを開き、[ファイル] タブから「ライブラリを読み込む」を選択し、生成したDLLファイルを選択してインポートします。
インポート後、新しいノード(カスタムノード)として「DoorFamilyTypesDropdown」が利用可能になりましたので、配置してみます。

このコードを使うことで、Dynamo の “Family Types” ノードのような動作を、ドアファミリ専用 にカスタマイズすることができます。既存の”Family Types” ですとRevitのプロジェクト上にインポートされたファミリがすべてドロップダウン化されてしまうので、取得したいファミリを選択するにはスクロールをする必要が出てきます。ほしいものを探すのに時間がかかるのはストレスです。このノードを開発して、カテゴリごとにドロップダウン化しておくことで時間短縮を見込めますので、このコードをもとに違うカテゴリで皆さんも作成してみてください。
今回は、ZeroTouch Nodeを使用して、ドロップダウン付きのノード作成を説明してきました。
次回は、第7弾でスライダーやボタンを付けるといった、より複雑で便利なノードのUIをカスタマイズするお話をしていきます。
AMDlabでは、開発に力を貸していただけるエンジニアさんを大募集しております。少しでもご興味をお持ちいただけましたら、カジュアルにお話するだけでも大丈夫ですのでお気軽にご連絡ください!
中途求人ページ: https://www.amd-lab.com/recruit-list/mid-career
カジュアル面談がエントリーフォームからできるようになりました。
採用種別を「カジュアル面談(オンライン)」にして必要事項を記載の上送信してください!
エントリーフォーム: https://www.amd-lab.com/entry
COMMENTS