按钮控件使能

新建一对话框应用程序PraDlg,将对话框模板上的静态文件控件ID改为IDC_STATIC_TEST,再添加一按钮,改ID为IDC_BTN_TEST, Caption改为Test。
        双击Test按钮, 即可添加该按钮的消息映像函数。在此函数中做如下处理:

< 1 >.
    void
 CPraDlgDlg::OnBtnTest() 
    {
           CButton 
*pBut = (CButton *)GetDlgItem(IDC_BTN_TEST);    //获得按钮指针

        pBut->EnableWindow(FALSE);                                                    //将该按钮禁止使能,变灰。
        static int nNum = 0;                                                                       //统计该按钮被点击次数。我使用静态变量。
        SetDlgItemInt(IDC_STATIC_TEST, ++nNum);                         //将此数据显示在静态文本框中。
        Sleep(5000);                                                                                  //这里是问题的关键的关键。假定我们做五秒钟的工作
        pBut->EnableWindow(TRUE);                                                    //做完工作后再使能
    }


        可能你还没弄明白我要干什么,没关系,咱们运行,然后按照我说的做。
        在按钮上连续点击五次,当然这五次不要超过五秒,因为我刚才Sleep了五秒,注意观察按钮和文本框的变化。问题出来了。
        问题描述:
        我们原意是在按钮被点击时,为防止按钮再次被点击,先将该按钮禁止使能,直到要做的处理全部做完后再允许使用者点击。这里的Sleep (5000); 是假定我们要做的处理工作。但上面的例子运行的现象是,当第一次点击按钮时,按钮状态立即变灰,即非使能,静态文本框中出现数字1,这一点,完全在我们的 预料之中。 每过五秒,文本框中的数字会一直累加直到五(因为刚才点击了五次)。
        嗯? 问题出来了,完了,按钮眞的有BUG,因为后四次肯定是在按钮变灰后点击的,可函数还是执行了五次,难道是按钮的使能状态没用? 但不对呀,以前做的按钮使能都没问题的呀。 那么,是不是在消息响应函数执行完之前点击的,按钮变灰不影响点击。 好,我们再做下面实验:

<   2   >
    
void  CPraDlgDlg::OnBtnTest() 
    {
        CButton 
* pBut  =  (CButton  * )GetDlgItem(IDC_BTN_TEST);     // 获得按钮指针
        pBut -> EnableWindow(FALSE);                                                     // 将该按钮禁止使能,变灰。
        static  int  nNum  =   0 ;                                                                          // 统计该按钮被点击次数。我使用静态变量。
        SetDlgItemInt(IDC_STATIC_TEST,  ++ nNum);                            // 将此数据显示在静态文本框中。
        Sleep( 5000 );                                                                                      // 这里是问题的关键的关键。假定我们做五秒钟的工作
         //   pBut->EnableWindow(TRUE);                                                   //屏蔽掉按钮使能,验证下是否按钮响应退出前不受变灰状态的改变。
    }

        好了,将最后一条语句屏蔽掉后,运行,连续点击五次按钮,发现点击完一次后,按钮立即变灰,文本框出现数字1, 再等n久,按钮还是没反应。也就是说,该按钮只接
受了一次点击,和上面做的推测显然不符,那么究竟什么原因导致问题的呢?
        从1和2的现象综合分析,得出如下结论:
         a:  按钮消息处理的时候,鼠标点击事件被记录下来了,这说明按钮的点击事件优先级较高。
        b:  按钮消息处理完后,再处理下一次的按钮消息。这就是为什么消息能再次被响应。(最后一句又使能了嘛,所以能再次被处理)
         c:  按钮是否根据使能状态响应函数是在派遣消息时判断的,而不是在点击时判断的。
        通过上面b和c结论可以初步推测出鼠标点击到响应过程的眞面目。
        那么怎么解决这个问题, 以达到我们的目的呢? 定时器? 好,继续做上面的实验:
< 3 >  
    
void  CPraDlgDlg::OnBtnTest() 
    {
        CButton 
* pBut  =  (CButton  * )GetDlgItem(IDC_BTN_TEST);     // 获得按钮指针
        pBut -> EnableWindow(FALSE);                                                      // 将该按钮禁止使能,变灰。
        static  int  nNum  =   0 ;                                                                          // 统计该按钮被点击次数。我使用静态变量。
        SetDlgItemInt(IDC_STATIC_TEST,  ++ nNum);                            // 将此数据显示在静态文本框中。
        Sleep( 5000 );                                                                                      // 这里是问题的关键的关键。假定我们做五秒钟的工作
        SetTimer( 10 , NULL);                                                                    // 处理完后启动定时器,让定时器取消息按钮使能。
         // pBut->EnableWindow(TRUE);                                                    //做完工作后再使能
    }
    
void  CPraDlgDlg::OnTimer(UINT nIDEvent) 
    {
        GetDlgItem(IDC_BTN_TEST)
-> EnableWindow(TRUE);            // 取消按钮使能。
        KillTimer(nIDEvent);                                                                     // 去掉定时器。
        CDialog::OnTimer(nIDEvent);
    }


        运行,连续点击n次按钮,发现第一次点击后按钮变灰,之后经过五秒钟,按钮变可用,文本框中数字变为1而没变成5,哈哈成功了。
        你可能会问,为什么牠不先执行定时器的函数,使按钮可用,再执行鼠标点击的事件函数,继续响应按钮呢。嗯,这个呢,是因为定时器的优先级更低,没有按钮消息时
