lamarmora
lamarmora

Reputation: 1126

wpf forcing update UI window during a procedure

I need only to show a custom control (a clock with rotating hands) and with this to replace the mouse cursor, if I use a file .cur or .ani to replace the mouse cursor Me.CUrsor = New Cursor("absolute path of the .ani file") there is no problem: I can change the cursor during a procedure: but the quality of the animation is very bad, and, also for other reasons, I'd prefer to use my little user-control. The problem is that if I write:

Me.gridScreen.Visibility = Visibility.Visible

' some operations that takes about 1 second

Me.gridScreen.Visibility = Visibility.Hidden

(gridScreen is the grid that contains the user-control)

Obviously I can see nothing, because the update of the UI happens at the end of the procedure. I have tried Me.UpdateLayout(), but it doesn't work.

I have tryed to use the dispacker in many way but none that works :-(

This is my lost attempt:

(uCurClock is the usercontrol, gridScreen a Grid placed at the top-level in the window, with trasparent background, that contains the usercontrol)

Private Sub showClock()G
    Dim thread = New System.Threading.Thread(AddressOf showClockIntermediate)
    thread.Start()
End Sub

Private Sub hideClock()
    Dim thread = New System.Threading.Thread(AddressOf hideClockIntermediate)
    thread.Start()
End Sub

Private Sub showClockIntermediate()
    Me.Dispatcher.BeginInvoke(DispatcherPriority.Normal, New Action(AddressOf showClockFinale))
End Sub

Private Sub hideClockIntermediate()
    Me.Dispatcher.BeginInvoke(DispatcherPriority.Normal, New Action(AddressOf hideClockFinale))
End Sub

Private Sub showClockFinale()
    Dim pt As Point = Mouse.GetPosition(Nothing)
    Me.uCurClock.Margin = New Thickness(pt.X - 9, pt.Y - 9, 0, 0)
    Me.gridScreen.Visibility = Visibility.Visible
    Me.Cursor = Cursors.None
    Me.UpdateLayout()
End Sub

Private Sub hideClockFinale()
    Me.gridScreen.Visibility = Visibility.Hidden
    Me.Cursor = Cursors.Arrow
    Me.UpdateLayout()
End Sub

Private Sub u_MouseMove(ByVal sender As System.Object, ByVal e As MouseEventArgs) Handles gridScreen.MouseMove
    Dim pt As Point = e.GetPosition(Nothing)
    Me.uCurClock.Margin = New Thickness(pt.X - 9, pt.Y - 9, 0, 0)

    e.Handled = True
End Sub

Private Sub u_MouseEnter(ByVal sender As System.Object, ByVal e As MouseEventArgs) Handles gridScreen.MouseEnter
    Me.uCurClock.Visibility = Visibility.Visible

    e.Handled = True
End Sub

Private Sub u_MouseLeave(ByVal sender As System.Object, ByVal e As MouseEventArgs) Handles gridScreen.MouseLeave
    Me.uCurClock.Visibility = Visibility.Hidden

    e.Handled = True
End Sub

PIleggi

Upvotes: 0

Views: 2493

Answers (2)

C J
C J

Reputation: 173

reference DispatcherFrame Class Reference

good ole DoEvents for WPF!!!

Public Sub DoEvents()
    Dim frame As New DispatcherFrame()
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, New DispatcherOperationCallback(AddressOf ExitFrame), frame)
    Dispatcher.PushFrame(frame)
End Sub

Public Function ExitFrame(ByVal f As Object) As Object
    CType(f, DispatcherFrame).Continue = False
    Return Nothing
End Function

Upvotes: 1

Ian Griffiths
Ian Griffiths

Reputation: 14557

While the following code will do what you ask for, I suspect it won't actually help you, since you've mentioned animation. You're going to need to use multiple threads. However, just to demonstrate why that is, here's something that answers the question you've asked:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click

    uc1.Visibility = Visibility.Visible
    Cursor = Cursors.Wait

    ' Push visibility changes now.
    ' (Sort of like DoEvents - and a horrible idea for exactly the same
    ' reasons that DoEvents was a total train wreck. Never actually do
    ' this - use a background thread instead.)
    Dim df As New DispatcherFrame(True)
    Dispatcher.BeginInvoke(Sub() df.Continue = False, DispatcherPriority.ContextIdle)
    Dispatcher.PushFrame(df)

    Thread.Sleep(1000)

    ClearValue(CursorProperty)
    uc1.Visibility = Visibility.Hidden

End Sub

Assuming you have some usercontrol called uc1 on the page, this will force it to be visible while your slow procedure runs.

But no animations will run. The problem is, if you're doing something slow on the UI thread, the UI thread can't do anything else - it can't run animations, it can't respond to user input. Basically the UI is frozen out. The only reason the code shown here even makes the user control visible is that it basically says "do any outstanding UI thread work now", which has the side effect of processing your change to the Visible property.

But animations happen on the UI thread too.

If you want to do this properly, you need to do the work on a background thread, possibly by using the BackgroundWorker, or by writing your own threading code.

Upvotes: 1

Related Questions