Agent skill
wpf-webview-threading
Thread-safe WebView2 bridge messaging in WPF with async handlers
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/wpf-webview-threading
SKILL.md
Context
Use this pattern when building a WPF desktop application that hosts a WebView2 control and needs to exchange messages with web content. Especially important when bridge handlers perform async I/O that uses .ConfigureAwait(false).
Problem
WebView2 bridge handlers that call CoreWebView2.PostWebMessageAsJson() after async operations using .ConfigureAwait(false) will fail intermittently because execution resumes on a thread pool thread, but WPF controls require UI thread access.
Pattern
1. Service Initialization Timing
Initialize services that capture Application.Current.Dispatcher AFTER InitializeComponent():
public MainWindow()
{
InitializeComponent(); // FIRST
// THEN initialize services that need dispatcher
_fileDialogService = new NativeFileDialogService();
// ... other services
}
Not:
// BAD: Field initializer runs before InitializeComponent()
private readonly IFileDialogService _fileDialogService = new NativeFileDialogService();
2. Thread-Safe WebView2 Message Posting
Always marshal WebView2 message posts to the UI thread:
private void Post(BridgeMessageEnvelope message)
{
if (!_webView.Dispatcher.CheckAccess())
{
_webView.Dispatcher.Invoke(() => Post(message));
return;
}
var json = JsonSerializer.Serialize(message, BridgeJson.SerializerOptions);
_webView.CoreWebView2.PostWebMessageAsJson(json);
}
3. Async Handler Context
For the event handler, prefer .ConfigureAwait(true) to try staying on original context, but rely on the Post dispatcher check as the robust guarantee:
private async void HandleWebMessageReceived(object? sender, CoreWebView2WebMessageReceivedEventArgs e)
{
// ... deserialize request ...
var response = await _dispatcher.DispatchAsync(request).ConfigureAwait(true);
if (response is not null)
{
Post(response); // Safe: Post checks thread
}
}
Why It Helps
- File dialogs and other UI operations get a valid dispatcher reference
- WebView2 message posts never fail due to cross-thread access violations
- Async handlers using
.ConfigureAwait(false)(recommended for I/O) don't break message flow - First-try operations work reliably instead of requiring retry
Symptoms Without This Pattern
- File operations work on second try but not first
- Intermittent "cross-thread operation" exceptions
- Messages posted from background threads don't reach web content
- Dialogs fail to show or show on wrong thread
Testing Strategy
- Unit test file dialog service with explicit dispatcher
- Manual test: verify first-try file open/import succeeds
- Look for thread ID mismatches in logs if issues persist
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?