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