Заставить выпадающий список "вместо" внизу

При нажатии на кнопку "выпадающего списка" в выпадающем списке под выпадающим списком появляется выпадающий список, если только под ним недостаточно места, в этом случае список появляется выше.

Теперь я задаюсь вопросом, есть ли возможность заставить список отображаться над списком, даже если под ним достаточно места.

иллюстрация

Когда я нажимаю на поле со списком, я бы хотел, чтобы список "выпадающий" отображался всегда выше, как на левой копии экрана.

введите описание изображения здесь

2 ответа

Все возможно, и вам не нужно реализовывать управление "с нуля".

Во-первых, вы можете создать подкласс класса ListBox вашего ComboBox, чтобы получить полный контроль над ним, как описано в 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, вы увидите, что список разворачивается сверху вниз.

В качестве альтернативы, вы должны быть в состоянии "обмануть" комбинированный список, так как он расположен слишком близко к нижней части экрана; тогда оно выпадет само собой. Я оставляю это как упражнение для читателей:)

Это было бы относительно легко, за исключением случаев, когда поле со списком имеет эффект "открытия слайда". Если вы переместите раскрывающийся список в верхнюю часть, а комбо-слайд откроется сверху вниз, это будет выглядеть странно. Таким образом, вы должны отключить анимацию или повернуть ее вспять.

В этой функции я вызываю AnimateWindow в OnWindowPosChangingКажется, это не вызывает никаких проблем, но я не уверен на 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 чтобы уменьшить высоту списка и убедиться, что выпадающий список помещается сверху.

Другие вопросы по тегам