使用多个线程显示加载消息的WPF

本文关键字:加载 消息 WPF 显示 线程 | 更新日期: 2023-09-27 18:26:55

我以前也发布过类似的问题,但现在我正在根据我决定采用的特定方法来调整这个问题。

简单地说,我想做的是在用户点击一个页面后显示一条加载消息,当下一个页面完全加载(将27000家公司加载到DataGrid中),然后显示下一个网页时,该消息就会消失。订单是这样的;

  1. 用户点击第一页上的按钮
  2. "Please Wait…"(请稍候…)显示在同一页上,而下一页显示正在加载
  3. 下一页已完全加载
  4. 隐藏"请稍候……"
  5. 显示下一页

建议我使用asyncwait。然而,这冻结了我的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();
        }));
    }

使用多个线程显示加载消息的WPF

正如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