views:

46

answers:

2

I am trying to give some user entertainment, and show a "please wait" window, with Marquee, during the loading of a separate complex Window. I am attempting to do this by loading the Window in a new thread, like this:


    Public Function ShowPleaseWait() As System.Threading.Thread
        Dim PleaseWait As New System.Threading.Thread(AddressOf LoadPleaseWait)
        PleaseWait.SetApartmentState(System.Threading.ApartmentState.STA)
        PleaseWait.IsBackground = True
        PleaseWait.Start()

        Return PleaseWait
    End Function

    Public Sub LoadPleaseWait()
        Dim window As New windowPleaseWait
        Try
            window.Show()
            System.Windows.Threading.Dispatcher.Run()
        Catch e As System.Threading.ThreadAbortException
            window.Close()
            window = Nothing
        End Try
    End Sub 

In the calling code, it calls ShowPleaseWait and saves the Thread for later.. To close the window, it calls Thread.Abort, on the saved thread. This in turn will causes it to enter the Catch. I have tried, many different ways, with and without the catch.

This works incredibly, the first time it is called. However, additional calls will fail at window.Show() with the exception: The calling thread cannot access this object because a different thread owns it..

This really puzzles me as the window was created one line above the call to window.Show and is local. How is it owned by a different thread? How can I fix this?

A: 

UI manipulation has to be done on the UI thread. This means that if you want to manipulate UI stuff from a background thread then you need to marshal the code/function being executed back onto the UI thread before doing the UI actions.

I would suggest you change your approach - do your "separate complex" grunt work on the background thread, and move that code away from the code that manages the window. Using patterns like MVVM or frameworks like Prism will help with this approach.

Your flow should be something like this:

  • load your main window in an empty state

  • kick off your data load UI experience (in your case, the "please wait" animation)

  • start a background thread

  • use that background thread to fetch and/or manipulate data

  • once the background thread is done, populate your ViewModel, or marshal the code execution back on to the UI thread to populate the UI

Once again, look at using MVVM or similar, this will help separate out where each of these bits of work are done (i.e. the background thread can execute in the Model and never come near the View).

slugster
This does make sense, but does not answer the question. Why does it work the first time, and not the second time?
John Christman
A man walks into his mechanic and says "My seat belt rubs my neck". The mechanic say, "Drive a motorcycle." --Unfortunately, though it is not really complex work, but rather loading a long list view. It only takes about 10 seconds, but I was hoping, as an after thought, to bolt on some user entertainment. It does look nice, when it works. I was hoping that I just needed to add something, small to fix it.
John Christman
@John, i can't answer why it works once but not again, but i stand by the comment: the list building/retrieving should be the thing that happens on the background thread, not the showing of UI. It's always nice to be able to just bolt on extra functionality, but i think the original design choice has restricted your options somewhat - time for a small refactor?
slugster