才会响应定时器。
而在派遣按钮消息时,按钮又是非使能的,所以也没法运行。
        眞像差不多大白了,但上面的处理还要借助定时器,太麻烦了,有没有更简单的方法呢?
   
        回顾一下Windows的消息机制, Windows应用程序都有一个消息队列,系统会从消息队列中一个一个的取出消息,翻译,派遣,再来看一下所做的实验 1:
        当鼠标点击时, 点击消息被放到消息队列中,Windows从消息队列中取出一条消息,然后派遣,执行消息响应函数。在函数中,先将按钮禁止使能,再后做我们需要的工
作,这时又过来几条消息,因为当前工作尙未完成,所以仍然堆到消息队列中。当工作完成后,使按钮可用,退出响应函数,Windows再从消息队列中取出一条消息,因为此
时按钮已经可用,所以该消息仍然可以被派遣,执行。 实验1中就这样一次一次的将按钮消息执行完。
        完美解决方法: 在消息响应函数退出之前,将所有的按钮消息从消息队列中取出来,仍掉。


void  CPraDlgDlg::OnBtnTest() 
{
    
//  TODO: Add your control notification handler code here
    CButton  * pBut  =  (CButton  * )GetDlgItem(IDC_BTN_TEST);     // 獲得按鈕指針
    SetTimer( 1 0 , NULL);
    pBut
-> EnableWindow(FALSE);                                                      // 將該按鈕禁止使能,變灰。
    static  int  nNum  =   0 ;                                                                          // 統計該按鈕被點擊次數。我使用靜態變量。
    SetDlgItemInt(IDC_STATIC_TEST,  ++ nNum);                           // 將此數據顯示在靜態文本框中。
    Sleep( 5000 );                                                                                     // 這裡是問題的關鍵的關鍵。假定我們做五秒鐘的工作
    pBut ->EnableWindow(TRUE);                                                      //做完工作後再使能
    MSG msg;
    
while(PeekMessage(&
msg, GetSafeHwnd(), WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE ))
        NULL;

}

 

 

 

( ?? 杂碎
用户在按钮上单击鼠标,消息是发送到(类似于PostMessage)按钮的Owner窗口上(Dialog)的,因为这时候Dialog的主线程还在执 行中(使用Sleep),所以消息还是在消息队列中,当你函数退出,Dialog从消息队列中取道了鼠标消息,根据POINT可以获知是在BUTTON 上,然后再把消息投递到按钮上,因为这时候按钮已经Enabled了,所以能够处理消息。
关键的地方就是即使在一个按钮上单击鼠标,这个消息并不是发送到这个按钮上的,而是发送到这个按钮Owner上的。使用Spy++可以看到Owner是谁。推荐使用MySpy,简单、小巧:)??)

 

 

CRect rec;
GetDlgItem(IDC_BUTTON1)->GetWindowRect(rec);
ScreenToClient(&rec);
rec.OffsetRect(2, 2);
PostMessage(WM_LBUTTONDOWN, MK_LBUTTON, *(LONG*)&rec.TopLeft() );
Sleep(200);
PostMessage(WM_LBUTTONDOWN, MK_LBUTTON, *(LONG *)&rec.TopLeft() );
發現IDC_BUTTON1響應了單擊消息。對話框控件的按鍵消息是由對話框接收後根據位置傳遞給控件的

 

 

 

拟鼠标单击的消息
SendInput
The SendInput function synthesizes keystrokes, mouse motions, and button clicks.

UINT SendInput(
  UINT nInputs,     // count of input events
  LPINPUT pInputs,  // array of input events
  int cbSize        // size of structure
);

 

 

做消息队列,并且进行处理的时候是类似于这样的:
MSGQUEUE msg; 

BOOL GetMssage(MSGQUEUE)
{
   EnterCS(&csMsg); //Sync message queue
  
   LeaveCS(&csMsg);
}

main_thread()
{
   while(GetMessage(&msg))
   {

            switch(msg.message)
            {
               EnterCS(&csMain);
               LeaveCS(&csMain);
             }


   }
}

LONG SendMessage(msg)
{
       LONG lRet;
        EnterCS(&csMain)

         swittch(msg.message)
         {
          //Call proc
          lRet = pfn(...);
         }

       LeaveCS(&csMain);
      return lRet;
}

void PostMessage(msg)
{
    EnterCS(&csMsg);      //  Sync message queue
    AddToMessage(&msg); //  Only to add message queue
    LeaveCS(&csMsg);
}


因为CS存在,所以在另外一个线程中使用SendMessage可能会导致死锁(SendMessage中存在EnterCS(&csMain))。
你在窗口上单击鼠标,仅仅是相当于调用了PostMessge,也就是说把鼠标单击这个消息加入到了消息队列。但是因为你Seelp了,主线程还 没有返回,等Sleep结束了,在此GetMessage时发现了那个鼠标单击的消息,这时候根据msg.hwnd会调用对应的WndProc函数,注 意:这时候你的那个BUTTON已经Enabled了,自然也就可以响应这个消息了。

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值