Multithreading and WPF

Multithreading and WPF

jbooth
Advocate Advocate
1,461 Views
4 Replies
Message 1 of 5

Multithreading and WPF

jbooth
Advocate
Advocate
Good afternoon,

I have a modeless command set up, using the managed API to create a large number of entities (think 5000+) based on external data provided by a point-of-sales program. Currently this command uses Windows.Forms to display a modeless dialog that notifies the user about its progress (which works just fine).

I've been trying (and failing) to rewrite the interface as window under WPF.

My first attempt was as follows:
{code}
Dim Splash as New SplashDialog() 'Inherits from System.Windows.Window
Dim WPF As New Windows.Interop.WindowInteropHelper(Splash)
WPF.Owner = AcAp.Application.DocumentManager.MdiActiveDocument.Window.Handle
Splash.Show()
{code}

What I noticed was that any delegates invoked through the splash window's Dispatcher would not update the UI. I think this is because this window is on the same thread as my command. Therefore the UI won't update until my thread is complete (not what I want).

I also tried creating the window on a new thread:

{code}
Protected Shared Splash As SplashDialog() 'Inherits from System.Windows.Window
Protected Shared WithEvents Worker As New System.ComponentModel.BackgroundWorker()
Protected Shared Sub Work(ByVal Sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles Worker.DoWork
Splash = New SplashDialog()
Dim WPF As New Windows.Interop.WindowInteropHelper(Splash)
WPF.Owner = AcAp.Application.DocumentManager.MdiActiveDocument.Window.Handle
Splash.Show()
System.Windows.Threading.Dispatcher.Run()
End Sub
{code}

The above approach also doesn't work. The variable holding a reference to the form is null, and I think it's because the main thread isn't waiting for the UI thread to finish initializing the new window.


Any advice?

Regards,
JB
0 Likes
1,462 Views
4 Replies
Replies (4)
Message 2 of 5

Anonymous
Not applicable
Not sure why you're making the handle of the active document window the owner of
your WindowInteropHelper.

You're not making clear the context your code runs in.

Is it a called from a command handler? If so, what are
the CommandFlags specified in the CommandMethod
attribute?

--
http://www.caddzone.com

AcadXTabs: MDI Document Tabs for AutoCAD
Supporting AutoCAD 2000 through 2011

http://www.acadxtabs.com

Email: string.Format("{0}@{1}.com", "tonyt", "caddzone");

wrote in message news:6388878@discussion.autodesk.com...
Good afternoon,

I have a modeless command set up, using the managed API to create a large number
of entities (think 5000+) based on external data provided by a point-of-sales
program. Currently this command uses Windows.Forms to display a modeless dialog
that notifies the user about its progress (which works just fine).

I've been trying (and failing) to rewrite the interface as window under WPF.

My first attempt was as follows:
{code}
Dim Splash as New SplashDialog() 'Inherits from System.Windows.Window
Dim WPF As New Windows.Interop.WindowInteropHelper(Splash)
WPF.Owner = AcAp.Application.DocumentManager.MdiActiveDocument.Window.Handle
Splash.Show()
{code}

What I noticed was that any delegates invoked through the splash window's
Dispatcher would not update the UI. I think this is because this window is on
the same thread as my command. Therefore the UI won't update until my thread is
complete (not what I want).

I also tried creating the window on a new thread:

{code}
Protected Shared Splash As SplashDialog() 'Inherits from System.Windows.Window
Protected Shared WithEvents Worker As New
System.ComponentModel.BackgroundWorker()
Protected Shared Sub Work(ByVal Sender As Object, ByVal e As
System.ComponentModel.DoWorkEventArgs) Handles Worker.DoWork
Splash = New SplashDialog()
Dim WPF As New Windows.Interop.WindowInteropHelper(Splash)
WPF.Owner = AcAp.Application.DocumentManager.MdiActiveDocument.Window.Handle
Splash.Show()
System.Windows.Threading.Dispatcher.Run()
End Sub
{code}

The above approach also doesn't work. The variable holding a reference to the
form is null, and I think it's because the main thread isn't waiting for the UI
thread to finish initializing the new window.


Any advice?

Regards,
JB
0 Likes
Message 3 of 5

jbooth
Advocate
Advocate
Thanks for the reply. Here are the command flags I have applied to the command handler: I may have mentioned modless when I actually meant modal (sorry). I have a full document lock while my entity generation routine is running.

{code}CommandFlags.Modal Or CommandFlags.NoActionRecording Or CommandFlags.NoBlockEditor Or CommandFlags.NoPaperSpace Or CommandFlags.NoPerspective{code}

To answer your other question, I am attaching the active document handle to this window, only so it can start under WindowStartupLocation="CenterOwner".
0 Likes
Message 4 of 5

Anonymous
Not applicable
Try using CommandFlags.Sesssion, and not setting the
document window as the parent/owner.

--
http://www.caddzone.com

AcadXTabs: MDI Document Tabs for AutoCAD
Supporting AutoCAD 2000 through 2011

http://www.acadxtabs.com

Email: string.Format("{0}@{1}.com", "tonyt", "caddzone");

wrote in message news:6389226@discussion.autodesk.com...
Thanks for the reply. Here are the command flags I have applied to the command
handler: I may have mentioned modless when I actually meant modal (sorry). I
have a full document lock while my entity generation routine is running.

{code}CommandFlags.Modal Or CommandFlags.NoActionRecording Or
CommandFlags.NoBlockEditor Or CommandFlags.NoPaperSpace Or
CommandFlags.NoPerspective{code}

To answer your other question, I am attaching the active document handle to this
window, only so it can start under WindowStartupLocation="CenterOwner".
0 Likes
Message 5 of 5

jbooth
Advocate
Advocate
Thanks for the help, Tony. I did find a solution, but changing the command flags did not work (see below). I think the key to the problem was creating a new thread that had an ApartmentState = STA (single-threaded apartment). Here is what I did:

{code}
Protected Shared Splash As New SplashDialog() 'Inherits from System.Windows.Window
Protected Shared Sub ShowSplash()
Splash = New SplashDialog()
Dim WPF As New Windows.Interop.WindowInteropHelper(Splash)
WPF.Owner = AcAp.Application.DocumentManager.MdiActiveDocument.Window.Handle
Splash.Show()
System.Windows.Threading.Dispatcher.Run()
End Sub
{code}
In my main thread I can then call the above method:
{code}
Dim th As New Threading.Thread(New Threading.ThreadStart(AddressOf ShowSplash))
th.SetApartmentState(Threading.ApartmentState.STA)
th.Start()
{code}

Changing my command from modal to session did not fix the problem. I don't think it would have helped anyway, because I had trouble once before calling acedcmd (which I use for approximated ellipse entities) from session-flagged command handlers. I also found out why I needed the active document's window handle. If you do not provide it, the WPF control won't display itself on on top of the AutoCad window, and therefore is not visible.

There is still something strange going on. The constructor for the Splash dialog needs to be called when the static variable is declared, or else it stays as a null reference once the new thread is created. ie:

{code}
Protected Shared Splash As New SplashDialog() 'This works.
Protected Shared Splash As SplashDialog = Nothing 'This does not work, but I expect it to be the correct way to initialize this variable.
{code}