【问题标题】:WPF Using Multiple Threads to Display Loading MessageWPF 使用多线程显示加载消息
【发布时间】:2015-12-15 14:06:53
【问题描述】:

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

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

  1. 用户点击首页上的按钮
  2. “请稍候....”显示在同一页面上,而下一页显示 正在加载
  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();
    }

这几乎在加载下一页时起作用,显示“请稍候...”但是我得到错误:调用线程无法访问此对象,因为不同的线程拥有它。我是否错误地使用了线程功能?

编辑:我也应该显示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();
        }));
    }

【问题讨论】:

  • 您想在主UI线程上设置加载消息,在后台线程上加载数据对象,然后将数据对象传递回主UI线程并将它们绑定到UI,然后删除加载消息和更新 UI(也是 UI 线程)。在后台线程上唯一应该做的就是加载数据对象。有关示例和说明,请参阅 herehere
  • 如果您在以下回复中看到答案,请将其标记为答案。请阅读这篇文章:meta.stackexchange.com/questions/5234/…

标签: c# wpf multithreading


【解决方案1】:

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

【讨论】:

  • 感谢您的回答。我想我需要使用Thread来显示消息,而不是在FillDataGrid()方法中,也许在PageLoaded方法中?
  • 嗯,我尝试在PageLoaded方法中使用它,但仍然有同样的错误。
  • 我在最后用更新的代码更新了我的答案
  • @CBreze 如果对您有帮助,可以将其标记为答案以简化对其他人的搜索。
  • @CBreeze 随时提出任何问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-08
  • 2012-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多