【问题标题】:Showing busy indicator on a STA thread在 STA 线程上显示忙碌指示符
【发布时间】:2015-08-04 11:53:40
【问题描述】:

我有一个很长的操作,我想显示扩展工具包繁忙指示器。我之前发过一篇关于这个的帖子,它是固定的Wpf Extended toolkit BusyIndicator not showing during operation。但是,在该调用期间,我必须与 UI 元素(画布)进行交互,并且我得到一个“调用线程必须是 STA,因为许多 UI 组件都需要这个”。我知道(现在)一个后台工作人员(见代码):

     private void CboItemId_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        
        BackgroundWorker _backgroundWorker = new BackgroundWorker();
        _backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
        _backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);
        ItemSearchBusyIndicator.IsBusy = true;
      
       // Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
        if (RdoItemSearch.IsChecked == false) return;
        ///backgroundWorker_DoWork(null, null);
        
        if (CboItemId.SelectedValue == null) return;
        if (CboItemId.SelectedValue.ToString() != string.Empty)
        {
            selectedItem = CboItemId.SelectedValue.ToString();
            _backgroundWorker.RunWorkerAsync();
        }
       // Mouse.OverrideCursor = System.Windows.Input.Cursors.Arrow;
    }

    public void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        
            LoadItemData(selectedItem);
    }

使用 MTA,不能设置为 STA。所以我尝试在自己的线程中调用使用 UI 元素的内部函数:

       public void LoadItemData(string itemId)
    {
        
        Axapta ax = new Axapta();
        files.Clear();
        try
        {
            ax.Logon(Settings.Default.Server, null, Settings.Default.Test, null);
            AxaptaContainer path = (AxaptaContainer)ax.CallStaticClassMethod(Settings.Default.ClassName, Settings.Default.ItemData, itemId);
            for (int i = 1; i <= path.Count; i++)
            {
                AxaptaContainer somestring = (AxaptaContainer)path.get_Item(i);
                for (int j = 1; j <= somestring.Count; j += 2)
                {
                    string extension = Path.GetExtension(somestring.get_Item(j + 1).ToString().ToLower());
                    if (extension == ".jpg"
                        || extension == ".jpeg"
                        || extension == ".gif"
                        || extension == ".png"
                        || extension == ".bmp"
                        || extension == ".pdf")
                        /* key=path - value=description */
                        files.Add(somestring.get_Item(j + 1).ToString(), somestring.get_Item(j).ToString());
                }
            }
            
           // _canvas.Children.Clear();
            Thread t = new Thread(new ThreadStart(LoadPictures));
            t.SetApartmentState(ApartmentState.STA);
            t.Start();
           
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
        finally
        {
            ax.Logoff();
        }
    }

这里是我与画布元素交互的地方:

      private void LoadPictures()
    {

        foreach (DictionaryEntry filePath in files)
        {

            try
            {

                Picture p = new Picture();
                ToolTip t = new ToolTip();
                t.Content = filePath.Value;
                p.ToolTip = t;
                TextBlock tb = new TextBlock();
                tb.Text = filePath.Value.ToString();
                Canvas.SetTop(tb, y);
                Canvas.SetLeft(tb, x);

                    p.ImagePath = filePath.Key.ToString();
                    p.OriginalImagePath = filePath.Key.ToString();
                    p.ImageName = filePath.Value.ToString();
                    _canvas.Children.Add(p); //<-------This is where i seem to error
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error:" + ex.Message,"File Load Error",MessageBoxButton.OK,MessageBoxImage.Error);
            }
        }
    }

但我得到一个“调用线程无法访问此对象,因为不同的线程拥有它” 我不知道如何在没有后台工作程序的情况下显示 BusyIndi​​cator 时调用长时间运行的 (LoadItemData()) 函数。任何帮助表示赞赏

【问题讨论】:

  • 你为什么不让它更简单,你使用 control.Dispatcher 对象?
  • 看这里stackoverflow.com/questions/7428260/…,wpf应该类似
  • 您必须从 BGW 的 RunWorkerCompleted 事件处理程序调用 LoadPictures()。

标签: c# wpf multithreading wpftoolkit


【解决方案1】:

有多种方法:

1) Async binding,不推荐,但确实有。您可以在属性 getter 中运行长时间运行的任务,框架将阻止 UI 阻塞,当它完成时 - UI 将得到更新。

2) 使用BackgroundWorkerTask/Thread 运行代码,但将其调用到 UI 线程中。在您的示例中:

Dispatcher.InvokeAsync(() => _canvas.Children.Add(p));

3)你可以完全阻塞主窗口的UI线程,没有问题。但是要表明它很忙,您可以在另一个线程中创建窗口并在那里显示忙碌状态(运行动画等):

        var thread = new Thread(() =>
        {
            var window = new SomeWindow();
            window.ShowDialog();
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.IsBackground = true;
        thread.Start();

【讨论】:

  • 当使用 Dispatcher.Invoke(() => _canvas.Children.Add(p));我收到一个错误:“无法将 lambda 表达式转换为类型 'System.Delegate',因为它不是委托类型”
  • 我将第二个建议转换为: Dispatcher.Invoke((Action)(() => _canvas.Children.Add(p)));因为它不会编译。但我仍然收到“调用线程无法访问此对象,因为不同的线程拥有它”错误。是因为我用一个新线程调用 LoadPicture() 吗?
  • 我不确定,这可能意味着我的答案是错误的。您可以发布完整的异常消息吗?
猜你喜欢
  • 1970-01-01
  • 2014-10-04
  • 1970-01-01
  • 1970-01-01
  • 2018-06-18
  • 2017-05-25
  • 1970-01-01
  • 1970-01-01
  • 2019-01-20
相关资源
最近更新 更多