使用多个线程显示加载消息的WPF
本文关键字:加载 消息 WPF 显示 线程 | 更新日期: 2023-09-27 18:26:55
我以前也发布过类似的问题,但现在我正在根据我决定采用的特定方法来调整这个问题。
简单地说,我想做的是在用户点击一个页面后显示一条加载消息,当下一个页面完全加载(将27000家公司加载到DataGrid中),然后显示下一个网页时,该消息就会消失。订单是这样的;
- 用户点击第一页上的按钮
- "Please Wait…"(请稍候…)显示在同一页上,而下一页显示正在加载
- 下一页已完全加载
- 隐藏"请稍候……"
- 显示下一页
建议我使用async
和wait
。然而,这冻结了我的UI,无法工作(Additional information: The calling thread cannot access this object because a different thread owns it.
)。然后我开始考虑使用多个线程,这有望解决我的问题。这就是我迄今为止所尝试的;
用户已单击按钮进入下一页
public CompanyManagement()
{
InitializeComponent();
Thread thread = Thread.CurrentThread;
this.DataContext = new
{
ThreadId = thread.ManagedThreadId
};
}
调用页面的Loaded事件
private void PageLoaded(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(() =>
{
var wait = new PleaseWait("My title", "My Message", () => FillDataGrid());
wait.ShowDialog();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
FillDataGrid()是一个花费所有时间(通过OleDB检索公司)的方法,看起来是这样的
private void FillDataGrid()
{
var _cDS = new CompanyDataService();
var Companies = new ObservableCollection<CompanyModel>();
Companies = _cDS.HandleCompanySelect();
CompanyICollectionView = CollectionViewSource.GetDefaultView(Companies);
//CompanyICollectionView.SortDescriptions.Add(new SortDescription("CompanyName", ListSortDirection.Ascending));
DataContext = this;
cancelButton.Visibility = Visibility.Hidden;
if (compNameRad.IsChecked == false &&
compTownRad.IsChecked == false &&
compPcodeRad.IsChecked == false)
{
searchBox.IsEnabled = false;
}
dataGrid.SelectedIndex = 0;
SetDefaultFilter();
}
这几乎是因为加载了下一个页面,显示了"Please Wait….",但我随后得到了错误:调用线程无法访问此对象,因为另一个线程拥有它。我是否错误地使用了线程功能?
编辑:我也应该显示PleaseWait
!
public partial class PleaseWait : Window
{
readonly Action _action;
public PleaseWait(string title, string message, Action action)
{
_action = action;
InitializeComponent();
this.Title = title;
this.label.Content = message;
}
private async void loaded(object sender, RoutedEventArgs e)
{
await Task.Run(() => _action());
this.Close();
}
}
编辑:用于StepUp;
private void PageLoaded(object sender, RoutedEventArgs e)
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
{
var wait = new PleaseWait("My title", "My Message", () => FillDataGrid());
wait.ShowDialog();
Dispatcher.Run();
}));
}
正如MSDN所说:
通常,WPF应用程序从两个线程开始:一个用于处理渲染和另一个用于管理UI。渲染线程有效地隐藏在后台运行,同时UI线程接收输入、处理事件、绘制屏幕和运行应用程序代码。大多数应用程序使用单个UI线程,尽管在某些情况下最好使用几个。
然后MSDN说:
UI线程在一个名为Dispatcher的对象内对工作项进行排队。Dispatcher按优先级选择工作项,并运行每个工作项一个完成。每个UI线程必须至少有一个Dispatcher,并且每个Dispatcher可以在恰好一个线程中执行工作项。
并且您正在从非UI线程更新UI,因此您捕获了一个异常。
private void FillDataGrid()
{
var _cDS = new CompanyDataService();
var Companies = new ObservableCollection<CompanyModel>();
Companies = _cDS.HandleCompanySelect();
CompanyICollectionView = CollectionViewSource.GetDefaultView(Companies);
//CompanyICollectionView.SortDescriptions.Add(new SortDescription("CompanyName", ListSortDirection.Ascending));
DataContext = this;
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
{
cancelButton.Visibility = Visibility.Hidden;
if (compNameRad.IsChecked == false && compTownRad.IsChecked == false &&
compPcodeRad.IsChecked == false)
{
searchBox.IsEnabled = false;
}
dataGrid.SelectedIndex = 0;
}));
SetDefaultFilter();
}
更新:此代码不符合条件:
private void PageLoaded(object sender, RoutedEventArgs e)
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
{
var wait = new PleaseWait("My title", "My Message", () => FillDataGrid());
wait.ShowDialog();
Dispatcher.Run();
}));
}
因为您正在PageLoaded
内部创建新线程。这意味着在UI线程中创建新线程,并从非UI线程更新UI。也就是说,您正在更新没有Dispatcher
的UI。您应该在新创建的thread
中使用Dispatcher
来更新UI
。