COptionUI
继承自 CButtonUI
, 按钮的各个状态会显示不同的文字颜色啊、背景图片啊什么的这个就不再介绍了,每个版本的 Duilib 对 COptionUI
的状态支持可能不同,大家参考一下自己的代码就可以了。这部分内容也可以看一下 Duilib 源码分析之 CButtonUI 篇。 这篇帖子中着重介绍一下 COptionUI
自己的特色的功能
- bool m_bSelected
- CDuiString m_sGroupName
以上两个成员变量相关处理是体现 COptionUI
功能的部分。
bool COptionUI::Activate()
{
if( !CButtonUI::Activate() ) return false;
if( !m_sGroupName.IsEmpty() ) Selected(true);
else Selected(!m_bSelected);
return true;
}
- 当
m_sGroupName
不为空的情况下,点击当前单选按钮相当于选中它,也就是说有一组单选按钮的情况下。 m_sGroupName
为空,代表此单选按钮不与其他单选按钮为一组,此时此单选按钮的作用其实是相当于一个 Checkbox ,每点击一次就切换一次当前的选中状态。
void COptionUI::Selected(bool bSelected, bool bTriggerEvent)
{
if( m_bSelected == bSelected ) return;
m_bSelected = bSelected;
if( m_bSelected ) m_uButtonState |= UISTATE_SELECTED;
else m_uButtonState &= ~UISTATE_SELECTED;
if( m_pManager != NULL ) {
if( !m_sGroupName.IsEmpty() ) {
if( m_bSelected ) {
CDuiPtrArray* aOptionGroup = m_pManager->GetOptionGroup(m_sGroupName);
for( int i = 0; i < aOptionGroup->GetSize(); i++ ) {
COptionUI* pControl = static_cast<COptionUI*>(aOptionGroup->GetAt(i));
if( pControl != this ) {
pControl->Selected(false, bTriggerEvent);
}
}
if (bTriggerEvent) m_pManager->SendNotify(this, DUI_MSGTYPE_SELECTCHANGED);
}
}
else {
if (bTriggerEvent) m_pManager->SendNotify(this, DUI_MSGTYPE_SELECTCHANGED);
}
}
Invalidate();
}
- 第一行,当选中状态就是当前的状态直接退出,这种情况发生在你单击某一个分组内的一个已经是选中状态的单选按钮时
- 当
m_sGroupName.IsEmpty()
为false
时,注意此时会调用m_pManager->GetOptionGroup(m_sGroupName)
来获取同组内的所有按钮,将当前按钮设置为选中状态且将其他按钮设置为非选中状态。我认为这里的if( m_bSelected )
判断是多余的,因为在组名非空的情况下,调用Select
函数时m_bSelected
必然为true
。
接下来我们看一下这个所谓的 CStdPtrArray* CPaintManagerUI::GetOptionGroup(LPCTSTR pStrGroupName)
函数。根据组名获取此组内的所有控件。关于 COptionUI
的组名的处理有两种场合:
- 设置了
group
属性时,初始化控件时 - 后续通过主动修改
SetGroup
时
好,接下来看一下 SetGroup
的代码
void COptionUI::SetGroup(LPCTSTR pStrGroupName)
{
if( pStrGroupName == NULL ) {
if( m_sGroupName.IsEmpty() ) return;
m_sGroupName.Empty();
}
else {
if( m_sGroupName == pStrGroupName ) return;
if (!m_sGroupName.IsEmpty() && m_pManager) m_pManager->RemoveOptionGroup(m_sGroupName, this);
m_sGroupName = pStrGroupName;
}
if( !m_sGroupName.IsEmpty() ) {
if (m_pManager) m_pManager->AddOptionGroup(m_sGroupName, this);
}
else {
if (m_pManager) m_pManager->RemoveOptionGroup(m_sGroupName, this);
}
Selected(m_bSelected);
}
不知道大家有没有发现这段代码的问题。我在第一次读到这个代码的时候,就对最后调用 m_pManager->RemoveOptionGroup
的条件产生了疑问,于是就看了下手头另外一个版本的 Duilib 代码,发现是一模一样的。其实这里是有个小 bug 的,但是几乎不会影响我们平时的使用。因为几乎谁也不会主动调用 SetGroup
去修改一个单选按钮的组名为空对吧。
问题就出在传入参数为空的情况下,大家想想,此时调用者的目的是要将当前按钮从原来所属的组内移除。但是由于 m_sGroupName.Empty()
调用的时机过早,导致调用 m_pManager->RemoveOptionGroup
时传入的组名为空,这样的话就导致这个函数根本没有起到作用嘛。 之后再对原来组内的其他单选按钮进行控制时,会同时将当前按钮设置非选中状态,呃呃呃。 所以我们需要做的就是在传入参数为空的情况下,先从组内移除,再设置 m_sGroupName
为空。