From 3a409516ccaa6a1694db5ab6c4304810ebefb261 Mon Sep 17 00:00:00 2001 From: Robin Date: Sun, 5 Mar 2017 20:43:13 +0000 Subject: [PATCH] Added dropdown control --- DarkUI/Controls/DarkDropdownItem.cs | 34 ++ DarkUI/Controls/DarkDropdownList.cs | 489 ++++++++++++++++++ DarkUI/DarkUI.csproj | 17 + DarkUI/Icons/DropdownIcons.Designer.cs | 73 +++ DarkUI/Icons/DropdownIcons.resx | 124 +++++ DarkUI/Renderers/DarkMenuRenderer.cs | 20 +- DarkUI/Resources/small_arrow.png | Bin 0 -> 16811 bytes .../Forms/Docking/DockDocument.Designer.cs | 16 + Example/Forms/Docking/DockDocument.cs | 9 + Example/Forms/Docking/DockLayers.Designer.cs | 17 +- Example/Forms/Docking/DockLayers.cs | 9 +- 11 files changed, 795 insertions(+), 13 deletions(-) create mode 100644 DarkUI/Controls/DarkDropdownItem.cs create mode 100644 DarkUI/Controls/DarkDropdownList.cs create mode 100644 DarkUI/Icons/DropdownIcons.Designer.cs create mode 100644 DarkUI/Icons/DropdownIcons.resx create mode 100644 DarkUI/Resources/small_arrow.png diff --git a/DarkUI/Controls/DarkDropdownItem.cs b/DarkUI/Controls/DarkDropdownItem.cs new file mode 100644 index 0000000..e110c29 --- /dev/null +++ b/DarkUI/Controls/DarkDropdownItem.cs @@ -0,0 +1,34 @@ +using System.Drawing; +using System.Windows.Forms; + +namespace DarkUI.Controls +{ + public class DarkDropdownItem + { + #region Property Region + + public string Text { get; set; } + + public Bitmap Icon { get; set; } + + #endregion + + #region Constructor Region + + public DarkDropdownItem() + { } + + public DarkDropdownItem(string text) + { + Text = text; + } + + public DarkDropdownItem(string text, Bitmap icon) + : this(text) + { + Icon = icon; + } + + #endregion + } +} diff --git a/DarkUI/Controls/DarkDropdownList.cs b/DarkUI/Controls/DarkDropdownList.cs new file mode 100644 index 0000000..ad43722 --- /dev/null +++ b/DarkUI/Controls/DarkDropdownList.cs @@ -0,0 +1,489 @@ +using DarkUI.Config; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; + +namespace DarkUI.Controls +{ + public class DarkDropdownList : Control + { + #region Event Region + + public event EventHandler SelectedItemChanged; + + #endregion + + #region Field Region + + private DarkControlState _controlState = DarkControlState.Normal; + + private ObservableCollection _items = new ObservableCollection(); + private DarkDropdownItem _selectedItem; + + private DarkContextMenu _menu = new DarkContextMenu(); + private bool _menuOpen = false; + + private bool _showBorder = true; + + private int _itemHeight = 22; + private int _maxHeight = 130; + + private readonly int _iconSize = 16; + + private ToolStripDropDownDirection _dropdownDirection = ToolStripDropDownDirection.Default; + + #endregion + + #region Property Region + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public ObservableCollection Items + { + get { return _items; } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public DarkDropdownItem SelectedItem + { + get { return _selectedItem; } + set + { + _selectedItem = value; + SelectedItemChanged?.Invoke(this, new EventArgs()); + } + } + + [Category("Appearance")] + [Description("Determines whether a border is drawn around the control.")] + [DefaultValue(true)] + public bool ShowBorder + { + get { return _showBorder; } + set + { + _showBorder = value; + Invalidate(); + } + } + + protected override Size DefaultSize + { + get { return new Size(100, 26); } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public DarkControlState ControlState + { + get { return _controlState; } + } + + [Category("Appearance")] + [Description("Determines the height of the individual list view items.")] + [DefaultValue(22)] + public int ItemHeight + { + get { return _itemHeight; } + set + { + _itemHeight = value; + ResizeMenu(); + } + } + + [Category("Appearance")] + [Description("Determines the maximum height of the dropdown panel.")] + [DefaultValue(130)] + public int MaxHeight + { + get { return _maxHeight; } + set + { + _maxHeight = value; + ResizeMenu(); + } + } + + [Category("Behavior")] + [Description("Determines what location the dropdown list appears.")] + [DefaultValue(ToolStripDropDownDirection.Default)] + public ToolStripDropDownDirection DropdownDirection + { + get { return _dropdownDirection; } + set { _dropdownDirection = value; } + } + + #endregion + + #region Constructor Region + + public DarkDropdownList() + { + SetStyle(ControlStyles.OptimizedDoubleBuffer | + ControlStyles.ResizeRedraw | + ControlStyles.UserPaint | + ControlStyles.Selectable | + ControlStyles.UserMouse, true); + + _menu.AutoSize = false; + _menu.Closed += Menu_Closed; + + Items.CollectionChanged += Items_CollectionChanged; + SelectedItemChanged += DarkDropdownList_SelectedItemChanged; + + SetControlState(DarkControlState.Normal); + } + + #endregion + + #region Method Region + + private ToolStripMenuItem GetMenuItem(DarkDropdownItem item) + { + foreach (ToolStripMenuItem menuItem in _menu.Items) + { + if ((DarkDropdownItem)menuItem.Tag == item) + return menuItem; + } + + return null; + } + + private void SetControlState(DarkControlState controlState) + { + if (_menuOpen) + return; + + if (_controlState != controlState) + { + _controlState = controlState; + Invalidate(); + } + } + + private void ShowMenu() + { + if (_menu.Visible) + return; + + SetControlState(DarkControlState.Pressed); + + _menuOpen = true; + + var pos = new Point(0, ClientRectangle.Bottom); + + if (_dropdownDirection == ToolStripDropDownDirection.AboveLeft || _dropdownDirection == ToolStripDropDownDirection.AboveRight) + pos.Y = 0; + + _menu.Show(this, pos, _dropdownDirection); + + if (SelectedItem != null) + { + var selectedItem = GetMenuItem(SelectedItem); + selectedItem.Select(); + } + } + + private void ResizeMenu() + { + var width = ClientRectangle.Width; + var height = (_menu.Items.Count * _itemHeight) + 4; + + if (height > _maxHeight) + height = _maxHeight; + + // Dirty: Check what the autosized items are + foreach (ToolStripMenuItem item in _menu.Items) + { + item.AutoSize = true; + + if (item.Size.Width > width) + width = item.Size.Width; + + item.AutoSize = false; + } + + // Force the size + foreach (ToolStripMenuItem item in _menu.Items) + item.Size = new Size(width - 1, _itemHeight); + + _menu.Size = new Size(width, height); + } + + #endregion + + #region Event Handler Region + + private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.Action == NotifyCollectionChangedAction.Add) + { + foreach (DarkDropdownItem item in e.NewItems) + { + var menuItem = new ToolStripMenuItem(item.Text) + { + Image = item.Icon, + AutoSize = false, + Height = _itemHeight, + Font = Font, + Tag = item, + TextAlign = ContentAlignment.MiddleLeft + }; + + _menu.Items.Add(menuItem); + menuItem.Click += Item_Select; + + if (SelectedItem == null) + SelectedItem = item; + } + } + + if (e.Action == NotifyCollectionChangedAction.Remove) + { + foreach (DarkDropdownItem item in e.OldItems) + { + foreach (ToolStripMenuItem menuItem in _menu.Items) + { + if ((DarkDropdownItem)menuItem.Tag == item) + _menu.Items.Remove(menuItem); + } + } + } + + ResizeMenu(); + } + + private void Item_Select(object sender, EventArgs e) + { + var menuItem = sender as ToolStripMenuItem; + if (menuItem == null) + return; + + var dropdownItem = (DarkDropdownItem)menuItem.Tag; + if (_selectedItem != dropdownItem) + SelectedItem = dropdownItem; + } + + private void DarkDropdownList_SelectedItemChanged(object sender, EventArgs e) + { + foreach (ToolStripMenuItem item in _menu.Items) + { + if ((DarkDropdownItem)item.Tag == SelectedItem) + { + item.BackColor = Colors.DarkBlueBackground; + item.Font = new Font(Font, FontStyle.Bold); + } + else + { + item.BackColor = Colors.GreyBackground; + item.Font = new Font(Font, FontStyle.Regular); + } + } + + Invalidate(); + } + + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + + ResizeMenu(); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + if (e.Button == MouseButtons.Left) + { + if (ClientRectangle.Contains(e.Location)) + SetControlState(DarkControlState.Pressed); + else + SetControlState(DarkControlState.Hover); + } + else + { + SetControlState(DarkControlState.Hover); + } + } + + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + + ShowMenu(); + } + + protected override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp(e); + + SetControlState(DarkControlState.Normal); + } + + protected override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + + SetControlState(DarkControlState.Normal); + } + + protected override void OnMouseCaptureChanged(EventArgs e) + { + base.OnMouseCaptureChanged(e); + + var location = Cursor.Position; + + if (!ClientRectangle.Contains(location)) + SetControlState(DarkControlState.Normal); + } + + protected override void OnGotFocus(EventArgs e) + { + base.OnGotFocus(e); + + Invalidate(); + } + + protected override void OnLostFocus(EventArgs e) + { + base.OnLostFocus(e); + + var location = Cursor.Position; + + if (!ClientRectangle.Contains(location)) + SetControlState(DarkControlState.Normal); + else + SetControlState(DarkControlState.Hover); + } + + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + + if (e.KeyCode == Keys.Space) + ShowMenu(); + } + + private void Menu_Closed(object sender, ToolStripDropDownClosedEventArgs e) + { + _menuOpen = false; + + if (!ClientRectangle.Contains(MousePosition)) + SetControlState(DarkControlState.Normal); + else + SetControlState(DarkControlState.Hover); + } + + #endregion + + #region Render Region + + protected override void OnPaint(PaintEventArgs e) + { + var g = e.Graphics; + + // Draw background + using (var b = new SolidBrush(Colors.MediumBackground)) + { + g.FillRectangle(b, ClientRectangle); + } + + // Draw normal state + if (ControlState == DarkControlState.Normal) + { + if (ShowBorder) + { + using (var p = new Pen(Colors.LightBorder, 1)) + { + var modRect = new Rectangle(ClientRectangle.Left, ClientRectangle.Top, ClientRectangle.Width - 1, ClientRectangle.Height - 1); + g.DrawRectangle(p, modRect); + } + } + } + + // Draw hover state + if (ControlState == DarkControlState.Hover) + { + using (var b = new SolidBrush(Colors.DarkBorder)) + { + g.FillRectangle(b, ClientRectangle); + } + + using (var b = new SolidBrush(Colors.DarkBackground)) + { + var arrowRect = new Rectangle(ClientRectangle.Right - DropdownIcons.small_arrow.Width - 8, ClientRectangle.Top, DropdownIcons.small_arrow.Width + 8, ClientRectangle.Height); + g.FillRectangle(b, arrowRect); + } + + using (var p = new Pen(Colors.BlueSelection, 1)) + { + var modRect = new Rectangle(ClientRectangle.Left, ClientRectangle.Top, ClientRectangle.Width - 1 - DropdownIcons.small_arrow.Width - 8, ClientRectangle.Height - 1); + g.DrawRectangle(p, modRect); + } + } + + // Draw pressed state + if (ControlState == DarkControlState.Pressed) + { + using (var b = new SolidBrush(Colors.DarkBorder)) + { + g.FillRectangle(b, ClientRectangle); + } + + using (var b = new SolidBrush(Colors.BlueSelection)) + { + var arrowRect = new Rectangle(ClientRectangle.Right - DropdownIcons.small_arrow.Width - 8, ClientRectangle.Top, DropdownIcons.small_arrow.Width + 8, ClientRectangle.Height); + g.FillRectangle(b, arrowRect); + } + } + + // Draw dropdown arrow + using (var img = DropdownIcons.small_arrow) + { + g.DrawImageUnscaled(img, ClientRectangle.Right - img.Width - 4, ClientRectangle.Top + (ClientRectangle.Height / 2) - (img.Height / 2)); + } + + // Draw selected item + if (SelectedItem != null) + { + // Draw Icon + var hasIcon = SelectedItem.Icon != null; + + if (hasIcon) + { + g.DrawImageUnscaled(SelectedItem.Icon, new Point(ClientRectangle.Left + 5, ClientRectangle.Top + (ClientRectangle.Height / 2) - (_iconSize / 2))); + } + + // Draw Text + using (var b = new SolidBrush(Colors.LightText)) + { + var stringFormat = new StringFormat + { + Alignment = StringAlignment.Near, + LineAlignment = StringAlignment.Center + }; + + var rect = new Rectangle(ClientRectangle.Left + 2, ClientRectangle.Top, ClientRectangle.Width - 16, ClientRectangle.Height); + + if (hasIcon) + { + rect.X += _iconSize + 7; + rect.Width -= _iconSize + 7; + } + + g.DrawString(SelectedItem.Text, Font, b, rect, stringFormat); + } + } + } + + #endregion + } +} diff --git a/DarkUI/DarkUI.csproj b/DarkUI/DarkUI.csproj index 6f4914b..5d274ca 100644 --- a/DarkUI/DarkUI.csproj +++ b/DarkUI/DarkUI.csproj @@ -50,6 +50,10 @@ Component + + + Component + @@ -161,6 +165,11 @@ True DockIcons.resx + + True + True + DropdownIcons.resx + True True @@ -202,6 +211,11 @@ DockIcons.Designer.cs DarkUI + + ResXFileCodeGenerator + DropdownIcons.Designer.cs + DarkUI + PublicResXFileCodeGenerator MenuIcons.Designer.cs @@ -292,6 +306,9 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\small_arrow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/DarkUI/Renderers/DarkMenuRenderer.cs b/DarkUI/Renderers/DarkMenuRenderer.cs index 9997e66..ef80e2f 100644 --- a/DarkUI/Renderers/DarkMenuRenderer.cs +++ b/DarkUI/Renderers/DarkMenuRenderer.cs @@ -1,4 +1,5 @@ using DarkUI.Config; +using DarkUI.Controls; using DarkUI.Icons; using System; using System.Drawing; @@ -22,6 +23,7 @@ namespace DarkUI.Renderers { base.InitializeItem(item); + item.BackColor = Colors.GreyBackground; item.ForeColor = Colors.LightText; if (item.GetType() == typeof(ToolStripSeparator)) @@ -107,15 +109,15 @@ namespace DarkUI.Renderers if (e.Item.Enabled) { - // Normal item - if (e.Item.Selected) - { - var rect = new Rectangle(2, 0, e.Item.Width - 3, e.Item.Height); + + var bgColor = e.Item.Selected ? Colors.GreyHighlight : e.Item.BackColor; - using (var b = new SolidBrush(Colors.GreySelection)) - { - g.FillRectangle(b, rect); - } + // Normal item + var rect = new Rectangle(2, 0, e.Item.Width - 3, e.Item.Height); + + using (var b = new SolidBrush(bgColor)) + { + g.FillRectangle(b, rect); } // Header item on open menu @@ -123,8 +125,6 @@ namespace DarkUI.Renderers { if (((ToolStripMenuItem)e.Item).DropDown.Visible && e.Item.IsOnDropDown == false) { - var rect = new Rectangle(2, 0, e.Item.Width - 3, e.Item.Height); - using (var b = new SolidBrush(Colors.GreySelection)) { g.FillRectangle(b, rect); diff --git a/DarkUI/Resources/small_arrow.png b/DarkUI/Resources/small_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..482481653597a147e56e890c8a408c4b4ca88010 GIT binary patch literal 16811 zcmeI4O^n+_6vtgvAXw2}I3Sgal?n+|9Dl@iVw0pTn=ZQ&DXZ;P-SQzivB$es6WiF% zCcCGK!l73rB!swgKmzVaAS5br=nZk{0Zts?Qi&6ADbIF1@p_Z(lwFFDCTcdX_mSI=UwilXX69NoyjQH`O??vXgRY~KTU?1L>#fT9 zq(gqAyUH*6A?CHJE?9C+;p;|K)(oYgzQ`+*tc#K=%7!4FM!nyZO2^e zEKRzD|FtSxaU7VUI2w(rqgvGuH$iR~qNIq5B7lbw?RYU73tn_O3zEm_;K&NyAa;F^ zPvWAUKa5+IO41Q|O~y3{NJm~YVFwh&F$zSvDv7g1wna3-FzlzdW?Lfe;{o>K2z2sH z9U$#?iF9UngTYK?6kphe5lo28R7UGN0T$PA+C!zz|^TAw=L80!vTuh?f`9KG4M8< z;hK8!^+NR{x9794ip<&c}f^4fuFlx3X7=|Njy}In^wvmpZ z+bzoXh3@#)FxfZ|I?FM(Z$WsnfJ|G{B*$v>1WC4`BN^9)o+UYgrS%lccI+CeIeABg zVEa0HEp#CTqJF_A8H){_HB<=ZsD^-Ry}F>QsxH7N6am==wrtce;3My1MwoqFExQq{ zx1GW|gw>cPxl8z+y{-9$zMCei0EH1w_HwI|Z)TF+Nb*WFX-+Vam87}0l`L>-G5;sBD8(TY0zbikb#6oPsM`lsE*LkHCaHi)M#KN!5dcM;io~*v($e#4bxR@ zZ6R+H+ifu)S>EsnXU_7yZ5%$#CW5Vspcuko)OJFDz@s4OyB10|q`2+blWKzGF+TSB zLbA=c7h*iZvD!FvA7-5v?vh~Q|7T-9^v+ausb15uE;wpk6&jWbC!efi!BBNgL9*3D z8p_x2VG<f@(Kl@9Yq$GwK zuahN}r6d(5=j($j87M~J+l5)!EO~9&pgaXj@;xbZ5*@0^yu5t4YA?IFf_zqV|FAV> z*2|1A)82CO1;B;MrAZDLbJ6enVb`~DyVewEH5Xzg-?2<6WudG}Ppr1w6!*22{Wp@5 zv_DKsPD7XACCbU)iL`jREWLWLw0OBJohmPTZroNTy_K2^s^p7c2ICSu{J?Ep(`s5% zEH{@GtL_;0H##nS8HtjjI+v8&XX>s_bpSu@D%BO*C%SBN6&_NglbN-`BM^A{B9g~1 zg?T0={LvfJ0Udp85|MR6P|z3*7Y!xi!*CH4GzP;(Ly7n>Tm%J;!En(~B0dZkK|y0M zTr`x355q-J&=?FC4JG2ka1j(V2E#=|iTE&F1O<)3aM4gAJ`5K@L1QpnG?a)B!$nZg z7z`H;CE~+y5fn5A!$m`h_%K`q1&zUQ(NH2j3>QH`V=!Dal!y<*MNrTf3>OV0;=^ze z6f_3IMMH`BFkA!$jlpoyP$E7I7ePT|FkCd0h!4X>P|z3*7Y!xi!*CH4GzP;(Ly7n> zTm%J;!En(~B0dZkK|y02h-+c`g9g}xUojZL&lUXm-m(clb--K8Yh8}peU9U9+~l}F z@5AS>95<9W?zd|kXMV(S&-fp_eR~Of&#ZLLuaED(`Te&iIO(aI=RW=H%lk_|z5jLP t@-M^Q-H*RGe&?kdw@zt;w|+jxeWE@6`=I&59guM=7cO=_fBD+Ge*@