はじめに
AMDlab BIMエンジニアの河野(twitter@tatsukikouno)です。
RevitAPIで学ぶC#シリーズ、第4弾です。
前回はRevitアドインで利用できるUIを作成しました。
UIと言っても、Revitドキュメント内のElementの個数を表示するだけでした。
今回は入力可能なUIを用意し、入力内容を利用したコマンドを実装してみましょう。
前回実装したコードを再利用します。
アドインの構築等も前回記事を参考にしてください。
Viewの変更
今回用意する入力可能なUIコントロールは、
- 検索するファミリ名
- 完全一致検索するかどうか
- 検索を実行するボタン
の3点とします。
「検索するファミリ名」はTextBox、「完全一致検索するかどうか」はCheckbox、「検索を実行するボタン」はButtonで配置します。
それぞれ、Bindingキーワードで
- TextBox.Text
- Checkbox.IsChecked
- Button.Command
をViewModelとバインドしています。
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 |
<Window x:Class="RevitAddinTemplate.MainView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:RevitAddinTemplate" mc:Ignorable="d" Height="200" Width="500"> <StackPanel Margin="10"> <StackPanel Orientation="Horizontal" Margin="5"> <TextBlock VerticalAlignment="Center" Text="検索するファミリ名"/> <TextBox VerticalAlignment="Center" Text="{Binding TargetFamilyName}" Width="200" Margin="10"/> </StackPanel> <StackPanel Orientation="Horizontal" Margin="5"> <CheckBox VerticalAlignment="Center" IsChecked="{Binding DoExactMatch}"/> <TextBlock VerticalAlignment="Center" Text=" ファミリ名が完全一致するインスタンスのみを対象とする"/> </StackPanel> <Button Content="検索" Command="{Binding SearchCommand}" Margin="5"/> <StackPanel Orientation="Horizontal" Margin="10"> <TextBlock Text="見つかったファミリインスタンスは" FontSize="24" FontWeight="Bold"/> <TextBlock Text="{Binding ElementsCount}" FontSize="24" FontWeight="Bold"/> <TextBlock Text="個でした。" FontSize="24" FontWeight="Bold"/> </StackPanel> </StackPanel> </Window> |
ViewModelの変更
ViewModelを変更して、Viewに記述したプロパティTargetFamilyName、DoExactMatch、SearchCommandを追加します。
ElementsCountは前回のままで大丈夫です。
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 |
using System; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows.Input; namespace RevitAddinTemplate { public class MainViewModel : INotifyPropertyChanged { public MainViewModel() { SearchCommand = new RelayCommand(ExecuteSearch); } public void ExecuteSearch(object parameter) { var searchedElements = Model.ExecuteSearch(TargetFamilyName, DoExactMatch); ElementsCount = searchedElements.Count(); } public ICommand SearchCommand { get; } private int elementsCount; public int ElementsCount { get { return elementsCount; } set { elementsCount = value; OnPropertyChanged(); } } private string targetFamilyName; public string TargetFamilyName { get { return targetFamilyName; } set { targetFamilyName = value; OnPropertyChanged(); } } private bool doExactMatch; public bool DoExactMatch { get { return doExactMatch; } set { doExactMatch = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string name = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } } } |
string型のTargetFamilyNameプロパティが「検索対象とするファミリ名」に、bool型のDoExactMatchプロパティが「完全一致検索するかどうか」に対応するのは前回のとおりですが、今回はICommand型のSearchCommandプロパティも用意しています。
ICommandは、ボタン等がクリックされた際の処理の実態を記述するためのインターフェイスです。
Button.CommandにバインドしたICommand型のSearchCommandプロパティに検索処理を持たせておくことで、UIでボタンをクリックしたときに検索を実行することができます。
コードでは、MainViewModelクラスのコンストラクタでSearchCommandプロパティに検索処理であるExecuteSearchメソッドを渡しています。
1 2 3 4 |
public MainViewModel() { SearchCommand = new RelayCommand(ExecuteSearch); } |
ここで、WPFにはICommandインターフェイスの標準的な実装が存在しないため、RelayCommandというICommandインターフェイスを実装したクラスを自前実装します。
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 |
public class RelayCommand : ICommand { private readonly Action<object> execute; private readonly Func<object, bool> canExecute; public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null) { this.execute = execute; this.canExecute = canExecute; } public bool CanExecute(object parameter) { return canExecute == null || canExecute(parameter); } public void Execute(object parameter) { execute(parameter); } public event EventHandler CanExecuteChanged { add => CommandManager.RequerySuggested += value; remove => CommandManager.RequerySuggested -= value; } } |
Modelの変更
前回、Modelクラス(IExternalCommandインターフェイスを実装)は、要素の数をカウント、ViewModelのプロパティに渡し、Viewを立ち上げる役目でした。
今回は検索実行ボタン押下時にFamilyInstanceの数をカウントするため、ExecuteではViewを立ち上げるだけとし、ViewModelの
1 |
var searchedElements = Model.ExecuteSearch(TargetFamilyName, DoExactMatch); |
の行で利用するための静的メソッドを追加します。
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 |
using Autodesk.Revit.Attributes; using Autodesk.Revit.DB; using Autodesk.Revit.UI; using System.Collections.Generic; using System.Linq; namespace RevitAddinTemplate { [Transaction(TransactionMode.Manual)] public class Model : IExternalCommand { public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { var viewModel = new MainViewModel(); var view = new MainView(viewModel); view.ShowDialog(); return Result.Succeeded; } public static List<FamilyInstance> ExecuteSearch(string familyName, bool doExactMatch) { return new FilteredElementCollector(RevitDocuments.Doc) .OfClass(typeof(FamilyInstance)) .Cast<FamilyInstance>() .Where(x => { if (doExactMatch) return x.Symbol.Family.Name == familyName; else return x.Symbol.Family.Name.Contains(familyName); }) .ToList(); } } } |
実行
ビルドが通ったら、前回同様、%APPDATA%\Autodesk\Revit\Addins\2024
等に
- RevitAddinTemplate.dll
- RevitAddinTemplate.addin
を配置してRevitを起動してみましょう。
今回もRUGの構造サンプルで試しました。
ファミリ名が『RC』と部分一致するファミリインスタンスは153個ありました。
ファミリ名が『RC』と完全一致するファミリインスタンスは0個でした。
ファミリ名が『RC_B』と完全一致するファミリインスタンスは25個でした。
おわりに
RevitAPIで学ぶC#シリーズ第4回は、前回に引き続きUIについて取り上げました。
個数を表示するだけだった前回に比べ、ずいぶん便利になったと思います。
弊社では随時BIMエンジニア、コンピュテーショナルデザイナーを募集しています。
ご興味のある方は、DMかContactからご連絡ください。
COMMENTS