none
Minimizing Form Region Inspector on Screen Shot RRS feed

  • Question

  • We are taking a screenshot of the desktop from within a button click event.  We want to hide or minimize our Form Region Inspector window (containing the button control) when taking the screenshot.  The code (shown below) creates a screenshot, but parts of the form are still visible in the image.  We suspect there isn’t enough time for the desktop to redraw the content following the change to Inspector.WindowState()

    Is an event triggered once the Inspector window is minimized and the content behind the window has redrawn, such as a Ready() event?

    We’ve considered a threading delay using Sleep() following the minimize and before taking the screenshot to give things time to redraw, but that just seems wrong.

        Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click

            Dim FileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".png"

            Dim WindowState = Globals.ThisAddIn.Application.ActiveInspector.WindowState

            Try

                Globals.ThisAddIn.Application.ActiveInspector.WindowState = _

                    Outlook.OlWindowState.olMinimized

                Using img As Drawing.Bitmap = New  _

                    Drawing.Bitmap( _

                        Windows.Forms.SystemInformation.VirtualScreen.Width, _

                        Windows.Forms.SystemInformation.VirtualScreen.Height)

                    Using g As Drawing.Graphics = Drawing.Graphics.FromImage(img)

                        g.CopyFromScreen( _

                            Windows.Forms.SystemInformation.VirtualScreen.Left, _

                            Windows.Forms.SystemInformation.VirtualScreen.Top, _

                            0, _

                            0, _

                            img.Size)

                        img.Save(FileName, Drawing.Imaging.ImageFormat.Png)

                        Ticket.Attachments.Add(FileName)

                    End Using

                End Using

            Catch ex As Exception

                Windows.Forms.MessageBox.Show(ex.ToString, My.Application.Info.Title)

            Finally

                Globals.ThisAddIn.Application.ActiveInspector.WindowState = WindowState

            End Try

        End Sub

    We're wishing there was a post resize event, such as Form.Resized()



    • Edited by Peter Schlosser Monday, December 30, 2013 10:26 PM grammer correction
    Monday, December 30, 2013 10:12 PM

Answers

  • Thanks for the reply, Eugene.  And thanks for the help creating screenshots, but we think we have it handled.  None of the examples cited appear to omit the window initiating the screenshot and that’s the problem we are writing about.

     

    Using a delay seems the only option, except Threading.Thread.Sleep() blocks the Outlook window (behind our form) from repainting following the call to minimize the Inspector window of our FormRegion.  Traces of the minimized window are still visible in the screenshot.

     

    We found a work-around that may work for others.  If we start a new thread that pauses and takes the screenshot, waiting for the thread to complete seems to allow Outlook time to repaint what it needs following the property change to minimize the window.

     

    Below we’ve provided the Click event handler from our AddIn.FormRegion and the TakeScreenShot() task thread.

     

        ' handles user request to take screenshot

        Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click

            Dim FileName As String = System.IO.Path.GetTempPath() + "screenshot.png"

            Dim ActiveInspector As Outlook.Inspector = _

                Globals.ThisAddIn.Application.ActiveInspector

            Dim WindowState As Outlook.OlWindowState = ActiveInspector.WindowState()

            Try

                Button3.Enabled = False

                ActiveInspector.WindowState = Outlook.OlWindowState.olMinimized

                Dim myTask = Threading.Tasks.Task(Of Boolean).Factory.StartNew( _

                    Function() Common.TakeScreenShot(FileName))

                myTask.Wait()

                If myTask.Result = True Then

                    Me.OutlookItem.Attachments.Add(FileName)

                End If

                myTask = Nothing

            Catch ex As Exception

                Windows.Forms.MessageBox.Show(ex.ToString, My.Application.Info.Title)

            Finally

                ActiveInspector.WindowState = WindowState

                Runtime.InteropServices.Marshal.ReleaseComObject(ActiveInspector)

                Button3.Enabled = True

            End Try

        End Sub

     

    Module Common

     

        ' takes a screenshot of entire desktop, all display regions.

        Public Function TakeScreenShot(ByVal filename As String, _

                                       Optional ByVal delay As Integer = 2000) As Boolean

            Try

                ' delay giving time for windows to minimize and the desktop to redraw

                Threading.Thread.Sleep(delay)

     

                ' take the screenshot

                Using img As Drawing.Bitmap = _

                    New Drawing.Bitmap( _

                        Windows.Forms.SystemInformation.VirtualScreen.Width, _

                        Windows.Forms.SystemInformation.VirtualScreen.Height)

                    Using g As Drawing.Graphics = Drawing.Graphics.FromImage(img)

                        g.CopyFromScreen( _

                            Windows.Forms.SystemInformation.VirtualScreen.Left, _

                            Windows.Forms.SystemInformation.VirtualScreen.Top, _

                                              0, 0, img.Size)

                        img.Save(filename, Drawing.Imaging.ImageFormat.Png)

                        Return True

                    End Using

                End Using

            Catch ex As Exception

                Windows.Forms.MessageBox.Show(ex.ToString, My.Application.Info.Title)

            End Try

            Return False

        End Function

     

    End Module


    • Edited by Peter Schlosser Thursday, January 9, 2014 7:53 PM formatting changes.
    • Marked as answer by Peter Schlosser Thursday, January 9, 2014 8:44 PM
    Thursday, January 9, 2014 7:49 PM

