在线用户    站点地图 技术论坛 文档中心 站点导航 资源下载 语言参考 教程专题 站点统计 业务项目 使用帮助
您的位置
MSDN参考新加了CSS2参考,全中文,制作精良。245k下载。           新加了sxf_sxf推荐的"动态网页专家指导" 下载!          新加了lsg2002lsg推荐的"ASP.NET高级编程(PDF)" 下载!          新加了DHTML代码预览!!,功能强大哦!欢迎试用          新加了网友lincong为大家提供的SQL Server 2000 程序员指南,有点大,但同时也就说明内容全咯!           新加了网友ibra为大家提供的北大的JAVA课件,很适合初学者入门           新加了一个java的连接缓冲池代码示例           新加了人月神话电子版下载!软件工程巨著哦~~           更新了评分标准,引入了“专家分”概念,相信可以更好的激励大家的学习!详情请看“使用帮助”           新加了由网友GGG提供的“IIS超级管理手册”,值得一看哦!(虽然我没看过 :P )           新加了“英雄榜”,欢迎测试!           “无组件上传·从原理剖析到实践”已经全部完成,源码全部开放,有兴趣的朋友请到文档中心一探究竟。  您的位置  首页>文档中心>C++>文档A0000976 在同一窗口中打开页面中的链接 在新窗口中打开页面中的链接 将ASPCHINA设为首页 将ASPCHINA加入书签    
 LOGIN HERE
用户:
密码: 忘记密码
记住密码 用户注册 游客进入
相关文章
没有相关文章
发表文章
能弹出复选tree列表的combo box

作者:happiness     提交人:happiness     发布时间:12-01-30     文章类型:原创     浏览量:2866
参考链结:http://    
关键字:CTreeCtrl|CComboBox|复选
[本文档没有附件]
字体:    视力保护色: 杏仁黄  秋叶褐  胭脂红  芥末绿  天蓝  雪青  灰  银河白(默认色) [A0000976]

禁止了combo box原单一选择的list box,换成了自己的CTreeCtrl.广泛用于用户录入、查询多选的记录字段。
一直为combo box不能弹出有复选框的CTreeCtrl而烦恼,因为有的记录项需要用户多项选择录入或查询,如果直接在界面上放上所有复选项,那么将浪费有限的空间。所以我下决心搞定combo box.
经过一番跟踪和试验,终于找到了解决办法,这是我成功后的界面:

现在将我研究的过程和结果和大家分享一下:
    终于发现combo box下的list box并不是其子控件,原来是桌面的子控件,且有WS_EX_TOOLWINDOW扩展属性(防止在任务栏中出来标题框,嘿嘿)。
第一步:模仿之:
    新建一个基于CTreeCtrl的子类CTreeList,并做为CComboBox继承类CComboList的成员变量m_tree
BOOL CTreeList::Create(CWnd* pBuddyWnd, UINT nID, DWORD dwStyle)
{
    m_pBuddyWnd = pBuddyWnd;
    dwStyle |= WS_CHILD | WS_BORDER | WS_TABSTOP;
    dwStyle &= ~WS_POPUP;
    return CWnd::CreateEx(WS_EX_TOOLWINDOW, WC_TREEVIEW, NULL, dwStyle, CRect(0,0,0,0), GetDesktopWindow(), nID, NULL);
}

void CComboList::PreSubclassWindow()
{
    m_tree.Create(this, IDC_TREELIST);
    CComboBox::PreSubclassWindow();
}

运行,没报错,有希望。
第二步,弹出它:
void CComboList::OnDropdown()
{
    m_tree.ShowWindow(TRUE, m_cyTree);
}

BOOL CTreeList::ShowWindow(BOOL bShow, int cy)
{
  if (bShow && m_pBuddyWnd)
  {
    if (!m_bInitialize)
    {
      NMHDR nmhdr = {m_hWnd, GetDlgCtrlID(), TVN_INITIALUPDATE};
      if (!::SendMessage(m_pBuddyWnd->m_hWnd, WM_NOTIFY, TVN_INITIALUPDATE, (LPARAM)&nmhdr))//向同伴发初始化通知
        return FALSE;
      m_bInitialize = TRUE;
    }
    NMTREEVIEW nmtv = {{m_hWnd, GetDlgCtrlID(), TVN_WILLBESHOW}, 0, {0, GetDropHilightItem()}, {0}, {0, 0}};
    ::SendMessage(m_pBuddyWnd->m_hWnd, WM_NOTIFY, TVN_WILLBESHOW, (LPARAM)&nmtv);//向同伴发将要显示通知
    RECT rc;
    m_pBuddyWnd->GetWindowRect(&rc);
    SetWindowPos(&wndTop, rc.left, rc.bottom, rc.right - rc.left, cy, SWP_SHOWWINDOW);
  }
  else
  {
    CTreeCtrl::ShowWindow(FALSE);
  }
  return TRUE;
}

