65.9K
CodeProject 正在变化。 阅读更多。
Home

带位图复选框的树控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (9投票s)

2002 年 10 月 24 日

3分钟阅读

viewsIcon

156595

downloadIcon

4701

在树项旁边添加复选框

Sample Image - new_float.jpg

引言

几个月前我发布了一篇关于浮点控件的文章。有人建议我应该在我的控件中添加一个复选框。我花了一个小时,发现这很容易做到。这是我的结果。我今天早上醒来, "__Stephane Rodriguez__" (fakedwar@hotmail.com) 给 我发了一封邮件。他建议修改这篇文章的标题,使其更准确。谢谢。Anthony_Yio 给 我发了一封邮件,建议像 Office 2000 的功能一样,完全选择子项和完全取消选中树形控件中的子项。我也在我的树形控件中添加了该功能。 

错误修复

Stephane Rodriguez 提到了树的点击和展开中的一个错误。这个版本中已经修复。

自定义树形控件的实现

在树形控件的属性页中,有一个名为 CheckBox 的样式。您应该从这一点开始实现您的树形控件。

首先,您应该在您想要使用带复选框的树形控件的资源中选中 CheckBox 样式。在您的项目中添加一个新的位图资源,名为 IDB_STATE,它包含 3 个图像:空白图像、未选中和选中框。

然后,我们从 CTreeControl 派生一个新类。因为这个示例从我之前的文章开始,所以我使用了与之前文章中相同的类。您应该首先使用以下语句加载状态的图像

m_imageState.Create( IDB_STATE, 16, 1, RGB(255,255,255) );
SetImageList( &m_imageState, TVSIL_STATE );

该图像将与表示意义的图像一起使用。我们应该响应 Windows 消息 WM_LBUTTONDOWN 来处理项目的选中或取消选中,如下所示

void CXMLTree::OnLButtonDown(UINT nFlags, CPoint point) 
{
   UINT uFlags=0;
   HTREEITEM hti = HitTest(point,&uFlags);

   if( uFlags & TVHT_ONITEMSTATEICON )
   {
      ToggleItemState(hti);

//  int iImage = GetItemState( hti, TVIS_STATEIMAGEMASK )>>12;
//  SetItemState( hti, INDEXTOSTATEIMAGEMASK(iImage == 1 ? 2 : 1), 
//               TVIS_STATEIMAGEMASK );
   return;
   }
   //The following line is needed for our program to run smoothly.
   CTreeCtrl::OnLButtonDown(nFlags, point);
}

在控件的标准 Windows 实现中,您应该使用空格键来切换复选框的状态。因此,我们需要响应 WM_KEYDOWN 以处理空格键。

   if( nChar == VK_SPACE )
   {
      HTREEITEM hti = GetSelectedItem();
      ToggleItemState(hti);
//      int iImage = GetItemState( hti, TVIS_STATEIMAGEMASK )>>12;
//      SetItemState( hti, INDEXTOSTATEIMAGEMASK(iImage == 1 ? 2 : 1), 
//               TVIS_STATEIMAGEMASK );
      return;
   }
   CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);

我们添加了一个新函数来支持完整选择子项目。更改了 pros 函数以支持它。以下是添加的函数

void CXMLTree::ToggleItemState(HTREEITEM hti)
{
   int iImage = GetItemState( hti, TVIS_STATEIMAGEMASK )>>12;
   SetItemState( hti, INDEXTOSTATEIMAGEMASK(iImage == 1 ? 2 : 1), 
            TVIS_STATEIMAGEMASK );
   if ( ItemHasChildren(hti))
   {
      HTREEITEM htiChild = this->GetChildItem (hti);
      if (htiChild)
         ToggleItemState(htiChild);
      else
         return ;
      HTREEITEM htiSibling = GetNextSiblingItem (htiChild);
      while (htiSibling )
      {
         ToggleItemState(htiSibling);
         htiSibling = GetNextSiblingItem(htiSibling);
      }
   }
}

我们将实现几个辅助函数来获取或设置附加到树形控件中树形项目的复选框状态。如果是复选框,则意味着多重选择,我们将迭代所有选中的项目。我们还提供了用于迭代的函数。但是这些函数尚未经过全面测试。

BOOL CXMLTree::IsItemChecked(HTREEITEM hItem)
{
   return GetItemState( hItem, TVIS_STATEIMAGEMASK )>>12 == 2;
}

HTREEITEM CXMLTree::GetFirstCheckedItem()
{
   for ( HTREEITEM hItem = GetRootItem(); 
     hItem!=NULL; hItem = GetNextItem( hItem ) )
      if ( IsItemChecked(hItem) )
         return hItem;

   return NULL;
}

HTREEITEM CXMLTree::GetNextCheckedItem(HTREEITEM hItem)
{
   for ( hItem = GetNextItem( hItem ); 
     hItem!=NULL; hItem = GetNextItem( hItem ) )
      if ( IsItemChecked(hItem) )
         return hItem;

   return NULL;
}

HTREEITEM CXMLTree::GetPrevCheckedItem(HTREEITEM hItem)
{
   for ( hItem = GetPrevItem( hItem ); 
     hItem!=NULL; hItem = GetPrevItem( hItem ) )
      if ( IsItemChecked(hItem) )
         return hItem;

   return NULL;
}

void CXMLTree::SetChecked(HTREEITEM hItem)
{
   SetItemState( hItem, INDEXTOSTATEIMAGEMASK(1), 
                              TVIS_STATEIMAGEMASK );
}
// GetNextItem   - Get next item as if outline was completely expanded
// Returns      - The item immediately below the reference item
// hItem      - The reference item

HTREEITEM CXMLTree::GetNextItem(HTREEITEM hItem)
{
   HTREEITEM   hti;

   if( ItemHasChildren( hItem ) )
      return GetChildItem( hItem ); // return first child
   else{
      // return next sibling item
      // Go up the tree to find a parent's sibling if needed.
      while( (hti = GetNextSiblingItem( hItem )) == NULL ){
         if( (hItem = GetParentItem( hItem ) ) == NULL )
            return NULL;
      }
   }
   return hti;
}

HTREEITEM CXMLTree::GetPrevItem(HTREEITEM hItem)
{
   HTREEITEM hti;

   // Return a previous sibling item if it exists
   if( hti = GetPrevSiblingItem(hItem) )
      return hti;
   else
      // No sibling, return the parent 
      return GetParentItem(hItem);
}

在树形控件中添加了一个新功能。如果您选择该项目,则会选择其所有子项。 取消选中操作也是如此。 请参阅 insertItem 函数。

使用这个新控件非常简单。在您的对话框中添加一个树形控件并设置复选框样式。其他操作与其他控件相同。 享受吧!

致谢

感谢 Anthony_Yio 和 Stephane Rodriguez。 他们给了我很好的建议和错误报告。 感谢 Prettybabe Prettybabe 提出了编写这样一个控件的想法。

历史

  • 2002 年 10 月 25: 版本 1.0
    • 修复了 SetChecked 中的错误 - 状态错误
    • 修复了鼠标按下以填充节点的错误
    • 添加了用于完整选择和取消选择的新功能
    • 添加了新功能: SetUnChecked
  • 2002 年 10 月 24: 版本 0.9. 初始版本

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.