FooButton是怎么实AddToGroup组别的?
在了解原理之前需要一些知识储备 ,如下:
1、CMapStringToPtr是啥,怎么用? - Abraverman - 博客园 (cnblogs.com)
2、CPtrArray是啥,怎么用? - Abraverman - 博客园 (cnblogs.com)
之后我们便可以开始我们的探索之旅了!
一、分析
FooButton在使用的时候,只需要将原始的CButton替换成FooButton即可,如果有特殊需求,也只需要在初始化的时候使用SetType,AddToGroup等属性。
主要问题就出现在了AddToGroup上面,每一个按钮是独立的,没有任何瓜葛,也没有修改CButton里面任何代码,就可以实现每个按钮之间通讯,在一个组别里面,一个按钮被按下,其他的都弹起来。
在整个程序中,按钮和按钮之间没有任何连接关系
如果说有公有的,那就是都在一个程序里面,公用了内存地址。那么是否是从这个方面实现的了?
结合上面的知识储备,也就不难理解其中的原理了。
二、源码解读
如下,为AddToGroup源码:
1 //! Adds the button to a named button group. 2 //! @param strGroupName Name of button group. 3 //! @return true if successful, false otherwise. 4 bool FooButton::addToGroup 5 (CString strGroupName) 6 { 7 // Group name must be specified 8 if (strGroupName.IsEmpty()) 9 return (false); 10 11 // Create the group if it doesn't exist 12 void* pGroup = NULL; 13 getButtonGroup (strGroupName, pGroup); 14 if (pGroup == NULL) { 15 pGroup = new CPtrArray(); 16 ASSERT (pGroup != NULL); 17 m_btnGroups.SetAt (strGroupName, pGroup); 18 } 19 20 // Remove the button from its current group 21 removeFromGroup(); 22 23 // Add button's HWND to group if not already present 24 m_strButtonGroup = strGroupName; 25 int nButtons = ((CPtrArray *) pGroup)->GetSize(); 26 for (int nIndex=0; (nIndex < nButtons); nIndex++) 27 if (((CPtrArray *) pGroup)->GetAt (nIndex) == this) 28 return (true); 29 ((CPtrArray *) pGroup)->Add (GetSafeHwnd()); 30 31 return (true); 32 }
如上,调用函数的时候是需要传递一个字符串数据的,且这个字符串数据就作为了组别的判断。
如此,便可以通过函数 CMapStringToPtr中的Lookup来获取一段内存空间,
1 //! Gets a button group by name 2 //! @param strGroupName Group name 3 //! @param pGroup Returned group 4 void FooButton::getButtonGroup 5 (CString strGroupName, void* & pGroup) 6 { 7 pGroup = NULL; 8 m_btnGroups.Lookup (strGroupName, pGroup); // CMapStringToPtr FooButton::m_btnGroups; 9 }
获取到这段内存地址空间之后,便可以对地址里面写入一些东西。
写入什么了?就写入每个按钮的唯一特征即可,每一个按钮都是有一段地址的,那么只需要把相同组别的按钮的地址放到同一段地址空间即可。如下:
1 int nButtons = ((CPtrArray *) pGroup)->GetSize(); 2 for (int nIndex=0; (nIndex < nButtons); nIndex++) 3 if (((CPtrArray *) pGroup)->GetAt (nIndex) == this) 4 return (true); 5 ((CPtrArray *) pGroup)->Add (GetSafeHwnd());
pGroup 是刚刚通过字符串获取到的内存地址空间,我们在初始化地址的时候就写入了 CPtrArray();,由上面可以知道 CPtrArray 可以动态分配很多的空间,所以我们就将按钮的句柄放入到内存中。
以后只需要通过这个字符串获取到这段地址,然后循环CPtrArray中的数组,即可知道每一组里面有哪些按钮。
我们就可以通过如下方式来在一个按钮不对象中,获取每个按钮的状态,或者改变每个按钮状态。
1 // Get at the button's parent group 2 void* pGroup = NULL; 3 getButtonGroup (m_strButtonGroup, pGroup); 4 ASSERT (pGroup != NULL); 5 int nButtons = ((CPtrArray *) pGroup)->GetSize(); 6 7 // Uncheck the other buttons in the group 8 for (int nIndex=0; (nIndex < nButtons); nIndex++) { 9 HWND hWnd = (HWND) ((CPtrArray *) pGroup)->GetAt (nIndex); 10 ASSERT (hWnd != NULL); 11 if (hWnd != GetSafeHwnd()) { 12 if (IsWindow (hWnd)) { 13 FooButton* pButton = (FooButton *) CWnd::FromHandle (hWnd); 14 ASSERT (pButton != NULL); 15 if ((pButton != this) && pButton->isChecked()) 16 pButton->check (false); 17 } 18 } 19 }
如下图:
三、总结
这是一个很巧妙的方法,实现了独立组件之间的交流,在内存地址中共享数据,从而获取数据监控。
1、通过 CMapStringToPtr 来实现地址的分组和定位。
2、通过 CPtrArray 将每个按钮的指针放入到指定地址中。
对酒当歌,人生几何!