它是弹出了,但原有的listbox也弹出了,再努力。幸亏微软提供了的一个函数:
BOOL GetComboBoxInfo(
  HWND hwndCombo, // handle to combo box
  PCOMBOBOXINFO pcbi // combo box information
);
可以让我们找到list box的HWND,有了这个窗口句柄,啥事都好办了,我改变它的消息处理过程。
void CComboList::PreSubclassWindow()
{
    m_tree.Create(this, IDC_TREELIST);
    /////////////////////////////////////////////
    COMBOBOXINFO cbi = {sizeof(cbi)};
    ::GetComboBoxInfo(m_hWnd, &cbi);
    ::SendMessage(cbi.hwndItem, EM_SETREADONLY, TRUE, 0);//不让用户乱输入
    ::SetParent(cbi.hwndList, m_hWnd);//改变其父窗口
    ::SetWindowLong(cbi.hwndList, GWL_WNDPROC, (LONG)ListBoxProc);//变为我自己的消息处理过程
    RECT rc;
    GetDroppedControlRect(&rc);//主要是得到其高度,还有用
    m_cyTree = rc.bottom - rc.top;
    GetWindowRect(&rc);
    m_cyTree -= rc.bottom - rc.top;
    m_cyTree = max(100, m_cyTree);
    CComboBox::PreSubclassWindow();
}

LRESULT CALLBACK CComboList::ListBoxProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    return 0;//自己的消息处理过程,直接返回0,这样它的WM_WINDOWPOSCHANGED等默认的消息处理就被我禁掉了。
}

测试,果然如我所愿,list box老实了,再不弹出了,感觉是在肉鸡,哈哈。
第三步:通过测试发现,控件一但基于桌面,就无法获得焦点了.(可能是非自己顶层窗口下的子窗口的缘故吧)所以为了让自己的treelist响应WM_MOUSEWHEEL,WM_KEYDOWN等需要有焦点才能有效的消息.
模拟tool tip类,中继消息:
BOOL CComboList::PreTranslateMessage(MSG* pMsg)
{
    m_tree.RelayEvent(pMsg);
   
    return CComboBox::PreTranslateMessage(pMsg);
}

void CTreeList::RelayEvent(LPMSG lpMsg)
{
    if (lpMsg->message == WM_MOUSEWHEEL || lpMsg->message == WM_KEYDOWN)
    {
        ::SendMessage(m_hWnd, lpMsg->message, lpMsg->wParam, lpMsg->lParam);
    }
}

经过这一番处理,tree list可以处理这两个消息了,但失去焦点下的选择项是灰背景,有过编程经验的都知道这个现象.但幸亏tree list提供了拖拽到目标项就高亮的功能,偷梁换柱吧。
void CTreeList::OnLButtonDown(UINT nFlags, CPoint point)
{
    UINT uFlag;//接收有关点击测试的信息的整数
    HTREEITEM hItemNew = HitTest(point, &uFlag);//返回与CtreeCtrl关联的光标的当前位置和句柄
    if (hItemNew && ((TVHT_ONITEMLABEL | TVHT_ONITEMSTATEICON) & uFlag))//点中标签文字或复选框
    {
        HTREEITEM hItemOld = GetDropHilightItem();
        if (hItemOld != hItemNew)//发选择项变化通知
        {
            if (hItemOld)
                Select(hItemOld, NULL);//消息原先的目标项高亮显示
            Select(hItemNew, TVGN_DROPHILITE);//让新选择项有拖拽到目标项就高亮显示
            NMTREEVIEW nmtv = {{m_hWnd, GetDlgCtrlID(), TVN_SELCHANGED}, 0, {0, hItemOld}, {0, hItemNew}, {point.x, point.y}};
            ::SendMessage(m_pBuddyWnd->m_hWnd, WM_NOTIFY, TVN_SELCHANGED, (LPARAM)&nmtv);
        }
    }

    CTreeCtrl::OnLButtonDown(nFlags, point);

    if (hItemNew && (TVHT_ONITEMSTATEICON & uFlag))//发复选框状态变化通知
    {
        NMTREEVIEW nmtv = {{m_hWnd, GetDlgCtrlID(), TVN_CHECKCHANGED}, 0, {0, hItemNew}, {0, hItemNew}, {0, 0}};
        ::SendMessage(m_pBuddyWnd->m_hWnd, WM_NOTIFY, TVN_CHECKCHANGED, (LPARAM)&nmtv);
    }
}

WM_KEYDOWN处理过程可以模仿之,上下左右,调用默认的消息处理,只不过让选择项高亮就行了,复选只不过处理空格就行了:
void CTreeList::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    HTREEITEM hItemOld = GetDropHilightItem();

    CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);

    HTREEITEM hItemNew = GetSelectedItem();
    if (hItemOld != hItemNew)//发选择项变化通知
    {
        if (hItemOld)
            Select(hItemOld, NULL);
        Select(hItemNew, TVGN_DROPHILITE);
        NMTREEVIEW nmtv = {{m_hWnd, GetDlgCtrlID(), TVN_SELCHANGED}, 0, {0, hItemOld}, {0, hItemNew}, {0, 0}};
        ::SendMessage(m_pBuddyWnd->m_hWnd, WM_NOTIFY, TVN_SELCHANGED, (LPARAM)&nmtv);
    }

    if (nChar == VK_SPACE)
    {
        HTREEITEM hItemNew = GetDropHilightItem();
        if (hItemNew)//发复选框状态变化通知
        {
            NMTREEVIEW nmtv = {{m_hWnd, GetDlgCtrlID(), TVN_CHECKCHANGED}, 0, {0, hItemNew}, {0, hItemNew}, {0, 0}};
            ::SendMessage(m_pBuddyWnd->m_hWnd, WM_NOTIFY, TVN_CHECKCHANGED, (LPARAM)&nmtv);
        }
    }
}

再测试,发现窗口可以高亮显示选择项,这个类就基本成功了,但还要完善最后一步。
第四步:让同伴类处理4个通知,为了让继承类能重载,先什么都不做:
#define TVS_NOHSCROLL 0x8000//微软没有公开的宏
#define TVN_CHECKCHANGED (TVN_FIRST-16)//用户操作了一个复选框,发此通知
#define TVN_INITIALUPDATE (TVN_FIRST-17)//第一次弹出窗口时,发此通知
#define TVN_WILLBESHOW (TVN_FIRST-18)//每次弹出窗口前,发此通知
注意TVN_INITIALUPDATE通知的*pResult
*pResult = 1;//1初始化成功,0初始失败

BEGIN_MESSAGE_MAP(CComboList, CComboBox)
  //{{AFX_MSG_MAP(CComboList)
  ON_CONTROL_REFLECT(CBN_DROPDOWN, OnDropdown)
  ON_CONTROL_REFLECT(CBN_CLOSEUP, OnCloseup)
  ON_WM_CTLCOLOR()
  ON_NOTIFY(TVN_INITIALUPDATE, IDC_TREELIST, OnTreeInitialUpdate)
  ON_NOTIFY(TVN_WILLBESHOW, IDC_TREELIST, OnTreeWillbeShow)
  ON_NOTIFY(TVN_SELCHANGED, IDC_TREELIST, OnTreeSelchanged)
  ON_NOTIFY(TVN_CHECKCHANGED, IDC_TREELIST, OnTreeCheckChanged)
  ON_WM_PAINT()
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CComboList::OnTreeInitialUpdate(NMHDR* pNMHDR, LRESULT* pResult)
{

  *pResult = 1;
}

void CComboList::OnTreeWillbeShow(NMHDR* pNMHDR, LRESULT* pResult)
{

  *pResult = 0;
}

void CComboList::OnTreeSelchanged(NMTREEVIEW* pNMTV, LRESULT* pResult)
{

  *pResult = 0;
}

void CComboList::OnTreeCheckChanged(NMTREEVIEW* pNMTV, LRESULT* pResult)
{

  *pResult = 0; 
}

有了这几个通知,用户可以方便的重载添加自己的初始化代码,点击复选后保存复选项,让其与输入数据同步等功能。
一切大功告成,打包上来供大家使用。
能弹出复选tree列表的combo box

关于这篇文章的评论 [注意:这里仅仅是给大家提供了一个发表对文章本身看法的地方,如果有疑问,请到论坛提出] 我要提问!
标题
内容
发言
*您尚未以注册用户身份登录,不能发表评论。这里登录
您的位置
  (c)2000-2018 Yup Studio, all rights reserved.  
117.1875