【问题标题】:WPF - Load Visio File with BackgroundworkerWPF - 使用 Backgroundworker 加载 Visio 文件
【发布时间】:2020-03-19 15:28:34
【问题描述】:

/免责声明,这是我第一次使用 WPF 和多线程,如果我犯了一些大错误,请多多包涵/

所以我有一个带有选项卡控件的应用程序。在其中一个选项卡中,我打算通过通常的 Windows 窗体主机 + Visio 查看器 activeX 控件等加载 visio 文件......它工作得很好。唯一的问题是,当我在文档中加载时,UI 会冻结 20 秒(因为我正在加载相当大的文件)。当我读到这是因为我的应用程序在一个简单的线程上运行。所以我试图实现一个后台工作者,以在后台线程运行时保持 UI 反应。

当我实例化我的 UserControl 然后我添加到选项卡(初始路径是 visio 文件的文件路径):

        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += Worker_DoWork;
        worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
        worker.RunWorkerAsync(initialpath);


        private void Worker_DoWork(object sender, DoWorkEventArgs e)
        {
        DiagramView UCworker;
        UCworker = new DiagramView((string)e.Argument);
        e.Result = UCworker;
        }

    private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
        UC = (DiagramView)e.Result;
        this.Host.Child = UC;
        }

当它创建新的 DiagramView 时:

public DiagramView(string path)
    {
        InitializeComponent();

       this.Resize += new EventHandler(this.UpdateSize);
       this.viewer = new AxVisioViewer.AxViewer();
       this.Controls.Add(this.viewer);
       this.viewer.CreateControl();
       this.viewer.Load(path);
       this.viewer.HighQualityRender = false;
       this.viewer.BackColor = Color.White;
       this.viewer.PageTabsVisible = true;
       this.viewer.ContextMenuEnabled = false;
       this.viewer.PropertyDialogEnabled = false;
       this.viewer.ToolbarVisible = true;
       this.viewer.OnSelectionChanged += Viewer_OnSelectionChanged;

    }

对于这一行:

this.viewer = new AxVisioViewer.AxViewer();

我得到:'ActiveX 控件'f8cf7a98-2c45-4c8d-9151-2d716989ddab' 无法实例化,因为当前线程不在单线程单元中。'

我读到后台工作人员无法修改 UI 元素(如果我错了,请纠正我)

我也看到了这个帖子:Single-threaded apartment - cannot instantiate ActiveX control

但我不确定如何实现它(这个 STA 公寓状态业务),当我尝试时,visio 查看器在尝试打开文档时简单崩溃。

我需要一些指南来解决这个问题,因为我的目标是让加载页面显示动画,直到文档完成加载/渲染,以便我可以显示它。

提前感谢您的回答。

更新: 我也尝试了以下方法:

    public partial class TabDiagramView : UserControl, INotifyPropertyChanged
{
    public delegate void DisplayVisio(DiagramView view);
    public DisplayVisio DelegateM;
    DiagramView UC;

    public TabDiagramView()
    {
        InitializeComponent();

        DelegateM = new DisplayVisio(DisplayV);
        Thread t = new Thread(RT);
        t.SetApartmentState(ApartmentState.STA);
        t.IsBackground = true;
        t.Start();          
    }

    #region Thread

    private void RT()
    {
        DiagramView UCworker;
        UCworker = new DiagramView(initialpath);
        Dispatcher.Invoke(DelegateM, UCworker);
    }

    private void DisplayV (DiagramView DiagV)
    {
        UC = DiagV;
        this.Host.Child = DiagV;
    }

但在这种情况下,当我在 DisplayV 方法中时,我在 UC 和 this.Host.chilld=DiagV 上收到以下消息: System.InvalidOperationException: 'Cross-thread operation not valid: Control '' 从线程访问除了创建它的线程。'

【问题讨论】:

  • 如果您告诉我们您使用的是什么库会有所帮助?什么是 AxVisioViewer.AxViewer?
  • 根本不可能从另一个线程加载 UI 控件。如果该库包含异步加载器,则可以使用该方法加载而不会阻塞 UI 线程,否则您将不走运
  • 嗨! AxVisioViewer.AxViewer 只是对 Visio 查看器 API (docs.microsoft.com/en-us/office/vba/api/…) 的引用,创建的对象是 Visio 查看器对象 (docs.microsoft.com/en-us/office/vba/api/visio.viewer)。我还尝试手动创建线程(请参阅更新)
  • 正如我所说,您不能在另一个线程中创建控件。该库应提供线程安全的方法,否则无法完成。最接近实现它的方法是仅将 Load() 放在另一个线程中,但我认为这行不通

标签: c# wpf multithreading visio


【解决方案1】:

根本不可能从另一个线程加载 UI 控件。但是,如果库提供了线程安全的方法(例如AxViewer.LoadAsync),您可以解决冻结问题,因此使用户可以忍受viewer.Load 的唯一方法是延迟viewer.Load 操作直到加载窗口:

string _path;
public DiagramView(string path)
{
    InitializeComponent();

    _path = path;
   this.Resize += new EventHandler(this.UpdateSize);
   this.viewer = new AxVisioViewer.AxViewer();
   this.Controls.Add(this.viewer);
   this.viewer.CreateControl();
   this.viewer.HighQualityRender = false;
   this.viewer.BackColor = Color.White;
   this.viewer.PageTabsVisible = true;
   this.viewer.ContextMenuEnabled = false;
   this.viewer.PropertyDialogEnabled = false;
   this.viewer.ToolbarVisible = true;
   this.viewer.OnSelectionChanged += Viewer_OnSelectionChanged;
   this.Loaded += Viewer_Loaded;
}

private void Viewer_Loaded(object sender, RoutedEventArgs e)
{
    viewer.Load(_path);
}

更重要的是,删除后台工作代码。它在这里没有任何好处。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    相关资源
    最近更新 更多