1

I inherited a winforms app. It uses a third-part-closed control that renders documents and photos... It has only sync methods for opening a document. The problem is that my clients are dealing with really big documents (in the area of 2GB!!!) and opening these docs really "block" the UI thread... which is bad...

Common sense would make you think "Just off-load it to a background thread" but the question is "HOW"! See, to alter the control (because calling "Open" causes it to be altered) I need to Invoke it, and that causes the code to run o UI thread again... locking it up...

So I turned the table upside down. What if instead of creating the control on the main thread and passing it to a background thread for processing, I could create the control on the background thread, load it up (avoiding this way the cross-thread exception) and, when done, feed it to the main thread?!?

Right now what I need is to know how to definitively handle a control to another thread, and not only temporally...

Leonardo
  • 10,737
  • 10
  • 62
  • 155
  • 2
    The control is going to have to be designed to support this, and presumably that isn't the case. It sounds like its core design is simply flawed. The only really good solutions are to fix that, and without the ability to open it up, you likely can't do that. – Servy Dec 15 '14 at 18:33
  • Yeah. Sounds totally like "we only had a hammer and an incompetent craftsman so we designed this control to look like a nail". Definitely using it on large files is not in the scope - or they accepted the issue. Talk to the supplier, they will have to fix that. No way to do that from your end. – TomTom Dec 15 '14 at 18:40
  • Note that calling Invoke from a background thread needn't tie up the UI thread for very long at a time. If the loading task is GUI-less and merely needs to, say, update a progress bar in the GUI, using Invoke to update the progress bar wouldn't tie the UI up for any significant length of time, so the UI would remain responsive. The biggest trouble would then be how to correctly hand the loaded document from the background thread to the GUI thread--a problem that I don't understand very well (related to processor caches and memory visibility) but that @Servy probably does. – adv12 Dec 15 '14 at 18:56
  • (My above comment depends on there being a GUI-less data model that can be used to populate the third-party control once data loading is complete. If "loading" is really just the process of adding data directly from the file to the third-party control, then loading on a background thread doesn't buy you much, except that you would have many short calls to Invoke between which the GUI thread could process other events, meaning your GUI would stay at least somewhat responsive.) – adv12 Dec 15 '14 at 19:09

2 Answers2

1

I'm not sure if this is possible but you could try to:

  • create a new form on a secondary thread (this form will host your fancy control)
  • load the document from this secondary UI. It will be blocked but you can hide it and only display a loading message on the main UI.
  • when the job is finished transfer the 'work' to main UI and main thread.

It's just an idea.

Community
  • 1
  • 1
B0Andrew
  • 1,725
  • 13
  • 19
  • I tested it and I can say that it works (at least with a button). On the secondary form I added a button with `public` modifier. After the secondary form is loaded I transfer the button to primary form. – B0Andrew Dec 15 '14 at 19:37
0

What you are asking to do is impossible. A Winforms control's thread affinity is determined when that control is created, and it cannot be changed.

The best solution is to not use that control. I doubt there's anything it does that cannot be implemented correctly and competently by someone else.

If you are okay running a completely different window in a second STA thread, then that would be the next best thing. That particular window will still be frozen while the document loads, but at least your main UI would still be okay. Note that you should not try to mix and match controls from different threads in the same window; that will lead to all kinds of headaches.

Finally, as a complete hack, you might consider going ahead and calling this Open() method in a background thread in spite of the control being owned by the main UI thread. On the admittedly shaky assumption that the only time that control will actually attempt to access the UI component itself would be at the very end of the Open() method operation, you can go ahead and catch the InvalidOperationException that is thrown, and use that as your signal that the document loading has completed. Then just invalidate the control in the main UI thread.

I'd give the odds of this last suggestion working no better than 50/50. It will depend on what the control actually does with the loaded data, and if it's some kind of composite control where it's relying on actually taking the result of its loading and copying that to a control as part of the Open() method, that part might fail and the control would not wind up properly initialized.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136