简体   繁体   中英

WPF - How to run code immediately after a Window is hidden

In my app I take a screenshot of the desktop. Right before that I hide my app's Window , so It will not cover part of the desktop:

MainWindow.Hide();  
TakeScreenShot();

The problem is that sometimes the Window does not get hidden fast enough, so I ended up taking a screenshot of it too. I have tried taking the screenshot from a Window.IsVisibleChanged event handler, but the result is the same.

Of course, I can use Thread.Sleep or similar, but I am looking for a better solution.

Update

I have just switched to Aero theme and enabled Desktop Composition, and now the situation is even worse. Now the window "fade out" instead of instantly hidden when calling Window.Hide . So now my app always take a screenshot of a fading window... I will try winapi with SetWindowPos or ShowWindow and will post an update.

Update 2

  1. ShowWindow and SetWindowPos give the same result as Window.Hide ( Window.Hide uses ShowWindow(HWND, SW_HIDE) internally).

  2. In order to disable the fade-out effect when hiding the window, I use DwmSetWindowAttribute .

like this:

using System.Windows.Interop;  
using System.Runtime.InteropServices;

[DllImport("dwmapi.dll")]  
private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);

private const int DWMWA_TRANSITIONS_FORCEDISABLED = 3;

and in the Window.Loaded event handler:

if (Environment.OSVersion.Version.Major >= 6) {

    IntPtr WinHandle = new WindowInteropHelper(this).Handle;

    int BOOL_TRUE = 1;

    int HR = DwmSetWindowAttribute(WinHandle, DWMWA_TRANSITIONS_FORCEDISABLED, BOOL_TRUE, Marshal.SizeOf(BOOL_TRUE));

    if (HR != 0)
        Marshal.ThrowExceptionForHR(HR);
}  

So beside the fade-out effect, the problem remain.

You can start with punting your window outside the area you are trying to copy(when a sub window restore issue was taken into account).

public partial class MainWindow : Window
{

    private DispatcherOperation _action;
    private int _width = 2000;
    private int _height = 1000;
    private double _top;

    public MainWindow()
    {
        InitializeComponent();
        Dispatcher.Hooks.OperationCompleted += HooksOnOperationCompleted;
    }

    private void HooksOnOperationCompleted(object sender, DispatcherHookEventArgs dispatcherHookEventArgs)
    {
        if(dispatcherHookEventArgs.Operation != _action) return;
        _action.Task.ContinueWith((t) =>
        {
            Rectangle rect = new Rectangle(0, 0, _width, _height);
            Bitmap bmp = new Bitmap(rect.Width, rect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            Graphics g = Graphics.FromImage(bmp);
            g.CopyFromScreen(rect.Left, rect.Top, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
            bmp.Save("help" + DateTime.Now.Ticks + ".jpg", ImageFormat.Jpeg);
        }).ContinueWith(t =>
        {
            Dispatcher.BeginInvoke((Action)(() =>
            {
                Top = _top;
            }));
        });


    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        _top = Top;
        _action = Dispatcher.BeginInvoke((Action) (() =>
        {
            Top = 10000;
        }));
    }
}

Try minimizing the Window before hiding the Window and run it Asynchronously with least priority . Something like this.

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        //Minimize here before hiding. . 
        this.WindowState = WindowState.Minimized;  //this is the key
        this.Visibility = Visibility.Hidden;
        this.Dispatcher.BeginInvoke(DispatcherPriority.SystemIdle, new Action(() =>
        {                
            CaptureImage();
        }));            
    }

    private void CaptureImage()
    {
        System.Drawing.Rectangle bounds = new System.Drawing.Rectangle(0, 0, (int)System.Windows.SystemParameters.PrimaryScreenWidth, (int)System.Windows.SystemParameters.PrimaryScreenHeight);
        using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
        {
            using (Graphics g = Graphics.FromImage(bitmap))
            {
                g.CopyFromScreen(System.Drawing.Point.Empty, System.Drawing.Point.Empty, bounds.Size);
            }
            bitmap.Save("test.jpg", ImageFormat.Jpeg);
        }
    }

Hope this helps. Thanks.

You can use something like this:

Visibility = Visibility.Collapsed;
Dispatcher.BeginInvoke(()=>{ 
   Thread.Sleep(2000);

   TakeScreenShot();
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM