.NET

Reply
Distinguished Contributor
jbooth
Posts: 223
Registered: ‎02-17-2006
Message 1 of 5 (475 Views)

Multithreading and WPF

475 Views, 4 Replies
05-10-2010 04:09 PM
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
*Tony Tanzillo
Message 2 of 5 (475 Views)

Re: Multithreading and WPF

05-10-2010 09:04 PM in reply to: jbooth
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
Distinguished Contributor
jbooth
Posts: 223
Registered: ‎02-17-2006
Message 3 of 5 (475 Views)

Re: Multithreading and WPF

05-11-2010 07:31 AM in reply to: jbooth
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".
*Tony Tanzillo
Message 4 of 5 (475 Views)

Re: Multithreading and WPF

05-11-2010 03:00 PM in reply to: jbooth
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".
Distinguished Contributor
jbooth
Posts: 223
Registered: ‎02-17-2006
Message 5 of 5 (475 Views)

Re: Multithreading and WPF

05-12-2010 10:03 AM in reply to: jbooth
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}
Post to the Community

Have questions about Autodesk products? Ask the community.

New Post
Announcements
Do you have 60 seconds to spare? The Autodesk Community Team is revamping our site ranking system and we want your feedback! Please click here to launch the 5 question survey. As always your input is greatly appreciated.