【问题标题】:Forcing a combobox to "dropdown" above instead of below强制组合框在上方而不是下方“下拉”
【发布时间】:2016-03-30 11:25:59
【问题描述】:

当您单击组合框的“下拉”按钮时,下拉列表框出现在组合框的下方,除非下方没有足够的空间,在这种情况下列表框出现在上方.

现在我想知道是否有可能强制 lisbox 出现在组合框的上方,即使下方有足够的空间。

插图

当我点击组合框时,我希望“下拉”列表框始终显示在左侧屏幕副本的上方。

【问题讨论】:

  • 据我所知不可能...这是由绘制列表的控件处理的。现在你总是可以继承控制和覆盖paint方法...
  • 这是 Windows 处理组合框和菜单的方式,您无法更改。覆盖油漆或绘图不会改变它。您可能需要从头开始编写自己的自定义控件。放弃这个想法!

标签: winapi combobox mfc comctl32


【解决方案1】:

一切皆有可能,无需“从头开始”实现控件。

首先,您可以将 ComboBox 的 ListBox 部分子类化以完全控制它,如MSDN 中所述。您可以使用类向导创建从 CListBox 派生的类。您只需要在其中实现WM_WINPOSITIONCHANGING 处理程序:

void CTopListBox::OnWindowPosChanging(WINDOWPOS* lpwndpos)
{
    CListBox::OnWindowPosChanging(lpwndpos);
    if ((lpwndpos->flags & SWP_NOMOVE) == 0)
    {
        lpwndpos->y -= lpwndpos->cy + 30;
    }
}

在这里,为简单起见,我将框向上移动(高度+30)。您可以获得 ComboBox 的高度,而不是我的 30

然后你在你的对话框类中声明一个成员变量:

CTopListBox m_listbox;

并像这样子类化它:

HBRUSH CMFCDlgDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    if (nCtlColor == CTLCOLOR_LISTBOX)
    {
        if (m_listbox.GetSafeHwnd() == NULL)
        {
            m_listbox.SubclassWindow(pWnd->GetSafeHwnd());
            CRect r;
            m_listbox.GetWindowRect(r);
            m_listbox.MoveWindow(r);
        }
    }

    HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
    return hbr;
}

请注意,我在那里打电话给m_listbox.MoveWindow(r);它是必需的,因为该列表框的第一个 WM_CONTROLCOLOR 消息在它定位之后出现,所以第一次它会下拉而不是向上。

免责声明:这不是一个非常干净的解决方案,因为如果您启用了 Windows 动画,您会看到列表从上到下展开。

或者,您应该能够“欺骗”组合框,使其太靠近屏幕底部;然后它会自己掉下来。我把它作为练习留给读者:)

【讨论】:

  • 使用 GetComboBoxInfo 时,子类化要容易得多。也可以只在 CBN_DROPDOWN 中处理。
【解决方案2】:

这将相对容易,除非组合框具有“滑动打开”效果。如果将下拉列表框移到顶部,并且组合幻灯片从上到下打开,它看起来会很奇怪。所以你必须禁用动画或反转它。

在这个函数中,我在OnWindowPosChanging 中调用AnimateWindow,它似乎没有引起任何问题,但我不是100% 确定它!

class CComboBox_ListBox : public CListBox
{
public:
    CWnd *comboBox;
    void OnWindowPosChanging(WINDOWPOS *wndpos)
    {
        CListBox::OnWindowPosChanging(wndpos);
        if (comboBox && wndpos->cx && wndpos->cy && !(wndpos->flags & SWP_NOMOVE))
        {
            CRect rc;
            comboBox->GetWindowRect(&rc);

            //if listbox is at the bottom...
            if (wndpos->y > rc.top) {
                //if there is enough room for listbox to go on top...
                if (rc.top > wndpos->cy) {
                    wndpos->y = rc.top - wndpos->cy;
                    BOOL animation;
                    SystemParametersInfo(SPI_GETCOMBOBOXANIMATION, 0, &animation, 0);
                    //if combobox slides open...
                    if (animation) {
                        //we have to set the x coordinate otherwise listbox 
                        //is in the wrong place when parent window moves
                        SetWindowPos(0, wndpos->x, wndpos->y, 0, 0, 
                            SWP_NOSENDCHANGING | SWP_HIDEWINDOW | SWP_NOSIZE);

                        AnimateWindow(100, AW_VER_NEGATIVE);
                    }
                }
            }
        }
    }
    DECLARE_MESSAGE_MAP()
};

用法:

COMBOBOXINFO ci = { sizeof(COMBOBOXINFO) };
comboBox.GetComboBoxInfo(&ci);
CComboBox_ListBox *listBox = new CComboBox_ListBox;
listBox->comboBox = &comboBox;
listBox->SubclassWindow(ci.hwndList);

您也可以使用SetMinVisibleItems 降低列表框高度并确保下拉列表适合顶部。

【讨论】:

  • OnWindowPosChanging 被调用的原因有很多(激活等);您需要检查SWP_NOMOVE 标志以查看大小是否发生变化。
  • @Vlad 我测试了cxcy,我认为这是一回事。我添加了SWP_NOMOVE 测试以确保
  • 仅测试一个位置是不够的 - 如果设置了标志 SWP_NOMOVE,它们可能为 0。
猜你喜欢
  • 1970-01-01
  • 2012-02-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-30
  • 1970-01-01
  • 2020-03-11
相关资源
最近更新 更多