From a27f65e3d11c37eef16bd1393459051566d87e66 Mon Sep 17 00:00:00 2001 From: Patrick Simpson Date: Thu, 29 Jun 2017 15:50:21 +0200 Subject: [PATCH] Added owner drawing of combo box items --- .../AcaciaZPushPlugin.csproj | 3 + .../AcaciaZPushPlugin/Controls/KComboBox.cs | 74 +++++++++++++--- .../Controls/KComboBoxCustomDraw.cs | 85 +++++++++++++++++++ .../AcaciaZPushPlugin/Controls/KUIUtil.cs | 7 +- 4 files changed, 156 insertions(+), 13 deletions(-) create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBoxCustomDraw.cs diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj index e95a424..e9e4b33 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj @@ -226,6 +226,9 @@ Component + + Component + Component diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs index 3f14873..309b49c 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs @@ -28,12 +28,22 @@ namespace Acacia.Controls private readonly KComboBox _owner; private int _committedIndex = -1; - public DropList(KComboBox owner) + public DropList(KComboBox owner, bool ownerDraw) { this._owner = owner; SetStyle(ControlStyles.OptimizedDoubleBuffer, true); SetStyle(ControlStyles.Selectable, false); BorderStyle = BorderStyle.None; + + if (ownerDraw) + { + DrawMode = DrawMode.OwnerDrawFixed; + } + } + + protected override void OnDrawItem(DrawItemEventArgs e) + { + _owner.OnDrawItem(e); } protected override void OnMouseMove(MouseEventArgs e) @@ -98,7 +108,6 @@ namespace Acacia.Controls public void ItemsChanged(int selectIndex) { _committedIndex = SelectedIndex = selectIndex; - } } @@ -127,10 +136,14 @@ namespace Acacia.Controls private DisplayItem _selectedItem; - public KComboBox() + public KComboBox() : this(false) { + } + + protected internal KComboBox(bool ownerDraw) + { MaxDropDownItems = 8; - _list = new DropList(this); + _list = new DropList(this, ownerDraw); _list.IntegralHeight = true; _list.TabStop = false; _list.SelectedIndexChanged += _list_SelectedIndexChanged; @@ -164,31 +177,30 @@ namespace Acacia.Controls /// /// Wrapper for list items to use custom string formatting /// - private class DisplayItem + public class DisplayItem { private readonly KComboBox _owner; - private readonly object _item; + public readonly object Item; public DisplayItem(KComboBox owner, object item) { this._owner = owner; - this._item = item; + this.Item = item; } public override string ToString() { - return _owner.DataSource.GetItemText(_item); + return _owner.DataSource.GetItemText(Item); } public override bool Equals(object obj) { - bool result = obj is DisplayItem && ((DisplayItem)obj)._item == _item; - return result; + return obj is DisplayItem && ((DisplayItem)obj).Item == Item; } public override int GetHashCode() { - return ToString().GetHashCode(); + return Item.GetHashCode(); } } @@ -226,12 +238,46 @@ namespace Acacia.Controls // Select the current item only if new number of items is smaller. This means we don't keep selection // when the user is removing text, only when they are typing more. _list.ItemsChanged(_list.Items.Count < oldCount ? selected : -1); + + MeasureItems(); + UpdateDropDownLayout(); } finally { _list.EndUpdate(); } - UpdateDropDownLayout(); + } + + protected IEnumerable DisplayItems + { + get + { + foreach (object item in _list.Items) + yield return (DisplayItem)item; + } + } + + protected DisplayItem GetDisplayItem(int index) + { + return (DisplayItem)_list.Items[index]; + } + + protected int DisplayItemCount + { + get { return _list.Items.Count; } + } + + virtual protected void OnDrawItem(DrawItemEventArgs e) { } + + protected virtual void MeasureItems() + { + // Virtual placeholder + } + + protected void SetItemSize(Size size) + { + ItemHeight = size.Height; + _list.Width = size.Width; } protected override void OnTextChanged(EventArgs e) @@ -265,6 +311,10 @@ namespace Acacia.Controls // Forward cursor keys to the list case Keys.Down: case Keys.Up: + case Keys.PageDown: + case Keys.PageUp: + case Keys.Home: + case Keys.End: User32.SendMessage(_list.Handle, (int)WM.KEYDOWN, new IntPtr((int)e.KeyCode), IntPtr.Zero); e.IsInputKey = true; break; diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBoxCustomDraw.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBoxCustomDraw.cs new file mode 100644 index 0000000..cb15187 --- /dev/null +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBoxCustomDraw.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Acacia.Controls +{ + abstract public class KComboBoxCustomDraw : KComboBox + { + public class MeasureItemEventArgs : EventArgs + { + public readonly Graphics Graphics; + public readonly DisplayItem DisplayItem; + public object Item { get { return DisplayItem.Item; } } + public int ItemWidth { get; set; } + public int ItemHeight { get; set; } + + public MeasureItemEventArgs(Graphics graphics, DisplayItem item) + { + this.Graphics = graphics; + this.DisplayItem = item; + } + } + + public class DrawItemEventArgs : System.Windows.Forms.DrawItemEventArgs + { + public readonly DisplayItem DisplayItem; + + public object Item { get { return DisplayItem.Item; } } + + public DrawItemEventArgs(System.Windows.Forms.DrawItemEventArgs e, DisplayItem item) + : + base(e.Graphics, e.Font, e.Bounds, e.Index, e.State, e.ForeColor, e.BackColor) + { + DisplayItem = item; + } + + } + + public KComboBoxCustomDraw() : base(true) + { + } + + sealed protected override void OnDrawItem(System.Windows.Forms.DrawItemEventArgs e) + { + OnDrawItem(new DrawItemEventArgs(e, GetDisplayItem(e.Index))); + } + + abstract protected void OnDrawItem(DrawItemEventArgs e); + + protected abstract void OnMeasureItem(MeasureItemEventArgs e); + + private readonly Dictionary _sizeCache = new Dictionary(); + + protected override void MeasureItems() + { + int maxWidth = 0, maxHeight = 0; + using (Graphics graphics = CreateGraphics()) + { + foreach (DisplayItem item in DisplayItems) + { + Size s; + if (!_sizeCache.TryGetValue(item, out s)) + { + MeasureItemEventArgs e = new MeasureItemEventArgs(graphics, item); + OnMeasureItem(e); + s = new Size(e.ItemWidth, e.ItemHeight); + _sizeCache.Add(item, s); + } + + maxWidth = Math.Max(maxWidth, s.Width); + maxHeight = Math.Max(maxHeight, s.Height); + } + } + + if (maxHeight > 0) + { + SetItemSize(new Size(maxWidth, maxHeight)); + } + } + } +} diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KUIUtil.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KUIUtil.cs index 2e4d15f..07ad8be 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KUIUtil.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KUIUtil.cs @@ -24,7 +24,7 @@ using System.Windows.Forms; namespace Acacia.Controls { - internal static class KUIUtil + public static class KUIUtil { #region Geometry @@ -126,6 +126,11 @@ namespace Acacia.Controls return r; } + public static Point TopLeft(this Rectangle _this) + { + return new Point(_this.Left, _this.Top); + } + #endregion } }