All replies

  • Hi Peter,

    Which version of Outlook are you using and which type of Form Region are you developing with?

    What do you mean parts of the form are still visible in the image? Would you mind sharing with us an screen shot?

    I test the code in Outlook 2013 by creating an Adjoining From. The code works well for me as a champion. The code will hide the inspector when it taking the screen shot and show the inspector after finishing the screen shot.

    I suggest you setting the break point at the line of:

    Globals.ThisAddIn.Application.ActiveInspector.WindowState = Outlook.OlWindowState.olMinimized
    

     , and run the code step by step. Would it hide the inspector successfully?

    >> Is an event triggered once the Inspector window is minimized and the content behind the window has redrawn, such as a Ready() event?<<
    No, there is no such kind of events (Refer to Inspector events).

    Best regards
    Fei


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.


    Tuesday, December 31, 2013 9:37 AM
    Moderator
  • Hello Peter,

    > Is an event triggered once the Inspector window is minimized and the content behind the window has redrawn, such as a Ready() event?

    There is no such an event. Moreover, I am not sure whether the Windows API provides something for this.

    > We’ve considered a threading delay using Sleep() following the minimize and before taking the screenshot to give things time to redraw, but that just seems wrong.

    The Sleep method of the System.Threading.Tread class is the only possible solution in that case. However, you can try to use Windows API functions. As I wrote earlier, I am not sure whether it helps.

    Anyway, I have reproduced the issue you described above. Note, the animation is controlled by the OS (not Outlook). In that case the only possible way is to detect the OS version and increase the delay after setting the WindowState property of the Inspector class (depending on the OS version). For example, starting a delay from 100 milliseconds I tried to create a screenshot. Each time the value was increased by 100 milliseconds and here is the code which does the magic on my PC with Outlook 2013 and Windows 8:

    Dim FileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".png"
    Dim inspector As Outlook.Inspector = OutlookApp.ActiveInspector
    Dim WindowState = inspector.WindowState()
    Try
       inspector.WindowState = Outlook.OlWindowState.olMinimized
    
       System.Threading.Thread.Sleep(600)
    
       Using img As Drawing.Bitmap = New  _
             Drawing.Bitmap( _                Windows.Forms.SystemInformation.VirtualScreen.Width, _    Windows.Forms.SystemInformation.VirtualScreen.Height)
             Using g As Drawing.Graphics = Drawing.Graphics.FromImage(img)
                   g.CopyFromScreen( _                        Windows.Forms.SystemInformation.VirtualScreen.Left, _     Windows.Forms.SystemInformation.VirtualScreen.Top, _ 
                            0, _
                            0, _
                            img.Size)
                   img.Save(FileName, Drawing.Imaging.ImageFormat.Png)
    
                   MessageBox.Show(FileName)
                End Using
            End Using
        Catch ex As Exception
            Windows.Forms.MessageBox.Show(ex.ToString, My.Application.Info.Title)
        Finally
           inspector.WindowState = WindowState
           Marshal.ReleaseComObject(inspector)
        End Try
    Finally, you don't release underlying COM objects property. Each time calling the ActiveInspector property of the Application class you get the COM reference counter increased. You can read more about this in the Systematically Releasing Objects article in MSDN.

    Tuesday, December 31, 2013 11:54 AM
  • Hello Peter,

    Finally, you don't release underlying COM objects property. Each time calling the ActiveInspector property of the Application class you get the COM reference counter increased. You can read more about this in the Systematically Releasing Objects article in MSDN.

    Thanks for the reply, Eugene.  We ARE releasing objects inconsistently.  Good observation.  We'll give the article a read.

    Testing suggests the use of Threading.Sleep() halts animation and won't be of much use.  If you're right, and it's the OS animating the Minimize/Restore window, then a delay won't be reliable since workstation OS and CPU speeds vary.

    To answer Xiu's question, we're developing under Outlook 2010 x32 and Windows 7.


    Tuesday, December 31, 2013 10:12 PM
  • Hello Peter,

    You are right: the speed of animation depends on the PC hardware. I don't see any possible workaround except of using the delay.

    However, there is an alternative way of creating a screenshot. Please take a look at the following references: 

    Wednesday, January 1, 2014 2:46 PM
  • Thanks for the reply, Eugene.  And thanks for the help creating screenshots, but we think we have it handled.  None of the examples cited appear to omit the window initiating the screenshot and that’s the problem we are writing about.

     

    Using a delay seems the only option, except Threading.Thread.Sleep() blocks the Outlook window (behind our form) from repainting following the call to minimize the Inspector window of our FormRegion.  Traces of the minimized window are still visible in the screenshot.

     

    We found a work-around that may work for others.  If we start a new thread that pauses and takes the screenshot, waiting for the thread to complete seems to allow Outlook time to repaint what it needs following the property change to minimize the window.

     

    Below we’ve provided the Click event handler from our AddIn.FormRegion and the TakeScreenShot() task thread.

     

        ' handles user request to take screenshot

        Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click

            Dim FileName As String = System.IO.Path.GetTempPath() + "screenshot.png"

            Dim ActiveInspector As Outlook.Inspector = _

                Globals.ThisAddIn.Application.ActiveInspector

            Dim WindowState As Outlook.OlWindowState = ActiveInspector.WindowState()

            Try

                Button3.Enabled = False

                ActiveInspector.WindowState = Outlook.OlWindowState.olMinimized

                Dim myTask = Threading.Tasks.Task(Of Boolean).Factory.StartNew( _

                    Function() Common.TakeScreenShot(FileName))

                myTask.Wait()

                If myTask.Result = True Then

                    Me.OutlookItem.Attachments.Add(FileName)

                End If

                myTask = Nothing

            Catch ex As Exception

                Windows.Forms.MessageBox.Show(ex.ToString, My.Application.Info.Title)

            Finally

                ActiveInspector.WindowState = WindowState

                Runtime.InteropServices.Marshal.ReleaseComObject(ActiveInspector)

                Button3.Enabled = True

            End Try

        End Sub

     

    Module Common

     

        ' takes a screenshot of entire desktop, all display regions.

        Public Function TakeScreenShot(ByVal filename As String, _

                                       Optional ByVal delay As Integer = 2000) As Boolean

            Try

                ' delay giving time for windows to minimize and the desktop to redraw

                Threading.Thread.Sleep(delay)

     

                ' take the screenshot

                Using img As Drawing.Bitmap = _

                    New Drawing.Bitmap( _

                        Windows.Forms.SystemInformation.VirtualScreen.Width, _

                        Windows.Forms.SystemInformation.VirtualScreen.Height)

                    Using g As Drawing.Graphics = Drawing.Graphics.FromImage(img)

                        g.CopyFromScreen( _

                            Windows.Forms.SystemInformation.VirtualScreen.Left, _

                            Windows.Forms.SystemInformation.VirtualScreen.Top, _

                                              0, 0, img.Size)

                        img.Save(filename, Drawing.Imaging.ImageFormat.Png)

                        Return True

                    End Using

                End Using

            Catch ex As Exception

                Windows.Forms.MessageBox.Show(ex.ToString, My.Application.Info.Title)

            End Try

            Return False

        End Function

     

    End Module


    • Edited by Peter Schlosser Thursday, January 9, 2014 7:53 PM formatting changes.
    • Marked as answer by Peter Schlosser Thursday, January 9, 2014 8:44 PM
    Thursday, January 9, 2014 7:49 PM
  • Good news, Peter!

    Thank you for sharing the solution for other forum readers.

    Thursday, January 9, 2014 7:54 PM