1

/Disclaimer, this is my first time working with WPF and with multi-threading, so bear with me if I am making some big mistakes/

So I have an application with a tabcontrol. In one of the Tabs I intend to load in a visio file via the usual windows form host + Visio viewer activeX control etc... And it works perfectly. The only issue is that when I load in the document the UI freezes for 20 seconds (as I am loading in rather huge files). As I read this is because my application is running on a simple thread. So I was trying to implement a background worker to keep the UI reactive while the background thread is running.

When I instantiate my UserControl then I add to the Tab (Initialpath is the filepath of the visio file):

        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;
        }

And When it creates the new 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;

    }

And for this line:

this.viewer = new AxVisioViewer.AxViewer();

I get: : 'ActiveX control 'f8cf7a98-2c45-4c8d-9151-2d716989ddab' cannot be instantiated because the current thread is not in a single-threaded apartment.'

I read that the backgroundworker is not capable to modify the UI elements (correct me if I am wrong)

I also saw this thread: Single-threaded apartment - cannot instantiate ActiveX control

But I am not sure how to implement it (this STA apartment state business) and when I tried, the visio viewer simple crashed when trying to open the document.

I need some guideline how to approach this issue, cause my goal would be to having a loading page to display with an animation until the Document is finished loading/rendering so I can display it.

Thank you in advance for the answers.

UPDATE: I also tried the following approach:

    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;
    }

But in this case I get the following message on UC and the this.Host.chilld=DiagV when I am in the DisplayV method: System.InvalidOperationException: 'Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.'

Anstetten
  • 11
  • 3
  • it would help if you tell us what library are you using? what is AxVisioViewer.AxViewer? – Bizhan Mar 19 '20 at 17:15
  • 1
    It's simply impossible to load a UI control from another thread. If that library contains an async loader, you can use that method to load without blocking UI thread, otherwise you're out of luck – Bizhan Mar 19 '20 at 17:19
  • Hi! The AxVisioViewer.AxViewer simply is the reference to the Visio Viewer API (https://learn.microsoft.com/en-us/office/vba/api/visio.viewerref.aboutprogramming) and the created object is a Visio Viewer Object (https://learn.microsoft.com/en-us/office/vba/api/visio.viewer). I also tried by creating a thread manually (see update) – Anstetten Mar 19 '20 at 17:37
  • As I said, you can't create a control in another thread. The library should provide a thread-safe approach, or otherwise it can't be done. The closest thing to achieving it is to put only Load() in another thread but I don't think that would work – Bizhan Mar 19 '20 at 19:16

1 Answers1

0

It's simply impossible to load a UI control from another thread. However you could solve the freezing problem if the library had provided a thread-safe approach (e.g. AxViewer.LoadAsync) so the only way to make viewer.Load bearable for the user is to delay viewer.Load operation until the window is loaded:

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);
}

And more importantly, delete the background worker code. It doesn't do any good here.

Bizhan
  • 16,157
  • 9
  • 63
  • 101