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 将每个按钮的指针放入到指定地址中。


 

对酒当歌,人生几何!

 

posted @ 2021-08-28 17:56  Abraverman  阅读(81)  评论(0编辑  收藏  举报