none
如何判断窗体是鼠标点击关闭按钮后关闭的? RRS feed

  • 问题

  • 这是我尝试的代码。但是没成功

            private void dialog_Loaded(object sender, RoutedEventArgs e)
            {
                HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
                if (source != null) source.AddHook(WndProc);
            }
     
            private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
            {
                const int WM_SYSCOMMAND = 0x0112;
                const int SC_CLOSE = 0xF060;
     
                if (msg == WM_SYSCOMMAND && ((int)wParam == SC_CLOSE))
                {  
                    //通过鼠标点击窗口关闭按钮关闭
                    this.Tag = "false";
                } 
                return IntPtr.Zero;
            }
    2014年2月22日 3:28

全部回复

  • 你好,

    我可以使用堆栈跟踪信息(stack trace) 去查找,区分是否是点击关闭按钮的:

    public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
            {
                bool wasCodeClosed = new StackTrace().GetFrames().FirstOrDefault(x => x.GetMethod() == typeof(Window).GetMethod("Close")) != null;
                if (wasCodeClosed)
                {
                    // Closed with this.Close()
                    MessageBox.Show("Closed with this.Close()");
                }
                else
                {
                    // Closed some other way.
                    MessageBox.Show("Closed some other way");
                }
    
                base.OnClosing(e);
            }
        }


    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.

    2014年2月24日 3:35
    版主
  • 您好,我的窗体比较特别是被重写过的,无法用您的方式进行识别。demo如下

    http://aqsd72vydu.l3.yunpan.cn/lk/Q4VZ8b6J9DPxJ

    2014年2月24日 5:00
  • 你好,

    你可能没有理解我的代码,我测试了你的RibbonWindow,可以正常使用我的方法:

    比如我使用按钮,点击后使用this.Close()去关闭窗体,这是会弹出信息:Closed with this.Close()

    private void Button_Click(object sender, RoutedEventArgs e)
    {
                this.Close();
    }

    如果使用鼠标点击右上角关闭按钮,会弹出如下信息:Closed some other way


    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.

    2014年2月25日 2:16
    版主
  • 我发现您提供的代码还是存在问题的,我在XP上测试点窗口自带关闭按钮提示Closed with this.Close()

    在win7上测试点窗口自带的关闭按钮提示Closed some other way()

    2014年2月25日 2:41
  • 点击“关闭”按钮的消息是这样的:

    <00898> 000C0AFA P WM_NCLBUTTONDOWN nHittest:HTCLOSE xPos:2635 yPos:238
    <00899> 000C0AFA P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:475 yPos:-16
    <00900> 000C0AFA P WM_LBUTTONUP fwKeys:0000 xPos:475 yPos:-16
    <00901> 000C0AFA S WM_CAPTURECHANGED hwndNewCapture:00000000
    <00902> 000C0AFA R WM_CAPTURECHANGED
    <00903> 000C0AFA S WM_SYSCOMMAND uCmdType:SC_CLOSE xPos:2635 yPos:238
    <00904> 000C0AFA S WM_CLOSE

    .....

    你没成功的含义是什么?是没收到 WM_SYSCOMMAND 消息,还是 wParam 不等于 SC_CLOSE,还是 WndProc 就没 HOOK 成功?

    假设一切都正常,也不能单单使用 WM_SYSCOMMAND 消息,你需要结合 WM_NCLBUTTONDOWN 消息来判断是点击,还是直接发送的 WM_SYSCOMMAND 消息。

    2014年2月25日 3:18
  • 点击“关闭”按钮的消息是这样的:

    <00898> 000C0AFA P WM_NCLBUTTONDOWN nHittest:HTCLOSE xPos:2635 yPos:238
    <00899> 000C0AFA P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:475 yPos:-16
    <00900> 000C0AFA P WM_LBUTTONUP fwKeys:0000 xPos:475 yPos:-16
    <00901> 000C0AFA S WM_CAPTURECHANGED hwndNewCapture:00000000
    <00902> 000C0AFA R WM_CAPTURECHANGED
    <00903> 000C0AFA S WM_SYSCOMMAND uCmdType:SC_CLOSE xPos:2635 yPos:238
    <00904> 000C0AFA S WM_CLOSE

    .....

    你没成功的含义是什么?是没收到 WM_SYSCOMMAND 消息,还是 wParam 不等于 SC_CLOSE,还是 WndProc 就没 HOOK 成功?

    假设一切都正常,也不能单单使用 WM_SYSCOMMAND 消息,你需要结合 WM_NCLBUTTONDOWN 消息来判断是点击,还是直接发送的 WM_SYSCOMMAND 消息。


    你好,

    DroidXgnaW的建议中,WM_NCLBUTTONDOWN(http://msdn.microsoft.com/en-us/library/windows/desktop/ms645620(v=vs.85).aspx)是在左击非客户区时响应,简单的办法是,设置一个标志变量,如果之前WM_NCLBUTTONDOWN响应了,改变标志变量,最后结合 WM_SYSCOMMAND 和 SC_CLOSE 判断是否是左键点击:

    System.Windows.Threading.DispatcherTimer dispatcherTimer;
    int flag = 0;
            
    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
                const int WM_SYSCOMMAND = 0x0112;
                const int SC_CLOSE = 0xF060;
                const int WM_NCLBUTTONDOWN = 0x00A1;
    
                if (msg == WM_NCLBUTTONDOWN)
                {
                    flag = 1;
                }
    
                if (flag==1 && msg == WM_SYSCOMMAND && ((int)wParam == SC_CLOSE))
                {
                    //通过鼠标点击窗口关闭按钮关闭
                    this.Tag = "false";
                }
                return IntPtr.Zero;
    }
    
    private void Button_Click(object sender, RoutedEventArgs e)
    {
                this.Close();
    }
    
    private void dispatcherTimer_Tick(object sender, EventArgs e)
    {
                flag = 0;
    }
    
    private void RibbonWindow_Loaded(object sender, RoutedEventArgs e)
    {
                dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
                dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
                dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 100);
                dispatcherTimer.Start();
                HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
                if (source != null) source.AddHook(WndProc);
    }

    以上代码均在Win8上测试过:

    对于XP,因为条件有限,暂时无法测试。


    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.

    2014年2月25日 9:47
    版主
  • 非常感谢您的回来,经过测试,确实win7以上版本可以进行识别,但是XP依然不能用此方式识别出来。我还发现一个问题,就是当程序使用Binding机制后model继承自INotifyPropertyChanged,在.net4.0环境下数据驱动正常,在.net4.5下无反应,当把机器的.net4.5卸载后,重新安装.net4.0后数据驱动UI正常。课本上不都是说.net版本是可以共存切向后兼容,但是我用.net4.0开发的程序就在.net4.5上就有问题。而且装了.net4.5,.net4.0就没了 。还是我Binding语法不对?ItemsSource="{Binding Path=PageData,UpdateSourceTrigger=PropertyChanged}"
    2014年2月26日 8:23
  • 我们没有 XP,你有,那么你能否像我一样也用 Spy++ 看一下,在 XP 环境下,鼠标左键单击“关闭”按钮时,窗体的消息日志。

    能不能麻烦你花点心思把“XP依然不能用此方式识别”的问题描述的稍微全面点。

    2014年2月26日 8:41
  • 您好,您说的是这个吗?
    2014年2月26日 8:56
  • 我很佩服你啊,居然还截成图了。注意 <00064> 行,WM_LBUTTONDOWN 消息,这就是你的鼠标左键在“关闭”按钮上按下的第一个标志消息。

    这个消息很奇怪,我建议你使用 MFC 或 Winfrom 创建一个简单的对话框程序,然后监控一下消息日志,同 WPF 的比较下,找到规律。

    然后在你的代码中,通过 OS 版本来使用不同的消息来标识窗体是通过鼠标单击“关闭”按钮关闭的。

    2014年2月26日 9:14