昨天很纠结,一直上网搜索如何令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的成员了。