Skip to content

Commit 58b0744

Browse files
authored
Merge pull request #4 from mitevpi/development
Multi-Threading Updates
2 parents 401c170 + 1106861 commit 58b0744

File tree

13 files changed

+300
-161
lines changed

13 files changed

+300
-161
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Revit WPF Template
22

3+
![GitHub issues](https://img.shields.io/github/issues/mitevpi/revit-wpf-template)
4+
![GitHub pull requests](https://img.shields.io/github/issues-pr-raw/mitevpi/revit-wpf-template)
5+
![GitHub contributors](https://img.shields.io/github/contributors/mitevpi/revit-wpf-template)
6+
7+
![GitHub last commit](https://img.shields.io/github/last-commit/mitevpi/revit-wpf-template)
8+
![GitHub Release Date](https://img.shields.io/github/release-date/mitevpi/revit-wpf-template)
9+
![GitHub All Releases](https://img.shields.io/github/downloads/mitevpi/revit-wpf-template/total)
10+
11+
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/mitevpi/revit-wpf-template)
12+
![GitHub repo size](https://img.shields.io/github/repo-size/mitevpi/revit-wpf-template)
13+
![GitHub](https://img.shields.io/github/license/mitevpi/revit-wpf-template)
14+
315
WPF Template for Revit Add-Ins including wrapped external methods for execution in a "Valid Revit API Context"
416

517
![Window A](assets/window1.png)

Revit Template/App.cs

Lines changed: 57 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using System.Reflection;
45
using System.Threading;
56
using System.Windows.Media.Imaging;
@@ -22,7 +23,7 @@ class App : IExternalApplication
2223
private Ui _mMyForm;
2324

2425
// Separate thread to run Ui on
25-
private Thread _UiThread;
26+
private Thread _uiThread;
2627

2728
public Result OnStartup(UIControlledApplication a)
2829
{
@@ -32,31 +33,31 @@ public Result OnStartup(UIControlledApplication a)
3233
// Method to add Tab and Panel
3334
RibbonPanel panel = RibbonPanel(a);
3435
string thisAssemblyPath = Assembly.GetExecutingAssembly().Location;
35-
PushButton button =
36-
panel.AddItem(
37-
new PushButtonData("WPF Template", "WPF Template", thisAssemblyPath,
38-
"RevitTemplate.EntryCommand")) as
39-
PushButton;
4036

41-
// defines the tooltip displayed when the button is hovered over in Revit's ribbon
42-
button.ToolTip = "Visual interface for debugging applications.";
43-
44-
// defines the icon for the button in Revit's ribbon - note the string formatting
45-
Uri uriImage = new Uri("pack://application:,,,/RevitTemplate;component/Resources/code-small.png");
46-
BitmapImage largeImage = new BitmapImage(uriImage);
47-
button.LargeImage = largeImage;
48-
49-
PushButton button2 =
50-
panel.AddItem(
51-
new PushButtonData("WPF Template Multi-Thread", "WPF Template Multi-Thread", thisAssemblyPath,
52-
"RevitTemplate.EntryCommandSeparateThread")) as
53-
PushButton;
37+
// BUTTON FOR THE SINGLE-THREADED WPF OPTION
38+
if (panel.AddItem(
39+
new PushButtonData("WPF Template", "WPF Template", thisAssemblyPath,
40+
"RevitTemplate.EntryCommand")) is PushButton button)
41+
{
42+
// defines the tooltip displayed when the button is hovered over in Revit's ribbon
43+
button.ToolTip = "Visual interface for debugging applications.";
44+
// defines the icon for the button in Revit's ribbon - note the string formatting
45+
Uri uriImage = new Uri("pack://application:,,,/RevitTemplate;component/Resources/code-small.png");
46+
BitmapImage largeImage = new BitmapImage(uriImage);
47+
button.LargeImage = largeImage;
48+
}
5449

55-
// defines the tooltip displayed when the button is hovered over in Revit's ribbon
56-
button2.ToolTip = "Visual interface for debugging applications.";
50+
// BUTTON FOR THE MULTI-THREADED WPF OPTION
51+
if (panel.AddItem(
52+
new PushButtonData("WPF Template\nMulti-Thread", "WPF Template\nMulti-Thread", thisAssemblyPath,
53+
"RevitTemplate.EntryCommandSeparateThread")) is PushButton button2)
54+
{
55+
button2.ToolTip = "Visual interface for debugging applications.";
56+
Uri uriImage = new Uri("pack://application:,,,/RevitTemplate;component/Resources/code-small.png");
57+
BitmapImage largeImage = new BitmapImage(uriImage);
58+
button2.LargeImage = largeImage;
59+
}
5760

58-
// defines the icon for the button in Revit's ribbon - note the string formatting
59-
button2.LargeImage = largeImage;
6061

6162
// listeners/watchers for external events (if you choose to use them)
6263
a.ApplicationClosing += a_ApplicationClosing; //Set Application to Idling
@@ -82,16 +83,14 @@ public Result OnShutdown(UIControlledApplication a)
8283
public void ShowForm(UIApplication uiapp)
8384
{
8485
// If we do not have a dialog yet, create and show it
85-
if (_mMyForm == null || _mMyForm != null) // || m_MyForm.IsDisposed
86-
{
87-
//EXTERNAL EVENTS WITH ARGUMENTS
88-
EventHandlerWithStringArg evString = new EventHandlerWithStringArg();
89-
EventHandlerWithWpfArg evWpf = new EventHandlerWithWpfArg();
90-
91-
// The dialog becomes the owner responsible for disposing the objects given to it.
92-
_mMyForm = new Ui(uiapp, evString, evWpf);
93-
_mMyForm.Show();
94-
}
86+
if (_mMyForm != null && _mMyForm == null) return;
87+
//EXTERNAL EVENTS WITH ARGUMENTS
88+
EventHandlerWithStringArg evStr = new EventHandlerWithStringArg();
89+
EventHandlerWithWpfArg evWpf = new EventHandlerWithWpfArg();
90+
91+
// The dialog becomes the owner responsible for disposing the objects given to it.
92+
_mMyForm = new Ui(uiapp, evStr, evWpf);
93+
_mMyForm.Show();
9594
}
9695

9796
/// <summary>
@@ -103,28 +102,26 @@ public void ShowForm(UIApplication uiapp)
103102
public void ShowFormSeparateThread(UIApplication uiapp)
104103
{
105104
// If we do not have a thread started or has been terminated start a new one
106-
if (_UiThread is null || !_UiThread.IsAlive)
105+
if (!(_uiThread is null) && _uiThread.IsAlive) return;
106+
//EXTERNAL EVENTS WITH ARGUMENTS
107+
EventHandlerWithStringArg evStr = new EventHandlerWithStringArg();
108+
EventHandlerWithWpfArg evWpf = new EventHandlerWithWpfArg();
109+
110+
_uiThread = new Thread(() =>
107111
{
108-
//EXTERNAL EVENTS WITH ARGUMENTS
109-
EventHandlerWithStringArg evStr = new EventHandlerWithStringArg();
110-
EventHandlerWithWpfArg eDatabaseStore = new EventHandlerWithWpfArg();
111-
112-
_UiThread = new Thread(() =>
113-
{
114-
SynchronizationContext.SetSynchronizationContext(
115-
new DispatcherSynchronizationContext(
116-
Dispatcher.CurrentDispatcher));
117-
// The dialog becomes the owner responsible for disposing the objects given to it.
118-
_mMyForm = new Ui(uiapp, evStr, eDatabaseStore);
119-
_mMyForm.Closed += (s, e) => Dispatcher.CurrentDispatcher.InvokeShutdown();
120-
_mMyForm.Show();
121-
Dispatcher.Run();
122-
});
123-
124-
_UiThread.SetApartmentState(ApartmentState.STA);
125-
_UiThread.IsBackground = true;
126-
_UiThread.Start();
127-
}
112+
SynchronizationContext.SetSynchronizationContext(
113+
new DispatcherSynchronizationContext(
114+
Dispatcher.CurrentDispatcher));
115+
// The dialog becomes the owner responsible for disposing the objects given to it.
116+
_mMyForm = new Ui(uiapp, evStr, evWpf);
117+
_mMyForm.Closed += (s, e) => Dispatcher.CurrentDispatcher.InvokeShutdown();
118+
_mMyForm.Show();
119+
Dispatcher.Run();
120+
});
121+
122+
_uiThread.SetApartmentState(ApartmentState.STA);
123+
_uiThread.IsBackground = true;
124+
_uiThread.Start();
128125
}
129126

130127
#region Idling & Closing
@@ -157,27 +154,26 @@ public RibbonPanel RibbonPanel(UIControlledApplication a)
157154
{
158155
a.CreateRibbonTab(tab);
159156
}
160-
catch
157+
catch (Exception ex)
161158
{
159+
Util.HandleError(ex);
162160
}
163161

164162
// Try to create ribbon panel.
165163
try
166164
{
167165
RibbonPanel panel = a.CreateRibbonPanel(tab, "Develop");
168166
}
169-
catch
167+
catch (Exception ex)
170168
{
169+
Util.HandleError(ex);
171170
}
172171

173172
// Search existing tab for your panel.
174173
List<RibbonPanel> panels = a.GetRibbonPanels(tab);
175-
foreach (RibbonPanel p in panels)
174+
foreach (RibbonPanel p in panels.Where(p => p.Name == "Develop"))
176175
{
177-
if (p.Name == "Develop")
178-
{
179-
ribbonPanel = p;
180-
}
176+
ribbonPanel = p;
181177
}
182178

183179
//return panel

Revit Template/Methods.cs

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,32 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Threading.Tasks;
45
using Autodesk.Revit.DB;
56

67
namespace RevitTemplate
78
{
8-
99
/// <summary>
1010
/// Create methods here that need to be wrapped in a valid Revit Api context.
1111
/// Things like transactions modifying Revit Elements, etc.
1212
/// </summary>
1313
internal class Methods
1414
{
15+
/// <summary>
16+
/// Method for collecting sheets as an asynchronous operation on another thread.
17+
/// </summary>
18+
/// <param name="doc">The Revit Document to collect sheets from.</param>
19+
/// <returns>A list of collected sheets, once the Task is resolved.</returns>
20+
private static async Task<List<ViewSheet>> GetSheets(Document doc)
21+
{
22+
return await Task.Run(() =>
23+
{
24+
Util.LogThreadInfo("Get Sheets Method");
25+
return new FilteredElementCollector(doc)
26+
.OfClass(typeof(ViewSheet))
27+
.Select(p => (ViewSheet) p).ToList();
28+
});
29+
}
1530

1631
/// <summary>
1732
/// Rename all the sheets in the project. This opens a transaction, and it MUST be executed
@@ -23,38 +38,50 @@ internal class Methods
2338
/// <param name="doc">The Revit Document to rename sheets in.</param>
2439
public static void SheetRename(Ui ui, Document doc)
2540
{
26-
// get sheets
27-
ICollection<ViewSheet> sheets = new FilteredElementCollector(doc)
41+
Util.LogThreadInfo("Sheet Rename Method");
42+
43+
// get sheets - note that this may be replaced with the Async Task method above,
44+
// however that will only work if we want to only PULL data from the sheets,
45+
// and executing a transaction like below from an async collection, will crash the app
46+
List<ViewSheet> sheets = new FilteredElementCollector(doc)
2847
.OfClass(typeof(ViewSheet))
29-
.Select(p => (ViewSheet)p).ToList();
48+
.Select(p => (ViewSheet) p).ToList();
3049

31-
// report the count
32-
string message = $"There are {sheets.Count} Sheets in the project";
50+
// report results - push the task off to another thread
51+
Task.Run(() =>
52+
{
53+
Util.LogThreadInfo("Sheet Rename Show Results");
3354

34-
ui.Dispatcher.Invoke(() => ui.TbDebug.Text += "\n" + (DateTime.Now).ToLongTimeString() + "\t" + message);
55+
// report the count
56+
string message = $"There are {sheets.Count} Sheets in the project";
57+
ui.Dispatcher.Invoke(() =>
58+
ui.TbDebug.Text += "\n" + (DateTime.Now).ToLongTimeString() + "\t" + message);
59+
});
3560

36-
// rename all the sheets
37-
// first open a transaction
61+
// rename all the sheets, but first open a transaction
3862
using (Transaction t = new Transaction(doc, "Rename Sheets"))
3963
{
64+
Util.LogThreadInfo("Sheet Rename Transaction");
65+
4066
// start a transaction within the valid Revit API context
4167
t.Start("Rename Sheets");
4268

43-
// loop over the collection of sheets
44-
foreach (ViewSheet sheet in sheets)
69+
// loop over the collection of sheets using LINQ syntax
70+
foreach (string renameMessage in from sheet in sheets
71+
let renamed = sheet.LookupParameter("Sheet Name")?.Set("TEST")
72+
select $"Renamed: {sheet.Title}, Status: {renamed}")
4573
{
46-
// rename the sheets
47-
bool? renamed = sheet.LookupParameter("Sheet Name")?.Set("TEST");
48-
string renameMessage = $"Renamed Sheet: {sheet.Title}";
49-
ui.Dispatcher.Invoke(() => ui.TbDebug.Text += "\n" + (DateTime.Now).ToLongTimeString() + "\t" + renameMessage);
74+
ui.Dispatcher.Invoke(() =>
75+
ui.TbDebug.Text += "\n" + (DateTime.Now).ToLongTimeString() + "\t" + renameMessage);
5076
}
5177

5278
t.Commit();
5379
t.Dispose();
5480
}
5581

56-
// report completion
57-
ui.Dispatcher.Invoke(() => ui.TbDebug.Text += "\n" + (DateTime.Now).ToLongTimeString() + "\t" + "SHEETS HAVE BEEN RENAMED");
82+
// invoke the UI dispatcher to print the results to report completion
83+
ui.Dispatcher.Invoke(() =>
84+
ui.TbDebug.Text += "\n" + (DateTime.Now).ToLongTimeString() + "\t" + "SHEETS HAVE BEEN RENAMED");
5885
}
5986

6087
/// <summary>
@@ -77,15 +104,22 @@ public static void DocumentInfo(Ui ui, Document doc)
77104
/// <param name="doc">The Revit Document to count the walls of.</param>
78105
public static void WallInfo(Ui ui, Document doc)
79106
{
80-
ICollection<Wall> walls = new FilteredElementCollector(doc)
81-
.OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType()
82-
.Select(p => (Wall)p).ToList();
107+
Task.Run(() =>
108+
{
109+
Util.LogThreadInfo("Wall Count Method");
83110

84-
string message = $"There are {walls.Count} Walls in the project";
111+
// get all walls in the document
112+
ICollection<Wall> walls = new FilteredElementCollector(doc)
113+
.OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType()
114+
.Select(p => (Wall) p).ToList();
85115

86-
ui.Dispatcher.Invoke(() => ui.TbDebug.Text += "\n" + (DateTime.Now).ToLongTimeString() + "\t" + message);
87-
}
116+
// format the message to show the number of walls in the project
117+
string message = $"There are {walls.Count} Walls in the project";
88118

119+
// invoke the UI dispatcher to print the results to the UI
120+
ui.Dispatcher.Invoke(() =>
121+
ui.TbDebug.Text += "\n" + (DateTime.Now).ToLongTimeString() + "\t" + message);
122+
});
123+
}
89124
}
90-
}
91-
125+
}

Revit Template/MethodsWrapped.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,27 +41,27 @@ public override void Execute(UIApplication uiApp, Ui ui)
4141
UIDocument uiDoc = uiApp.ActiveUIDocument;
4242
Document doc = uiDoc.Document;
4343

44-
bool CbDocumentDataIsChecked = false;
45-
ui.Dispatcher.Invoke(() => CbDocumentDataIsChecked = ui.CbDocumentData.IsChecked.GetValueOrDefault());
44+
bool cbDocumentDataIsChecked = false;
45+
ui.Dispatcher.Invoke(() => cbDocumentDataIsChecked = ui.CbDocumentData.IsChecked.GetValueOrDefault());
4646

47-
bool CbSheetDataIsChecked = false;
48-
ui.Dispatcher.Invoke(() => CbSheetDataIsChecked = ui.CbSheetData.IsChecked.GetValueOrDefault());
47+
bool cbSheetDataIsChecked = false;
48+
ui.Dispatcher.Invoke(() => cbSheetDataIsChecked = ui.CbSheetData.IsChecked.GetValueOrDefault());
4949

50-
bool CbWallDataIsChecked = false;
51-
ui.Dispatcher.Invoke(() => CbWallDataIsChecked = ui.CbWallData.IsChecked.GetValueOrDefault());
50+
bool cbWallDataIsChecked = false;
51+
ui.Dispatcher.Invoke(() => cbWallDataIsChecked = ui.CbWallData.IsChecked.GetValueOrDefault());
5252

5353
// METHODS
54-
if (CbDocumentDataIsChecked)
54+
if (cbDocumentDataIsChecked)
5555
{
5656
Methods.DocumentInfo(ui, doc);
5757
}
5858

59-
if (CbSheetDataIsChecked)
59+
if (cbSheetDataIsChecked)
6060
{
6161
Methods.SheetRename(ui, doc);
6262
}
6363

64-
if (CbWallDataIsChecked)
64+
if (cbWallDataIsChecked)
6565
{
6666
Methods.WallInfo(ui, doc);
6767
}

Revit Template/Revit Template.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
<Compile Include="UI.xaml.cs">
122122
<DependentUpon>UI.xaml</DependentUpon>
123123
</Compile>
124+
<Compile Include="Util.cs" />
124125
</ItemGroup>
125126
<ItemGroup>
126127
<Content Include="License.txt">

0 commit comments

Comments
 (0)