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

时间:2015-12-15 14:06:53

标签: c# wpf multithreading

我之前发过一个类似的问题,但现在我正在根据我决定采用的具体方法来定制这个问题。

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

  1. 用户点击第一页上的按钮
  2. “Please Wait ....”在下一页显示在同一页面上 正在加载
  3. 下一页已满载
  4. 隐藏“请等待....”
  5. 显示下一页
  6. 我建议使用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
            };
        }
    

    调用Page的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();
            }));
        }
    

1 个答案:

答案 0 :(得分:0)

As MSDN says:

  

通常,WPF应用程序以两个线程开头:一个用于处理   渲染和另一个用于管理UI。渲染线程   在UI线程接收时有效地隐藏在后台   输入,处理事件,绘制屏幕并运行应用程序代码。   尽管在某些情况下,大多数应用程序使用单个UI线程   最好使用几个。

Then MSDN says:

  

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。您应该在新创建的Dispatcher内使用thread来更新UI