Skip to content

Commit a2d5ecc

Browse files
committed
Update Changelog.md
1 parent adf6f58 commit a2d5ecc

1 file changed

Lines changed: 167 additions & 26 deletions

File tree

Changelog.md

Lines changed: 167 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,103 @@
1-
# Release 2027.0.0-preview.2.20260123
1+
# Release 2027.0.0-preview.3.20260306
22

3-
## New Features
3+
### External Events
44

5-
- New **RevitContext** class for UI-level application context access
6-
- New **RevitApiContext** class for database-level application context access
7-
- New **AsyncExternalCommand** class for async/await support in external commands
8-
- New **BeginDialogSuppressionScope()** method with disposable pattern for dialog suppression
9-
- New **BeginFailureSuppressionScope()** method with disposable pattern for failure handling
10-
- New **BeginAssemblyResolveScope()** method with disposable pattern for dependency resolution
11-
- New **BeginAssemblyResolveScope(string directory)** overload for explicit path specification
12-
- New **SetExceptionHandler()** method for `ActionEventHandler` and `IdlingEventHandler`
13-
- New **CancellationToken** support for `AsyncEventHandler` and `AsyncEventHandler<T>`
14-
- New overloads for `BeginDialogSuppressionScope()`: `MessageBoxResult`, `TaskDialogResult`, custom handler
5+
New family of external event types replacing legacy `ActionEventHandler`, `AsyncEventHandler`:
6+
7+
- **ExternalEvent** — synchronous external event that queues the handler via the Revit external event mechanism.
8+
- **ExternalEvent\<T>** — generic synchronous external event that accepts an argument of type `T`.
9+
- **AsyncExternalEvent** — asynchronous external event with `RaiseAsync()` that returns a `Task`.
10+
- **AsyncExternalEvent\<T>** — generic asynchronous external event with argument support.
11+
- **AsyncRequestExternalEvent\<TResult>** — asynchronous external event that returns a result via `RaiseAsync()`.
12+
- **AsyncRequestExternalEvent\<T, TResult>** — generic asynchronous external event with argument and result support.
13+
- **ExternalEventOptions** — configuration flags for event behavior, including `AllowDirectInvocation` for direct execution in API context.
14+
15+
Events do not need to be created inside the Revit API context — the Toolkit handles initialization automatically, so you can create them anywhere in your code and on any thread.
16+
17+
### ExternalEvent Source Generator
18+
19+
New `[ExternalEvent]` attribute with incremental source generator that eliminates boilerplate for defining external events.
20+
Annotate a method in a partial type and the generator produces typed event properties automatically:
21+
22+
```c#
23+
public partial class MyViewModel : ObservableObject
24+
{
25+
[ExternalEvent]
26+
private void DeleteWindows(UIApplication application)
27+
{
28+
var document = application.ActiveUIDocument.Document;
29+
using var transaction = new Transaction(document, "Delete windows");
30+
transaction.Start();
31+
document.Delete(document.GetInstanceIds(BuiltInCategory.OST_Windows));
32+
transaction.Commit();
33+
}
34+
35+
[RelayCommand]
36+
private void DeleteWindows()
37+
{
38+
DeleteWindowsEvent.Raise();
39+
}
40+
}
41+
42+
// Generates:
43+
// public IExternalEvent DeleteWindowsEvent => field ??= new ExternalEvent(DeleteWindows);
44+
// public IAsyncExternalEvent DeleteWindowsAsyncEvent => field ??= new AsyncExternalEvent(DeleteWindows);
45+
```
46+
47+
Source generator supports multiple parameters, methods with extra parameters like `private void DeleteWindows(string arg1, int arg2, bool arg3)` also work.
48+
49+
**Generator capabilities:**
50+
51+
- `void` methods → generates both `IExternalEvent` and `IAsyncExternalEvent` properties.
52+
- Methods returning a value → generates `IAsyncRequestExternalEvent<TResult>` property.
53+
- Methods with extra parameters → generates typed `IExternalEvent<T>` / `IAsyncExternalEvent<T>` properties.
54+
- Methods with 2+ extra parameters → generates a `sealed record` for argument bundling with convenience extension methods.
55+
- `AllowDirectInvocation` attribute property support.
56+
- Static method support.
57+
- Nested type hierarchy support.
58+
- `field` keyword usage for C# 14+, backing field fallback for older versions.
59+
- Multi-version Roslyn support (4.14 and 5.0).
60+
61+
### Roslyn Analyzers and Code Fixers
62+
63+
New analyzer package with diagnostics for `[ExternalEvent]` annotated methods:
64+
.
65+
- **RVTTK0001** — Method returns `Task` or `Task<T>` (Error).
66+
- **RVTTK0002** — Method is `async void` (Warning) + code fixer to remove `async` modifier.
67+
- **RVTTK0003** — Method is generic (Error).
68+
- **RVTTK0004** — Duplicate method overloads with `[ExternalEvent]` (Error).
69+
- **RVTTK0005** — Containing type is not `partial` (Error) + code fixer to add `partial` modifier.
70+
71+
### Context
72+
73+
- New **RevitContext** class for UI-level application context access.
74+
- New **RevitApiContext** class for database-level application context access.
75+
- New **AsyncExternalCommand** class for async/await support in external commands.
76+
- New **BeginDialogSuppressionScope()** method with disposable pattern for dialog suppression.
77+
- New **BeginFailureSuppressionScope()** method with disposable pattern for failure handling.
78+
- New **BeginAssemblyResolveScope()** method with disposable pattern for dependency resolution.
79+
- New **BeginAssemblyResolveScope(string directory)** overload for explicit path specification.`
80+
- New overloads for `BeginDialogSuppressionScope()`: `MessageBoxResult`, `TaskDialogResult`, custom handler.
1581

1682
## Improvements
1783

18-
- Event handlers now use `ConcurrentQueue` for thread-safe action queuing
19-
- Disposable scopes support nesting with reference counting
20-
- Thread safety improvements with `Lock` class and `Interlocked` operations
21-
- `UnsafeAccessor` usage for .NET 8+ to improve performance
22-
- Removed `SemaphoreSlim` from `AsyncEventHandler<T>` for better performance
23-
- `BeginAssemblyResolveScope` now uses Stack to support nested scopes with different directories
84+
- Improve reflexion performance with `UnsafeAccessor` methods for .NET 8+.
85+
- Disposable scopes support nesting with reference counting.
86+
- Thread safety improvements with `Lock` class and `Interlocked` operations.
87+
- `BeginAssemblyResolveScope` now uses Stack to support nested scopes with different directories.
2488

2589
## Breaking Changes
2690

27-
- **Context** class is now obsolete, use `RevitContext` or `RevitApiContext` instead
28-
- **SuppressDialogs()** / **RestoreDialogs()** are obsolete, use `BeginDialogSuppressionScope()` instead
29-
- **SuppressFailures()** / **RestoreFailures()** are obsolete, use `BeginFailureSuppressionScope()` instead
30-
- **BeginAssemblyResolve()** / **EndAssemblyResolve()** are obsolete, use `BeginAssemblyResolveScope()` instead
31-
- **ExternalCommand.Document** is obsolete, use `ActiveDocument` instead
32-
- **ExternalCommand.UiDocument** is obsolete, use `ActiveUiDocument` instead
33-
- **ActionEventHandler.Cancel()** method removed
34-
- **IdlingEventHandler.Cancel()** method removed
91+
- **Context** class is now obsolete, use `RevitContext` or `RevitApiContext` instead.
92+
- **SuppressDialogs()** / **RestoreDialogs()** are obsolete, use `BeginDialogSuppressionScope()` instead.
93+
- **SuppressFailures()** / **RestoreFailures()** are obsolete, use `BeginFailureSuppressionScope()` instead.
94+
- **BeginAssemblyResolve()** / **EndAssemblyResolve()** are obsolete, use `BeginAssemblyResolveScope()` instead.
95+
- **ExternalCommand.Document** is obsolete, use `ActiveDocument` instead.
96+
- **ExternalCommand.UiDocument** is obsolete, use `ActiveUiDocument` instead.
97+
- **ActionEventHandler** is now obsolete — use `ExternalEvent` or `[ExternalEvent]` source generator.
98+
- **AsyncEventHandler** is now obsolete — use `AsyncExternalEvent` or `[ExternalEvent]` source generator.
99+
- **AsyncEventHandler\<T>** is now obsolete — use `AsyncRequestExternalEvent<T>` or `[ExternalEvent]` source generator.
100+
- **IdlingEventHandler** is now obsolete — use `ExternalEvent` or `[ExternalEvent]` source generator.
35101

36102
## Automatic Migration with ReSharper/Rider
37103

@@ -40,6 +106,7 @@ All obsolete methods are marked with `[CodeTemplate]` attributes, enabling autom
40106
## Migration Guide
41107

42108
Replace `Context` with `RevitContext` or `RevitApiContext`:
109+
43110
```csharp
44111
// Before
45112
Context.ActiveDocument.Delete(elementId);
@@ -51,6 +118,7 @@ RevitApiContext.Application.Username;
51118
```
52119

53120
Replace manual suppress/restore with disposable scopes:
121+
54122
```csharp
55123
// Before
56124
try
@@ -74,6 +142,7 @@ using (RevitApiContext.BeginFailureSuppressionScope())
74142
```
75143

76144
Replace `BeginAssemblyResolve`/`EndAssemblyResolve` with scope:
145+
77146
```csharp
78147
// Before
79148
try
@@ -94,6 +163,7 @@ using (ResolveHelper.BeginAssemblyResolveScope<MyType>())
94163
```
95164

96165
New: Specify directory path directly for assembly resolution:
166+
97167
```csharp
98168
// Path-based (new)
99169
using (ResolveHelper.BeginAssemblyResolveScope(@"C:\Libraries"))
@@ -111,6 +181,77 @@ using (ResolveHelper.BeginAssemblyResolveScope<MyType>())
111181
}
112182
```
113183

184+
Replace legacy event handlers with new External Events:
185+
186+
```csharp
187+
// Before
188+
private readonly ActionEventHandler _handler = new();
189+
190+
private void Execute()
191+
{
192+
_handler.Raise(app =>
193+
{
194+
app.ActiveUIDocument.Document.Delete(elementId);
195+
});
196+
}
197+
198+
// After
199+
private readonly ExternalEvent _handler = new(app =>
200+
{
201+
app.ActiveUIDocument.Document.Delete(elementId);
202+
});
203+
204+
private void Execute()
205+
{
206+
_handler.Raise();
207+
}
208+
```
209+
210+
Replace legacy async event handlers:
211+
212+
```csharp
213+
// Before
214+
private readonly AsyncEventHandler _handler = new();
215+
216+
private async Task ExecuteAsync()
217+
{
218+
await _handler.RaiseAsync(app =>
219+
{
220+
app.ActiveUIDocument.Document.Delete(elementId);
221+
});
222+
}
223+
224+
// After
225+
private readonly AsyncExternalEvent _handler = new(app =>
226+
{
227+
app.ActiveUIDocument.Document.Delete(elementId);
228+
});
229+
230+
private async Task ExecuteAsync()
231+
{
232+
await _handler.RaiseAsync();
233+
}
234+
```
235+
236+
Or use the source generator to avoid boilerplate entirely:
237+
238+
```csharp
239+
// After (source generator)
240+
partial class MyViewModel
241+
{
242+
[ExternalEvent]
243+
private void DeleteElement(UIApplication application)
244+
{
245+
application.ActiveUIDocument.Document.Delete(elementId);
246+
}
247+
}
248+
249+
// Usage:
250+
DeleteElementEvent.Raise();
251+
// or
252+
await DeleteElementAsyncEvent.RaiseAsync();
253+
```
254+
114255
# Release 2026.0.0
115256

116257
- New `Context.UiControlledApplication` property. Helps to manipulate with the Revit ribbon, context menus outside ExternalApplication.

0 commit comments

Comments
 (0)