昨天很糾結,一直上網搜索如何令CMFCRibbonBar的按鈕變灰,剛開始以爲獲得了按鈕的指針,裏面一定有方法或者接口達到我想要的功能,剛開始以爲那些按鈕和Checkbox等元素是我們普通的控件元素,誰知道用SPY++查了一下,這些都不是繼承於CWnd的元素,只是一張一張的圖片。後來在認真看了一下MSDN就寫了如下代碼獲得裏面的某一按鈕元素。
CArray<CMFCRibbonBaseElement* ,CMFCRibbonBaseElement*> arButtons;
m_wndRibbonBar.GetElementsByID(IDC_BUTTON_BRIMIN,arButtons);
arButtons.ElementAt(0)->SetVisible(enableTag);
誰知道寫了這些後,發覺一點效果都沒有,那時就納悶了。接着就打開MFC的源碼一句一句調試,發現原來是保護成員裏的m_bIsDisable的控制量控制,那可簡單了,只需要設成TRUE嗎,那就搜搜哪一個接口能更改這個值,誰知道還沒有呢,這是就想到有沒有方法能更改保護成員的值呢,想到更改這個值是編譯器有一個選項阻止了這樣的動作,沒道理要更改這個配置嘛,後來在網上搜到一個很強大的宏如下:
My Approch :
#define PROTECTED_CAST_DECL (CLASS_TYPE ,MEMBER_TYPE ,MEMBER_NAME ) /
template <typename ClassType , typename MemberType > /
class C ##MEMBER_NAME ##Accessor /
{ /
class CAccessor : public ClassType /
{ /
friend class C ##MEMBER_NAME ##Accessor ; /
}; /
public : /
static MemberType & GetMember (ClassType * pClass ) /
{ /
return ((CAccessor *)pClass )->MEMBER_NAME ; /
} /
};
#define PROTECTED_CAST (CLASS_TYPE ,CLASS_OBJECT_PTR ,MEMBER_TYPE ,MEMBER_NAME ) (C ##MEMBER_NAME ##Accessor <CLASS_TYPE , MEMBER_TYPE >::GetMember (CLASS_OBJECT_PTR ))
Sample :
以下示例代碼展示了 PROTECTED_CAST 的使用方法,代碼在 VC++ 2008 下測試通過。
class CPrivateMemberWrapper
{
protected :
int m_iValue ;
double m_dValue ;
string m_sValue ;
public :
CPrivateMemberWrapper (int i , double d , const char * lps ):
m_iValue (i ), m_dValue (d ), m_sValue (lps )
{}
~CPrivateMemberWrapper () {}
};
PROTECTED_CAST_DECL (CPrivateMemberWrapper , int , m_iValue )
PROTECTED_CAST_DECL (CPrivateMemberWrapper , double , m_dValue )
PROTECTED_CAST_DECL (CPrivateMemberWrapper , string , m_sValue )
這樣確實可以令到那個按鈕變灰,但是畢竟這是投機取巧的方法。後來又想到當這個元素沒有綁定特定的事件時也是呈現灰色狀態的,就想那有沒有能動態增刪事件綁定的方法,誰知道感覺這麼一個簡單的方法,居然還搜不到所要的答案,沒辦法了,看來只有按照自己的思路寫一下吧,看了一下,BeginMessage和EndMessage的宏,瞭解到消息鏈表也只是一個有結尾項的數組,那我所要實現的只不過是在這個數組上面增刪項目(瘋了,沒辦法就什麼都要嘗試),代碼如下:
const AFX_MSGMAP* message = this->GetMessageMap();
AFX_MSGMAP_ENTRY* mePtr = const_cast<AFX_MSGMAP_ENTRY*>((const_cast<AFX_MSGMAP*>(message))->lpEntries);
if(enableTag)
{
//綁定消息
int reduce = 1;
bool haveTag = false;
while(mePtr->nSig != AfxSig_end)
{
if(mePtr->nID == IDC_BUTTON_BRIMIN)
{
haveTag = true;
break;
}
++mePtr;
}
if(!haveTag)
{
mePtr[reduce] = mePtr[0];
AFX_MSGMAP_ENTRY tmpMA[] = {ON_COMMAND(IDC_BUTTON_BRIMIN, &CMainFrame::OnButtonBrimin){0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }};
*mePtr = tmpMA[0];
}
}
else
{
//移除消息綁定
int reduce = 0;
int index = 0;
while(mePtr->nSig != AfxSig_end)
{
if(mePtr->nID == IDC_BUTTON_BRIMIN)
{
++reduce;
}
*mePtr = *(mePtr + reduce);
++mePtr;
++index;
}
}
經測試這樣確實能動態增刪事件綁定,而且按鈕也會變灰,好像達到了要求,哎,不過本人還是比較糾結爲什麼這樣的方法網上沒有貼出來,肯定是或多或少有點問題,所以還是不滿足,就繼續找資料,又找了一個早上,纔在一篇文章裏看到必須要響應ON_UPDATE_COMMAND_UI或者ON_UPDATE_COMMAND_UI_RANGE事件達到這樣的功能,而且那裏的作者還寫着是基礎知識,後悔自己基礎唔牢固,鬱悶了,寫了一年多C++的我也完全沒了解過這個事件。後來就改成了這樣
ON_UPDATE_COMMAND_UI_RANGE(ID_BUTTON_BRIMIN,ID_BUTTON_TEST, &CMainFrame::OnUpdateIdrRibbonI)
void CMainFrame::OnUpdateIdrRibbonI(CCmdUI *pCmdUI)
{
BOOL enableTag = (BOOL)czDevs->czSelSect.size();
pCmdUI->Enable(enableTag);\\還能設置SetChecked等功能呢
}
這樣就完美解決了這個問題,原來這兩個消息是用來更新像菜單、工具欄、狀態欄、屬性窗口等UI界面的。哎,應該以後不會再犯如何更改m_bIsDisable的成員了。