【问题标题】:WPF - How to run code immediately after a Window is hiddenWPF - 如何在隐藏窗口后立即运行代码
【发布时间】:2016-08-21 20:54:31
【问题描述】:

在我的应用程序中,我截取了桌面的屏幕截图。在那之前我隐藏了我的应用程序的Window,所以它不会覆盖部分桌面:

MainWindow.Hide();  
TakeScreenShot();

问题是有时窗口隐藏得不够快,所以我最终也截取了它。我尝试从Window.IsVisibleChanged 事件处理程序中截取屏幕截图,但结果是一样的。

当然,我可以使用Thread.Sleep 或类似的,但我正在寻找更好的解决方案。

更新

我刚刚切换到 Aero 主题并启用了桌面合成,现在情况更糟。现在窗口“淡出”而不是在调用Window.Hide 时立即隐藏。所以现在我的应用程序总是截取渐变窗口的屏幕截图...我将尝试使用 SetWindowPosShowWindow 的 winapi 并发布更新。

更新 2

  1. ShowWindowSetWindowPos 给出与 Window.Hide 相同的结果(Window.Hide 在内部使用 ShowWindow(HWND, SW_HIDE))。

  2. 为了在隐藏窗口时禁用淡出效果,我使用DwmSetWindowAttribute

像这样:

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;

Window.Loaded 事件处理程序中:

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);
}  

所以除了淡出效果之外,问题依然存在。

【问题讨论】:

  • 你试过简单的OnDeactivated吗?或OnStateChanged 过滤器检查minimized 状态?
  • @quetzalcoatl:我试图从 Window.Deactivated 事件处理程序中截取屏幕截图。结果是一样的。我没有从 StateChanged 尝试,因为我不想最小化窗口。
  • @Bohoo 你能告诉我你TakeScreenShot 的样子吗?
  • 您是否尝试将窗口的可见性更改为 Window.Visibility = Visibility.Collapsed?尝试使用 Window.Deactivated 事件处理程序再次执行此操作。
  • @Gopichandar:我使用 System.Drawing.Graphics.CopyFromScreen

标签: c# wpf windows winapi window


【解决方案1】:

您可以先将窗口放在您尝试复制的区域之外(当考虑到子窗口恢复问题时)。

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;
        }));
    }
}

【讨论】:

  • 感谢您的回答。将窗口从屏幕上移开是可行的,但它是一种黑客行为......我不知道当我这样做时实际发生了什么,以及后果是什么。由于它是生产代码,我不想使用它。你也可以看到我对我的问题的更新。
  • 好的@Bohoo。但我不明白你说的是什么意思; “当我这样做时实际发生了什么,后果是什么”。通过应用 Top=Y_POSITION 您只需将窗口移动到屏幕上的特定(Y_POSITION)位置。问候。
【解决方案2】:

在隐藏Window 之前尝试最小化Window 并以最低优先级运行Asynchronously。像这样。

    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);
        }
    }

希望这会有所帮助。谢谢。

【讨论】:

  • 感谢您的回答。我不想最小化窗口,因为它涉及到动画。单独使用 DispatcherPriority.SystemIdle 并没有帮助。你可以看到我的问题的更新。
【解决方案3】:

你可以这样使用:

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

   TakeScreenShot();
});

【讨论】:

    猜你喜欢
    • 2012-09-30
    • 1970-01-01
    • 2021-02-24
    • 2012-12-03
    • 2014-05-14
    • 1970-01-01
    • 2012-05-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多