locked
WPF Control Background Image Change Delay RRS feed

  • Question

  • Hi. I have a WPF application that takes a screenshot of the contents of a WindowsFormsHost control and applies it to the background of a Grid control.  The Grid acts as a transition screen from the WindowsFormsHost to other WPF controls.  This is done to make a smooth transition effect and to avoid air space issues.  I first capture the WinFormsHost control image as a bitmap and apply it to the Grid background.  Then programmatically change the visibility of the transition Grid to visible.  Then do an opacity animation to smoothly show another control.  It works perfectly about 70 to 90 percent of the time depending on what computer I test the application on.  The problem is that the transition Grid background is not being rendered fast enough or at the correct time.  So that occasionally I am seeing a screenshot from a previous transition which does not match the current screen image at transition time.

    If I could somehow ensure that the transition image was drawn before the Grid is made visible, it would work everytime.  I just cannot see how to do this.  The application seems to always wait until the last minute to do any rendering.

    I have tried to force rendering with Dispatcher.Invoke() and Dispatcher.BeginInvoke() methods.  I have also tried to delay the time when the Grid is made visible with a Dispatcher Timer, but no matter how much time there is between the call setting the background image and setting the visibility, the Grid does not always update.  I have also tried things like InvalidateVisual() with no luck.  

     

            


    • Edited by t0mr Saturday, October 8, 2016 6:36 AM
    Saturday, October 8, 2016 5:27 AM

Answers

  • I did come up with a work around for this problem.  I do a transition animation before I take the screenshot.  The transition animation results in a lower resolution image than the WinFormsHost control would normally show.  Then I take the screenshot of the lower resolution image which can be applied to the transition grid control, after it is made visible, fast enough that there is never a flicker.  Then when I want to return to the WinFormsHost control, I run through this process in reverse.  My application now works without flicker and runs smoothly on most computers I have tried.  On some computers with large monitors, there is sometimes a slight delay when rendering the transition screen, but still no flicker.  The delay is about half a second, and I assume it has to do with the larger area that transition image must be rendered to.

     Also, the above was done in combination with method/attempt #2.  The flicker still occurs when in combination with attempt #1.





    Saturday, October 15, 2016 3:49 AM

All replies

  • >>If I could somehow ensure that the transition image was drawn before the Grid is made visible, it would work everytime.  I just cannot see how to do this.  The application seems to always wait until the last minute to do any rendering.

    This is because the rendering happens on a dedicated thread of its own in WPF. The application starts up with two threads, one for handling the rendering and another for managing the UI (this one is also known as the UI or dispatcher thread). The rendering thread effectively runs hidden in the background while the UI thread receives input, handles events, paints the screen, and runs application code. Please refer to MSDN for more information about the threading model in WPF: https://msdn.microsoft.com/en-us/library/ms741870(v=vs.110).aspx.

    As opposed to Windows Forms, there is no built-in immediate rendering mode in WPF I am afraid. Please refer to the following thread for more information about this: https://social.msdn.microsoft.com/Forums/en-US/f7bffbc8-a281-44fb-888a-dd400e737ebe/the-most-efficient-way-to-draw-in-wpf?forum=wpf

    This blog post should be helpful as well: http://kynosarges.org/WpfPerformance.html

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Saturday, October 8, 2016 8:47 AM
  • Hi Magnus.  Thanks for replying.  I researched some more into the rending and UI threads, and checked into your links.  I want to show you some code so you can see specifically what I have tried to get the result I am after.

    Attempt 1 - Works poorly, but attempt works better with a line of code that first sets background to nothing or black:

                    TransitionScreen.Visibility = Visibility.Visible
                    TransitionScreen.Background = Nothing
                    TransitionScreen.Background = New ImageBrush(BitmapToImageSource(GrabScreenshot(ScreenWidth, ScreenHeight)))
    
    		State = "WPFControlMode"
                    SwitchState()

    Attempt 2 - Best results so far.  Works fair to well depending on computer.  Increasing the counter value does not improve results.  The appearance of the TransitionScreen will be delayed with a larger counter value, but will have the same chance of showing a previous screenshot (about 1 in 5 times on my laptop):

    Case "Transition" 'Grab Screen Shot and apply to transition screen...' TransitionScreen.Visibility = Visibility.Visible TransitionScreen.Background = Nothing TransitionScreen.Background = New ImageBrush(BitmapToImageSource(GrabScreenshot(ScreenWidth, ScreenHeight))) End Select 'The following code is inside a loop...' If State = "Transition" Then TransitionCounter += 1 If TransitionCounter = 25 Then TransitionCounter = 0 TransitionScreen.Visibility = Visibility.Visible TransitionScreen.Background = Nothing TransitionScreen.Background = New ImageBrush(BitmapToImageSource(GrabScreenshot(ScreenWidth, ScreenHeight))) State = "WPFControlMode" SwitchState() End If

    End If


    Attempt 3, 4, 5, ... - Attempts try to force rendering of control by working through Dispatcher.  These attempts do not appear to help and often make the problem worse.  They have been used in combination with both attempts 1 and 2.

    Case "Transition" 
                    'Grab Screen Shot and apply to transition screen...'
    
                    TransitionScreen.Visibility = Visibility.Visible
                    TransitionScreen.Background = Nothing
                    TransitionScreen.Background = New ImageBrush(BitmapToImageSource(GrabScreenshot(ScreenWidth, ScreenHeight)))
    
    FlushWindowsMessageQueue()
    
                    
    End Select
    
    'Tried many variations of the following procedure.  Every dispatcher priority with both BeginInvoke and Invoke methods, as well as calling the methods through Application and TransitionScreen objects.' 
    
    Private Sub FlushWindowsMessageQueue()
        Application.Current.Dispatcher.Invoke( _
            New Action(AddressOf DummySub), _
            DispatcherPriority.Background, _
            New Object() {})
    End Sub
    
    Private Sub DummySub()
    End Sub

    There were more attempts also, but this should give you an idea of what I have tried.  This is a real puzzle.  This is the last major problem with a fairly involved project, and I am pretty much invested in the transition screen at this point.  If you can think of anything, I will greatly appreciate it.  Thanks.

     


    • Edited by t0mr Saturday, October 15, 2016 4:14 AM
    Sunday, October 9, 2016 12:58 PM
  • I did come up with a work around for this problem.  I do a transition animation before I take the screenshot.  The transition animation results in a lower resolution image than the WinFormsHost control would normally show.  Then I take the screenshot of the lower resolution image which can be applied to the transition grid control, after it is made visible, fast enough that there is never a flicker.  Then when I want to return to the WinFormsHost control, I run through this process in reverse.  My application now works without flicker and runs smoothly on most computers I have tried.  On some computers with large monitors, there is sometimes a slight delay when rendering the transition screen, but still no flicker.  The delay is about half a second, and I assume it has to do with the larger area that transition image must be rendered to.

     Also, the above was done in combination with method/attempt #2.  The flicker still occurs when in combination with attempt #1.





    Saturday, October 15, 2016 3:49 AM