自定义UserControl在类库事件触发时无法显示的问题求助
问题:类库事件触发后,主应用UI控件/MessageDialog无法显示,MessageBox却正常
场景还原
我维护两个项目:主应用程序(mainApplication)和负责硬件通信的类库(classLibrary)。类库中定义了connectionChanged事件,当指定硬件断开连接时会触发它。
在主应用里我把这个事件绑定到了自定义处理函数,但遇到了诡异的问题:函数里的自定义控件和MessageDialog完全没反应,但调用MessageBox.Show()却能正常弹出。我知道如果让类库引用主应用可能能绕开这个问题,但不想这么做。
示例代码
类库代码
public event ConnectedSATChanged connectionChanged;
主应用代码
sat.connectionChanged += new SAT.SATInternalFunctions.ConnectedSATChanged(Sat_connectionChanged); private void Sat_connectionChanged() { MessageDialog.Show("WARNING"); // 无法显示,其他自定义控件也一样 MessageBox.Show("Warning"); // 可以显示 }
问题根源
这本质是**线程单元模型(ApartmentState)**的问题:
- 硬件通信类库的事件触发线程几乎都是MTA(多线程单元)线程,这类线程不支持UI组件的创建和更新;
MessageBox是Win32原生对话框,它不依赖应用的UI线程模型,所以能正常显示;- 而
MessageDialog和自定义控件属于WPF/UWP的UI组件,必须在STA(单线程单元)线程(也就是主UI线程)中操作。
已验证的解决方案
你自己找到的新开STA线程的方法确实有效,代码如下:
var thread = new Thread(() => { MessageDialog.Show("Warning!"); }); thread.SetApartmentState(ApartmentState.STA); thread.Start();
更优雅的替代方案
如果要更新主应用中已存在的自定义控件,推荐用UI框架自带的调度器切换到主UI线程执行,比如:
- WPF中使用
Dispatcher.Invoke:
private void Sat_connectionChanged() { Application.Current.Dispatcher.Invoke(() => { // 这里可以安全地更新自定义控件或显示MessageDialog MyCustomControl.Visibility = Visibility.Visible; MessageDialog.Show("WARNING"); }); }
- UWP中使用
CoreDispatcher.RunAsync:
private async void Sat_connectionChanged() { await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync( Windows.UI.Core.CoreDispatcherPriority.Normal, () => { MessageDialog.Show("WARNING"); MyCustomControl.Visibility = Visibility.Visible; }); }
这种方式不需要额外创建线程,直接复用主UI线程,更符合UI编程的最佳实践。
内容的提问来源于stack exchange,提问作者R.Lowe
相关产品推荐
相关产品推荐

