{"id":263,"date":"2018-08-15T18:18:05","date_gmt":"2018-08-15T09:18:05","guid":{"rendered":"https:\/\/mathcadbimthingy.wordpress.com\/?p=263"},"modified":"2020-03-20T06:12:58","modified_gmt":"2020-03-20T06:12:58","slug":"revit-classes-and-wpf-the-first-m-in-mvvm","status":"publish","type":"post","link":"https:\/\/mathcadbimthingys.technikstudio.com\/index.php\/2018\/08\/15\/revit-classes-and-wpf-the-first-m-in-mvvm\/","title":{"rendered":"Revit classes and WPF? The first M in MVVM"},"content":{"rendered":"<p>I have been receiving inquiries on my blog regarding the use of Revit classes inside the WPF window, because I mentioned that we must not use the Revit API classes inside our view models, the reason being the Revit classes obstruct the use of view-view model functionality during the design of our WPF windows. So how are we going to use these functions\/classes especially in Revit&#8217;s Document class when the WPF window is active?<\/p>\n<p>This is now the turn of the first M in the acronym MVVM, which is the <strong>model<\/strong>.<\/p>\n<p>We represent some of our Revit classes in a model that we will call here the Revit model.<\/p>\n<p>Inside this Revit model, the UIApplication, UIDocument, and Document classes are represented by their respective variables, which are made private. These variables shall be initialized inside the model&#8217;s constructor, which has an argument of a UIApplication class. We name this <strong>modelRevitBridge.cs<\/strong>.<\/p>\n<p>Below is a sample of it.<\/p>\n<p>[code language=&#8221;csharp&#8221;]<br \/>\nclass modelRevitBridge<br \/>\n{<br \/>\n\/\/ Just like what you do when creating a Revit command, declare the necessary variable such as below.<br \/>\nprivate UIApplication UIAPP = null;<br \/>\nprivate Application APP = null;<br \/>\nprivate UIDocument UIDOC = null;<br \/>\nprivate Document DOC = null;<\/p>\n<p>\/\/ The model constructor. Include a UIApplication argument and do all the assignments here.<br \/>\npublic modelRevitBridge(UIApplication uiapp)<br \/>\n{<br \/>\nUIAPP = uiapp;<br \/>\nAPP = UIAPP.Application;<br \/>\nUIDOC = UIAPP.ActiveUIDocument;<br \/>\nDOC = UIDOC.Document;<br \/>\n}<\/p>\n<p>\/\/ This function will be called by the Action function in the view model, so it must be public.<br \/>\npublic List&lt;string&gt; GenerateParametersAndValues(int idIntegerValue)<br \/>\n{<br \/>\nList&lt;string&gt; resstr = new List();<\/p>\n<p>Element el = DOC.GetElement(new ElementId(idIntegerValue));<br \/>\nif (el != null)<br \/>\n{<br \/>\nforeach (Parameter prm in el.Parameters)<br \/>\n{<br \/>\nstring str = prm.Definition.Name;<br \/>\nstr += &#8221; : &#8220;;<br \/>\nstr += prm.AsValueString();<\/p>\n<p>resstr.Add(str);<br \/>\n}<br \/>\n}<\/p>\n<p>return resstr.OrderBy(x =&gt; x).ToList();<br \/>\n}<br \/>\n}<br \/>\n[\/code]<\/p>\n<p>Below is the view model that we integrate with the view later. Name this as <strong>viewmodelRevitBridge.cs<\/strong>. This class has a Dictionary that holds all the wall types&#8217; name and id in integer value. It also has a variable of type int that holds the selected value from the combo box, as well as a list of string declared as an ObservableCollection where we save all the parameter information of the selected wall type. Also, we create a pair of command variable and action function to be connected to the button that will generate the parameters in strings.<\/p>\n<p>[code language=&#8221;csharp&#8221;]<br \/>\nprivate Dictionary&lt;string, int&gt; _dicWallType;<br \/>\nprivate int _selectedWallType;<br \/>\nprivate ObservableCollection&lt;string&gt; _listParameters;<\/p>\n<p>\/\/ Declare the Revit model class here.<br \/>\n\/\/ Consequently, create a get-set variable representing this.<br \/>\nprivate modelRevitBridge _revitModel;<\/p>\n<p>public Dictionary DicWallType<br \/>\n{<br \/>\nget<br \/>\n{<br \/>\nreturn _dicWallType;<br \/>\n}<\/p>\n<p>set<br \/>\n{<br \/>\nSetProperty(ref _dicWallType, value);<br \/>\n}<br \/>\n}<\/p>\n<p>public int SelectedWallType<br \/>\n{<br \/>\nget<br \/>\n{<br \/>\nreturn _selectedWallType;<br \/>\n}<\/p>\n<p>set<br \/>\n{<br \/>\nSetProperty(ref _selectedWallType, value);<br \/>\n}<br \/>\n}<\/p>\n<p>public ObservableCollection&lt;string&gt; ListParameters<br \/>\n{<br \/>\nget<br \/>\n{<br \/>\nreturn _listParameters;<br \/>\n}<\/p>\n<p>set<br \/>\n{<br \/>\nSetProperty(ref _listParameters, value);<br \/>\n}<br \/>\n}<\/p>\n<p>\/\/  Commands<br \/>\n\/\/ This will be used by the button in the WPF window.<br \/>\npublic ICommand RetrieveParametersValuesCommand<br \/>\n{<br \/>\nget<br \/>\n{<br \/>\nreturn new DelegateCommand(RetrieveParametersValuesAction);<br \/>\n}<br \/>\n}<\/p>\n<p>\/\/ The get-set variable<br \/>\ninternal modelRevitBridge RevitModel<br \/>\n{<br \/>\nget<br \/>\n{<br \/>\nreturn _revitModel;<br \/>\n}<\/p>\n<p>set<br \/>\n{<br \/>\n_revitModel = value;<br \/>\n}<br \/>\n}<\/p>\n<p>\/\/ The action function for RetrieveParametersValuesCommand<br \/>\nprivate void RetrieveParametersValuesAction()<br \/>\n{<br \/>\nif (SelectedWallType != -1)<br \/>\n{<br \/>\nListParameters = new ObservableCollection(RevitModel.GenerateParametersAndValues(SelectedWallType));<br \/>\n}<br \/>\n}<\/p>\n<p>\/\/ Constructor<br \/>\npublic viewmodelRevitBridge()<br \/>\n{<\/p>\n<p>}<br \/>\n[\/code]<\/p>\n<p>The following, on the other hand, is a sample of a view of our sample project, coded in XAML. Let us name this <strong>viewRevitBridge.xaml<\/strong>. This view will have a combo box and a list box.<\/p>\n<p>Edit the corresponding xaml.cs file as well so that we make this window disposable and we hide the minimize and maximize button.<\/p>\n<p>[code language=&#8221;xml&#8221;]<br \/>\n&lt;Window x:Class=&#8221;RevitBridgeSample.viewRevitBridge&#8221;<br \/>\nxmlns=&#8221;http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation&#8221;<br \/>\nxmlns:x=&#8221;http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml&#8221;<br \/>\nxmlns:d=&#8221;http:\/\/schemas.microsoft.com\/expression\/blend\/2008&#8243;<br \/>\nxmlns:mc=&#8221;http:\/\/schemas.openxmlformats.org\/markup-compatibility\/2006&#8243;<br \/>\nxmlns:local=&#8221;clr-namespace:RevitBridgeSample&#8221;<br \/>\nmc:Ignorable=&#8221;d&#8221;<br \/>\nTitle=&#8221;Revit Bridge Sample&#8221; Height=&#8221;456&#8243; Width=&#8221;279&#8243; ResizeMode=&#8221;NoResize&#8221; WindowStartupLocation=&#8221;CenterScreen&#8221; ShowInTaskbar=&#8221;False&#8221; SourceInitialized=&#8221;Window_SourceInitialized&#8221;&gt;<br \/>\n&lt;Window.DataContext&gt;<br \/>\n&lt;local:RevitBridgeViewModel\/&gt;<br \/>\n&lt;\/Window.DataContext&gt;<br \/>\n&lt;Grid&gt;<br \/>\n&lt;ComboBox x:Name=&#8221;comboBox&#8221; Height=&#8221;35&#8243; Margin=&#8221;10,10,10,0&#8243; VerticalAlignment=&#8221;Top&#8221; ItemsSource=&#8221;{Binding DicWallType}&#8221; DisplayMemberPath=&#8221;Key&#8221; SelectedValuePath=&#8221;Value&#8221; SelectedValue=&#8221;{Binding SelectedWallType}&#8221;\/&gt;<br \/>\n&lt;ListBox x:Name=&#8221;listBox&#8221; Margin=&#8221;10,95,10,77&#8243; ItemsSource=&#8221;{Binding ListParameters}&#8221;\/&gt;<br \/>\n&lt;Button x:Name=&#8221;bOk&#8221; Content=&#8221;OK&#8221; HorizontalAlignment=&#8221;Right&#8221; Height=&#8221;37&#8243; Margin=&#8221;0,0,93,10&#8243; VerticalAlignment=&#8221;Bottom&#8221; Width=&#8221;105&#8243; IsDefault=&#8221;True&#8221; Click=&#8221;bOk_Click&#8221;\/&gt;<br \/>\n&lt;Button x:Name=&#8221;bCan&#8221; Content=&#8221;Cancel&#8221; HorizontalAlignment=&#8221;Right&#8221; Height=&#8221;27&#8243; Margin=&#8221;0,0,10,10&#8243; VerticalAlignment=&#8221;Bottom&#8221; Width=&#8221;78&#8243; IsCancel=&#8221;True&#8221;\/&gt;<br \/>\n&lt;Button x:Name=&#8221;bProp&#8221; Content=&#8221;Retrieve Parameters and values&#8221; Height=&#8221;30&#8243; Margin=&#8221;10,50,10,0&#8243; VerticalAlignment=&#8221;Top&#8221; Command=&#8221;{Binding RetrieveParametersValuesCommand, Mode=OneWay}&#8221;\/&gt;<br \/>\n&lt;\/Grid&gt;<br \/>\n&lt;\/Window&gt;<br \/>\n[\/code]<\/p>\n<p>Lastly, this will be the sample code for the Revit command, which will run the WPF window.<\/p>\n<p>[code language=&#8221;csharp&#8221;]<br \/>\nclass RevitBridgeCommand : IExternalCommand<br \/>\n{<br \/>\npublic Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)<br \/>\n{<br \/>\nUIApplication uiapp = commandData.Application;<br \/>\nUIDocument uidoc = uiapp.ActiveUIDocument;<br \/>\nApplication app = uiapp.Application;<br \/>\nDocument doc = uidoc.Document;<\/p>\n<p>try<br \/>\n{<br \/>\n\/\/ Get all the wall types in the current project and convert them in a Dictionary.<br \/>\nFilteredElementCollector felc = new FilteredElementCollector(doc).OfClass(typeof(WallType));<br \/>\nDictionary&lt;string, int&gt; dicwtypes = felc.Cast().ToDictionary(x =&gt; x.Name, y =&gt; y.Id.IntegerValue);<br \/>\nfelc.Dispose();<\/p>\n<p>\/\/ Create a view model that will be associated to the DataContext of the view.<br \/>\nviewmodelRevitBridge vmod = new viewmodelRevitBridge();<br \/>\nvmod.DicWallType = dicwtypes;<br \/>\nvmod.SelectedWallType = dicwtypes.First().Value;<\/p>\n<p>\/\/ Create a new Revit model and assign it to the Revit model variable in the view model.<br \/>\nvmod.RevitModel = new modelRevitBridge(uiapp);<\/p>\n<p>System.Diagnostics.Process proc = System.Diagnostics.Process.GetCurrentProcess();<\/p>\n<p>\/\/ Load the WPF window viewRevitbridge.<br \/>\nusing (viewRevitBridge view = new viewRevitBridge())<br \/>\n{<br \/>\nSystem.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(view);<br \/>\nhelper.Owner = proc.MainWindowHandle;<\/p>\n<p>\/\/ Assign the view model to the DataContext of the view.<br \/>\nview.DataContext = vmod;<\/p>\n<p>if (view.ShowDialog() != true)<br \/>\n{<br \/>\nreturn Result.Cancelled;<br \/>\n}<br \/>\n}<\/p>\n<p>return Result.Succeeded;<br \/>\n}<br \/>\ncatch (Exception ex)<br \/>\n{<br \/>\nmessage = ex.Message;<br \/>\nreturn Result.Failed;<br \/>\n}<br \/>\n}<br \/>\n}<br \/>\n[\/code]<\/p>\n<p>Running this code will turn out like this:<\/p>\n<p>The image below will be the initial screen, which gets all the wall types inside your Revit project.<\/p>\n<p><img loading=\"lazy\" src=\"http:\/\/mathcadbimthingys.technikstudio.com\/wp-content\/uploads\/2018\/08\/rmw01.png\" alt=\"rmw01\" class=\"alignnone size-full wp-image-266\" width=\"263\" height=\"446\" srcset=\"https:\/\/mathcadbimthingys.technikstudio.com\/wp-content\/uploads\/2018\/08\/rmw01.png 263w, https:\/\/mathcadbimthingys.technikstudio.com\/wp-content\/uploads\/2018\/08\/rmw01-177x300.png 177w\" sizes=\"(max-width: 263px) 100vw, 263px\" \/><\/p>\n<p>When you press the &#8220;Retrieve Parameters and values&#8221; button, all the parameters and their values of the selected wall type will be listed up inside the listbox.<\/p>\n<p><img loading=\"lazy\" src=\"http:\/\/mathcadbimthingys.technikstudio.com\/wp-content\/uploads\/2018\/08\/rmw02.png\" alt=\"rmw02\" class=\"alignnone size-full wp-image-267\" width=\"268\" height=\"455\" srcset=\"https:\/\/mathcadbimthingys.technikstudio.com\/wp-content\/uploads\/2018\/08\/rmw02.png 268w, https:\/\/mathcadbimthingys.technikstudio.com\/wp-content\/uploads\/2018\/08\/rmw02-177x300.png 177w\" sizes=\"(max-width: 268px) 100vw, 268px\" \/><\/p>\n<p>You can also create a function that writes something in your Revit project, like editing parameters of renaming the wall types. In this case though, we have to wrap the writing part of the code inside a <strong>Transaction<\/strong> class, or else the program will fail.<br \/>\nWhen you do this, every time you execute the writing function, it registers a command in the undo-redo mechanism. So if you want to run this function multiple times and want to undo or redo these process just once, you need to wrap the WPF window loading inside a <strong>TransactionGroup <\/strong>and just before the end of it, use the <strong>TransactionGroup.Assimilate()<\/strong> function.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I have been receiving inquiries on my blog regarding the use of Revit classes inside the WPF window, because I mentioned that we must not use the Revit API classes inside our view models, the reason being the Revit classes obstruct the use of view-view model functionality during the design of our WPF windows. So [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":267,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[14],"tags":[],"_links":{"self":[{"href":"https:\/\/mathcadbimthingys.technikstudio.com\/index.php\/wp-json\/wp\/v2\/posts\/263"}],"collection":[{"href":"https:\/\/mathcadbimthingys.technikstudio.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mathcadbimthingys.technikstudio.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mathcadbimthingys.technikstudio.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mathcadbimthingys.technikstudio.com\/index.php\/wp-json\/wp\/v2\/comments?post=263"}],"version-history":[{"count":2,"href":"https:\/\/mathcadbimthingys.technikstudio.com\/index.php\/wp-json\/wp\/v2\/posts\/263\/revisions"}],"predecessor-version":[{"id":341,"href":"https:\/\/mathcadbimthingys.technikstudio.com\/index.php\/wp-json\/wp\/v2\/posts\/263\/revisions\/341"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mathcadbimthingys.technikstudio.com\/index.php\/wp-json\/wp\/v2\/media\/267"}],"wp:attachment":[{"href":"https:\/\/mathcadbimthingys.technikstudio.com\/index.php\/wp-json\/wp\/v2\/media?parent=263"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mathcadbimthingys.technikstudio.com\/index.php\/wp-json\/wp\/v2\/categories?post=263"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mathcadbimthingys.technikstudio.com\/index.php\/wp-json\/wp\/v2\/tags?post=263"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}