#include "stdafx.h" #include "ComboBox.h" namespace Forms { ComboBox::ComboBox() : Control(), Items(this), _DrawMode(DrawMode::Normal), _FlatStyle(FlatStyle::Standard), _DropDownStyle(ComboBoxStyle::DropDown), _IntegralHeight(true), _DropDownWidth(-1), _DropDownHeight(-1), _MaxDropDownItems(8), _MaximumLength(SHRT_MAX), _SelectedIndex(-1), _ChildEdit(nullptr), _ChildListBox(nullptr) { SetStyle(ControlStyles::UserPaint | ControlStyles::UseTextForAccessibility | ControlStyles::StandardClick, false); // Default back color is different... this->_BackColor = Drawing::GetSystemColor(Drawing::SystemColors::Window); // We are a ComboBox control. this->_RTTI = ControlTypes::ComboBox; } DrawMode ComboBox::GetDrawMode() { return this->_DrawMode; } void ComboBox::SetDrawMode(DrawMode Value) { if (_DrawMode != Value) { _DrawMode = Value; UpdateStyles(); } } uint32_t ComboBox::DropDownWidth() { if (_DropDownWidth > -1) return _DropDownWidth; return _Width; } void ComboBox::SetDropDownWidth(uint32_t Value) { _DropDownWidth = (int32_t)Value; if (GetState(ControlStates::StateCreated)) SendMessageA(this->_Handle, CB_SETDROPPEDWIDTH, (WPARAM)Value, NULL); } uint32_t ComboBox::DropDownHeight() { if (_DropDownHeight > -1) return _DropDownHeight; return 106; // Default drop down height... } void ComboBox::SetDropDownHeight(uint32_t Value) { _DropDownHeight = (int32_t)Value; SetIntegralHeight(false); } bool ComboBox::DroppedDown() { if (GetState(ControlStates::StateCreated)) return (SendMessageA(this->_Handle, CB_GETDROPPEDSTATE, NULL, NULL) != 0); return false; } void ComboBox::SetDroppedDown(bool Value) { if (GetState(ControlStates::StateCreated)) SendMessageA(this->_Handle, CB_SHOWDROPDOWN, Value ? -1 : 0, NULL); } FlatStyle ComboBox::GetFlatStyle() { return this->_FlatStyle; } void ComboBox::SetFlatStyle(FlatStyle Value) { if (_FlatStyle != Value) { _FlatStyle = Value; UpdateStyles(); } } bool ComboBox::IntegralHeight() { return this->_IntegralHeight; } void ComboBox::SetIntegralHeight(bool Value) { if (_IntegralHeight != Value) { _IntegralHeight = Value; UpdateStyles(); } } int32_t ComboBox::ItemHeight() { return (int32_t)SendMessageA(this->_Handle, CB_GETITEMHEIGHT, NULL, NULL); } void ComboBox::SetItemHeight(int32_t Value) { if (_DrawMode == DrawMode::OwnerDrawFixed) { SendMessageA(this->_Handle, CB_SETITEMHEIGHT, (WPARAM)-1, (LPARAM)Value); SendMessageA(this->_Handle, CB_SETITEMHEIGHT, (WPARAM)0, (LPARAM)Value); } else if (_DrawMode == DrawMode::OwnerDrawVariable) { SendMessageA(this->_Handle, CB_SETITEMHEIGHT, (WPARAM)-1, (LPARAM)Value); for (uint32_t i = 0; i < Items.Count(); i++) SendMessageA(this->_Handle, CB_SETITEMHEIGHT, (WPARAM)i, (LPARAM)Value); } } uint8_t ComboBox::MaxDropDownItems() { return this->_MaxDropDownItems; } void ComboBox::SetMaxDropDownItems(uint8_t Value) { this->_MaxDropDownItems = Value; } uint32_t ComboBox::MaxLength() { return this->_MaximumLength; } void ComboBox::SetMaxLength(uint32_t Value) { if (_MaximumLength != Value) { _MaximumLength = Value; if (GetState(ControlStates::StateCreated)) SendMessageA(this->_Handle, CB_LIMITTEXT, (WPARAM)Value, NULL); } } int32_t ComboBox::SelectedIndex() { if (GetState(ControlStates::StateCreated)) return (int32_t)SendMessageA(this->_Handle, CB_GETCURSEL, NULL, NULL); return this->_SelectedIndex; } void ComboBox::SetSelectedIndex(int32_t Value) { if (GetState(ControlStates::StateCreated)) SendMessageA(this->_Handle, CB_SETCURSEL, (WPARAM)Value, NULL); else _SelectedIndex = Value; OnTextChanged(); OnSelectedIndexChanged(); OnSelectedItemChanged(); } String ComboBox::SelectedText() { if (_DropDownStyle == ComboBoxStyle::DropDownList) return ""; return Text().SubString(SelectionStart(), SelectionLength()); } void ComboBox::SetSelectedText(const String& Value) { if (_DropDownStyle != ComboBoxStyle::DropDownList) { if (GetState(ControlStates::StateCreated)) SendMessageA(this->_ChildEdit, EM_REPLACESEL, (WPARAM)-1, (LPARAM)(const char*)Value); } } int32_t ComboBox::SelectionLength() { int32_t End = 0; int32_t Start = 0; SendMessageA(this->_Handle, CB_GETEDITSEL, (WPARAM)&Start, (WPARAM)&End); return End - Start; } void ComboBox::SetSelectionLength(int32_t Value) { Select(SelectionStart(), Value); } int32_t ComboBox::SelectionStart() { int32_t Value = 0; SendMessageA(this->_Handle, CB_GETEDITSEL, (WPARAM)&Value, NULL); return Value; } void ComboBox::SetSelectionStart(int32_t Value) { Select(Value, SelectionLength()); } ComboBoxStyle ComboBox::DropDownStyle() { return this->_DropDownStyle; } void ComboBox::SetDropDownStyle(ComboBoxStyle Value) { if (_DropDownStyle != Value) { _DropDownStyle = Value; if (GetState(ControlStates::StateCreated)) UpdateStyles(); } } void ComboBox::Select(int32_t Start, int32_t Length) { int32_t End = Start + Length; SendMessageA(this->_Handle, CB_SETEDITSEL, NULL, MAKELPARAM(Start, End)); } void ComboBox::OnHandleCreated() { if (_MaximumLength > 0) SendMessageA(this->_Handle, CB_LIMITTEXT, (WPARAM)_MaximumLength, NULL); if (_DropDownStyle != ComboBoxStyle::DropDownList) { auto Hwnd = GetWindow(this->_Handle, GW_CHILD); if (Hwnd != NULL) { if (_DropDownStyle == ComboBoxStyle::Simple) { _ChildListBox = Hwnd; Hwnd = GetWindow(Hwnd, GW_HWNDNEXT); } _ChildEdit = Hwnd; } } if (_DropDownWidth > -1) SendMessageA(this->_Handle, CB_SETDROPPEDWIDTH, (WPARAM)_DropDownWidth, NULL); // If we have items, add them now for (auto& Item : Items) this->NativeAdd((const char*)Item); if (_SelectedIndex > -1) { SendMessageA(this->_Handle, CB_SETCURSEL, (WPARAM)_SelectedIndex, NULL); _SelectedIndex = -1; } // We must call the base event last Control::OnHandleCreated(); } void ComboBox::OnSelectedItemChanged() { SelectedItemChanged.RaiseEvent(this); } void ComboBox::OnSelectedIndexChanged() { SelectedIndexChanged.RaiseEvent(this); } void ComboBox::OnDropDownClosed() { DropDownClosed.RaiseEvent(this); } void ComboBox::WndProc(Message& Msg) { switch (Msg.Msg) { case WM_CTLCOLOREDIT: case WM_CTLCOLORLISTBOX: Msg.Result = InitializeDCForWmCtlColor((HDC)Msg.WParam, Msg.Msg); break; case WM_REFLECT + WM_COMMAND: WmReflectCommand(Msg); break; default: Control::WndProc(Msg); break; } } CreateParams ComboBox::GetCreateParams() { auto Cp = Control::GetCreateParams(); Cp.ClassName = "COMBOBOX"; Cp.Style |= WS_VSCROLL | CBS_HASSTRINGS | CBS_AUTOHSCROLL; if (!_IntegralHeight) Cp.Style |= CBS_NOINTEGRALHEIGHT; switch (_FlatStyle) { case FlatStyle::Popup: case FlatStyle::Flat: Cp.ExStyle |= WS_EX_STATICEDGE; default: Cp.ExStyle |= WS_EX_CLIENTEDGE; break; } switch (_DropDownStyle) { case ComboBoxStyle::Simple: Cp.Style |= CBS_SIMPLE; break; case ComboBoxStyle::DropDown: Cp.Style |= CBS_DROPDOWN; break; case ComboBoxStyle::DropDownList: Cp.Style |= CBS_DROPDOWNLIST; break; } switch (_DrawMode) { case DrawMode::OwnerDrawFixed: Cp.Style |= CBS_OWNERDRAWFIXED; break; case DrawMode::OwnerDrawVariable: Cp.Style |= CBS_OWNERDRAWVARIABLE; break; } return Cp; } uintptr_t ComboBox::InitializeDCForWmCtlColor(HDC Dc, int32_t Message) { if ((Message == WM_CTLCOLORSTATIC)) { return (uintptr_t)0; } else if ((Message == WM_CTLCOLORLISTBOX) && GetStyle(ControlStyles::UserPaint)) { SetTextColor(Dc, this->ForeColor().ToCOLORREF()); SetBkColor(Dc, this->BackColor().ToCOLORREF()); return BackColorBrush(); } else { return Control::InitializeDCForWmCtlColor(Dc, Message); } } int32_t ComboBox::NativeAdd(const char* Value) { return (int32_t)SendMessageA(this->_Handle, CB_ADDSTRING, NULL, (LPARAM)Value); } void ComboBox::NativeClear() { String Saved; if (_DropDownStyle != ComboBoxStyle::DropDownList) Saved = this->WindowText(); SendMessageA(this->_Handle, CB_RESETCONTENT, NULL, NULL); if (!String::IsNullOrEmpty(Saved)) this->SetWindowText(Saved); } int32_t ComboBox::NativeInsert(int32_t Index, const char* Value) { return (int32_t)SendMessageA(this->_Handle, CB_INSERTSTRING, (WPARAM)Index, (LPARAM)Value); } void ComboBox::NativeRemoveAt(int32_t Index) { if (_DropDownStyle == ComboBoxStyle::DropDownList && SelectedIndex() == Index) Invalidate(); SendMessageA(this->_Handle, CB_DELETESTRING, (WPARAM)Index, NULL); } void ComboBox::WmReflectCommand(Message& Msg) { switch ((int16_t)HIWORD(Msg.WParam)) { case CBN_EDITCHANGE: OnTextChanged(); break; case CBN_SELCHANGE: OnSelectedIndexChanged(); OnSelectedItemChanged(); break; case CBN_CLOSEUP: OnDropDownClosed(); break; } } ComboBox::ComboBoxItemCollection::ComboBoxItemCollection(ComboBox* Owner) : _Owner(Owner), _Items() { } void ComboBox::ComboBoxItemCollection::Add(const imstring& Value) { _Items.EmplaceBack(Value); if (_Owner->GetState(ControlStates::StateCreated)) _Owner->NativeAdd(Value); } void ComboBox::ComboBoxItemCollection::Insert(int32_t Index, const imstring& Value) { _Items.Insert(Index, Value); if (_Owner->GetState(ControlStates::StateCreated)) _Owner->NativeInsert(Index, Value); } void ComboBox::ComboBoxItemCollection::Clear() { _Items.Clear(); if (_Owner->GetState(ControlStates::StateCreated)) _Owner->NativeClear(); } bool ComboBox::ComboBoxItemCollection::Contains(const imstring & Value) { return (IndexOf(Value) > -1); } int32_t ComboBox::ComboBoxItemCollection::IndexOf(const imstring& Value) { auto Str = String(Value); auto Res = _Items.IndexOf(Str); if (Res == List::InvalidPosition) return -1; return Res; } void ComboBox::ComboBoxItemCollection::RemoveAt(int32_t Index) { if (_Owner->GetState(ControlStates::StateCreated)) _Owner->NativeRemoveAt(Index); _Items.RemoveAt(Index); } void ComboBox::ComboBoxItemCollection::Remove(const imstring& Value) { auto Index = IndexOf(Value); if (Index > -1) RemoveAt(Index); } uint32_t ComboBox::ComboBoxItemCollection::Count() { return _Items.Count(); } String* ComboBox::ComboBoxItemCollection::begin() const { return _Items.begin(); } String* ComboBox::ComboBoxItemCollection::end() const { return _Items.end(); } }