具有透明背景的Winforms的播放列表控件

h22fl7wq  于 2022-11-30  发布在  其他
关注(0)|答案(1)|浏览(177)

我正在尝试为项目创建播放列表(ListBox)控件。
此时,控件需要正确显示透明背景、项目选择和标记项目。
我只是在和透明背景做斗争。我知道这是可能的,但很难做到。
控制工作得相当好;我似乎很难清除选择和焦点背景矩形;并且因此执行选择而没有问题。
有谁能帮助完成这一点吗?
代码波纹管:

// <copyright file="RGPlaylist.cs" company="None"> Copyright (c) Ricardo Garcia. </copyright>
// <author> Ricardo Garcia </author>
// <date> 2022, 11.28 </date>

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace RG_Playlist.Controls
{
    public class RGPlaylist : ListBox
    {
        #region <Developer Notes>
        /*
         * Some Help from Jimi (Stackoverflow)
         * 
         */
        #endregion

        private void SetStyles()
        {
            //SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            //SetStyle(ControlStyles.SupportsTransparentBackColor, true);

            //DrawMode = DrawMode.OwnerDrawVariable;

            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            SetStyle(ControlStyles.Opaque, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.ResizeRedraw, true);
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);

            DrawMode = DrawMode.OwnerDrawFixed;
        }

        #region <Constructor>
        public RGPlaylist()
        {
            // > Set Styles
            SetStyles();

            // > Set the Item Selection Mode
            SelectionMode = SelectionMode.One;

            // > Further Configuration
            IntegralHeight = false;
            BorderStyle = BorderStyle.FixedSingle;
            BackColor = Color.FromArgb(255, 25, 25, 25);
            ForeColor = Color.Gainsboro;
        }
        #endregion
        // USED FOR TRANSPARENT BACKGOROUND

        [DllImport("uxtheme", ExactSpelling = true)]
        private extern static int DrawThemeParentBackground
            (
            IntPtr hWnd,        // Control Window Background Location?
            IntPtr hdc,         // Item Background Location ?
            ref Rectangle pRect // Item Background Rectangle?
            );

        #region <Fields>
        private const int LB_RESETCONTENT = 0x0184;
        private const int LB_DELETESTRING = 0x0182;
        private const int WM_KILLFOCUS = 0x8;
        private const int WM_VSCROLL = 0x115;
        private const int WM_HSCROLL = 0x114;

        private TextFormatFlags flags = TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.LeftAndRightPadding | TextFormatFlags.VerticalCenter;
        #endregion

        #region <Auto Properties>
        public bool IsSelected { get; private set; } = false;
        #endregion

        #region <Custom Properties> : (Behaviour)
        /// <summary> Enables to Auto Select the Control when the Mouse is Over the Control </summary>
        private bool autoSelectEnabled = true;
        [Category("1. Custom Properties"), DisplayName("Auto Select")]
        [Description("Enables to Auto Select the Control when the Mouse is Over the Control")]
        [Bindable(true)] /* Required for Enum Types */
        [Browsable(true)]
        public bool AutoSelectEnabled
        {
            get { return autoSelectEnabled; }
            set { autoSelectEnabled = value; }
        }
        #endregion

        #region <Custom Properties>
        private bool showVScroll = false;
        [Category("1. Custom Properties"), DisplayName("Show V ScrollBar")]
        [Description("Toggles Vertical ScrollBar Visibility")]
        [Browsable(true)]
        public bool ShowVScrollbar
        {
            get { return showVScroll; }
            set
            {
                if (value != showVScroll)
                {
                    showVScroll = value;
                    if (IsHandleCreated)
                        RecreateHandle();
                }
            }
        }

        // Tip: Always Verify that the new Values are Different from the Old Ones (Prevents Waste of Resources and Repeating Code when it is NOT Needed)

        private int markedIndex = -1;
        [Category("1. Custom Properties"), DisplayName("Marked Index")]
        [Description("Gets or Sets the Playlist Marked Item Index")]
        [Browsable(true)]
        public int MarkedIndex
        {
            get { return markedIndex; }
            set
            {
                if (value != markedIndex)
                {
                    markedIndex = value;

                    // Raise the MarkedItemChanged Event
                    OnMarkedItemChanged?.Invoke(markedIndex);

                    Invalidate();
                }
            }
        }

        // Read-Only: Just Return the Marked Item; (To Set: use 'MarkedIndex' Property).
        public object MarkedItem
        {
            get { return Items[markedIndex]; }
        }

        private Color markedItemForeColor = Color.Khaki;
        [Category("1. Custom Properties"), DisplayName("Marked Item ForeColor")]
        [Description("Gets or Sets Marked Item ForeColor")]
        [Browsable(true)]
        public Color MarkedItemForeColor
        {
            get { return markedItemForeColor; }
            set
            {
                if (value != markedItemForeColor)
                {
                    markedItemForeColor = value;
                    Invalidate();
                }
            }
        }

        private Color markedItemBackColor = Color.FromArgb(255, 25, 25, 25);
        [Category("1. Custom Properties"), DisplayName("Marked Item BackColor")]
        [Description("Gets or Sets Marked Item BackColor")]
        [Browsable(true)]
        public Color MarkedItemBackColor
        {
            get { return markedItemBackColor; }
            set
            {
                if (value != markedItemBackColor)
                {
                    markedItemBackColor = value;
                    Invalidate();
                }
            }
        }

        private Color selectionBackColor = Color.DimGray;
        [Category("1. Custom Properties"), DisplayName("Selection BackColor")]
        [Description("Gets or Sets ListBox Selection Rectangle BackColor")]
        [Browsable(true)]
        public Color SelectionBackColor
        {
            get { return selectionBackColor; }
            set
            {
                if (value != selectionBackColor)
                {
                    selectionBackColor = value;
                    Invalidate();
                }
            }
        }

        private Color selectionForeColor = Color.White;
        [Category("1. Custom Properties"), DisplayName("Selection ForeColor")]
        [Description("Gets or Sets ListBox Selection Rectangle ForeColor")]
        [Browsable(true)]
        public Color SelectionForeColor
        {
            get { return selectionForeColor; }
            set
            {
                if (value != selectionForeColor)
                {
                    selectionForeColor = value;
                    Invalidate();
                }
            }
        }
        #endregion

        #region <Custom Events>
        public delegate void MarkedItemChanged(int index);
        public event MarkedItemChanged OnMarkedItemChanged;
        #endregion

        protected override void OnPaint(PaintEventArgs e)
        {
            var rect = ClientRectangle;
            IntPtr hdc = e.Graphics.GetHdc();
            DrawThemeParentBackground(this.Handle, hdc, ref rect);
            e.Graphics.ReleaseHdc(hdc);

            using (var _reg = new Region(e.ClipRectangle))
            {
                if (Items.Count > 0)
                {
                    for (int i = 0; i < Items.Count; i++)
                    {
                        rect = GetItemRectangle(i);

                        if (e.ClipRectangle.IntersectsWith(rect))
                        {
                            if ((SelectionMode == SelectionMode.One && SelectedIndex == i) ||
                                (SelectionMode == SelectionMode.MultiSimple && SelectedIndices.Contains(i)) ||
                                (SelectionMode == SelectionMode.MultiExtended && SelectedIndices.Contains(i)))

                                // > Selected Item ??
                                OnDrawItem(new DrawItemEventArgs(e.Graphics, Font, rect, i, DrawItemState.Selected, ForeColor, BackColor));

                            else
                                // > Unselected Item ??
                                OnDrawItem(new DrawItemEventArgs(e.Graphics, Font, rect, i, DrawItemState.Default, ForeColor, BackColor));

                            _reg.Complement(rect);
                        }
                    }
                }
            }
        }

        #region <Overriden Events> : (Drawing)

        /// <summary> Draw a Item. </summary>
        /// <param name="e"></param>
        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            base.OnDrawItem(e);

            // > No Action when ListBox Contains no Items.
            if (Items.Count == 0) return;

            // > Draw Selected
            if (e.State.HasFlag(DrawItemState.Focus) && e.State.HasFlag(DrawItemState.Selected))
            {
                // -> Draw Selected Item Background
                //using (var brush = new SolidBrush(selectionBackColor))
                //{
                //    e.Graphics.FillRectangle(brush, e.Bounds);
                //}

                // -> Draw Selected Item Text 
                // Set the Color for Selected Item when it is the Same as Marked Index.
                var foreColor = markedIndex == e.Index ? markedItemForeColor : ForeColor;

                // Note: Use TextRenderer to Draw the Text Items (No Anti-Aliasing Required).
                //       Replaces Graphics.DrawString()
                //       This will give a more Natural aspect to the Rendered List of Items. (No Need to use Anti-Aliasing).
                TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), Font, e.Bounds, foreColor, flags);

                // Default
                // Note: Use TextRenderer to Draw the Items (No Anti-Aliasing Required).
                //       Replaces Graphics.DrawString()
                //       This will give a more Natural aspect to the Rendered List of Items. (No Need to use Anti-Aliasing).
                // TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), Font, e.Bounds, selectionForeColor, flags);
            }

            // Draw Unselected
            else
            {
                var color = markedIndex != -1 && markedIndex == e.Index ? markedItemBackColor : BackColor;
                //using (var brush = new SolidBrush(color))
                //{
                //    e.Graphics.FillRectangle(brush, e.Bounds);
                //}

                var foreColor = markedIndex == e.Index ? markedItemForeColor : ForeColor;

                // Note: Use TextRenderer to Draw the Items (No Anti-Aliasing Required).
                //       Replaces Graphics.DrawString()
                //       This will give a more Natural aspect to the Rendered List of Items. (No Need to use Anti-Aliasing).
                TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), Font, e.Bounds, foreColor, flags);
            }

            // > Focus Rectangle for the Selected Item 
            e.DrawFocusRectangle();
        }

        /// <summary> Measure ListBox Selected Item (Index). </summary>
        /// <param name="e"></param>
        protected override void OnMeasureItem(MeasureItemEventArgs e)
        {
            // Remarks:
            // - Set the Height of the Item (The Width: ONLY if Needed).
            // - This is the Standard Value (Modify as Required)
            if (Items.Count > 0) { e.ItemHeight = Font.Height + 4; }

            base.OnMeasureItem(e);
        }

        /// <summary> Selects (Marks) the Selected Item. </summary>
        /// <param name="e"></param>
        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left) { SetMarkedItem(); }

            base.OnMouseDoubleClick(e);
        }
        #endregion

        #region <Overriden Events> : (Key Presses)
        /// <summary> Occurs when a Key is Pressed. </summary>
        /// <param name="e"></param>
        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            base.OnKeyPress(e);

            if (e.KeyChar == (char)Keys.Enter) { SetMarkedItem(); }
        }
        #endregion

        #region <Overriden Events> : (Mouse)

        /* WARNING
         * 
         * - Careful with Vertical Scrolling on: MouseHove, MouseLeave and MouseWheel
         * 
         */

        /// <summary> Occurs when the Mouse is Over the Control. </summary>
        /// <param name="e"></param>
        protected override void OnMouseHover(EventArgs e)
        {
            base.OnMouseHover(e);

            Capture = true;
        }

        /// <summary> Occurs when the Mouse Leaves the Control. </summary>
        /// <param name="e"></param>
        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);

            Capture = false;
        }

        /// <summary> Occurs when the Mouse Scroll Wheel Rotates. </summary>
        /// <param name="e"></param>
        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);

            int v = e.Delta / 120; // Compute a Mouse Wheel Tick

            switch (MouseWheelDetentIsPositive(v))
            {
                case true:
                    if (SelectedIndex >= 0)
                        SelectedIndex--;
                    break;

                case false:
                    if (SelectedIndex < (Items.Count - 1))
                        SelectedIndex++;
                    break;
            }
        }

        /// <summary> Raises the <see cref="Control.MouseMove"></see> event. </summary>
        /// <param name="e">A <see cref="Control.MouseEventArgs"></see> that contains the event data.</param>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            // -> Select the Control and set its Selected State
            SelectControl();
        }
        #endregion


        #region <Methods> : (Behaviour)
        /// <summary> Selects the Control and Sets it as Selected. </summary>
        private void SelectControl()
        {
            // MouseMove += Panel_MouseMove;
            var loc = MousePosition;

            if (ClientRectangle.Contains(loc) && !IsSelected)
            {
                Select();
                IsSelected = true;
                //knbVolume.Focus();
            }

            else { IsSelected = false; }
        }
        #endregion

        #region <Methods> : (Marked Item)
        /// <summary> Used to Hide Vertical ScrollBar. </summary>
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                if (!showVScroll)
                    cp.Style = cp.Style & ~0x200000;
                return cp;
            }
        }

        /// <summary>
        /// WndProc is Overridden in order to Intercept the LB_RESETCONTENT(sent when the ObjectCollection is cleared);<br/> 
        /// and teh LB_DELETESTRING(sent when an Item is removed).
        /// This is done to reset the marked Item when the list is cleared or the marked Item is removed (otherwise an Item will remain marked when it shouldn't).
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                // List cleared
                case LB_RESETCONTENT:
                    markedIndex = -1;
                    break;

                // Item deleted
                case LB_DELETESTRING:
                    if (markedIndex == m.WParam.ToInt32())
                    {
                        markedIndex = -1;
                    }
                    break;

                case WM_KILLFOCUS:
                case WM_HSCROLL:
                case WM_VSCROLL:
                    Invalidate();
                    break;
            }

            base.WndProc(ref m);
        }

        // Toggle the State of a Marked Item, (in case you Double-Click it Twice).
        private void SetMarkedItem() => MarkedIndex = markedIndex == SelectedIndex ? -1 : SelectedIndex;
        #endregion

        #region <Methods> : (Data Validation)
        /// <summary> Determines if the Mouse Wheel Detend (Rotation Tick) Value is Either Positive or Negative. </summary>
        /// <param name="val"></param>
        /// <returns></returns>
        private bool MouseWheelDetentIsPositive(int val)
        {
            return val > 0;
        }
        #endregion
    }
}
vbopmzt1

vbopmzt11#

比我原来的问题干净一点。也许这里和那里有一些小问题(在这一点上:这仍然需要休息);它几乎完美地工作
问题:

  • 有点闪烁(重绘时)
  • 滚动出错
  • 焦点矩形拒绝保持可见
    备注:

当使用“TextRenderer”时,没有闪烁;但课文不太好。
反之;使用“Graphics.DrawString”,文本将被完美绘制。

namespace RGListBox.Controls
{
    internal class RGListBox : ListBox
    {
        #region <Imports>
        [DllImport("uxtheme", ExactSpelling = true)]
        private extern static int DrawThemeParentBackground
            (
            IntPtr hWnd,
            IntPtr hdc,
            ref Rectangle pRect
            );
        #endregion

        #region <Style Configuration>
        private void SetStyles()
        {
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            SetStyle(ControlStyles.Opaque, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.ResizeRedraw, true);
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);

            DrawMode = DrawMode.OwnerDrawVariable;
        }
        #endregion

        #region <Constructor>
        public RGListBox()
        {
            // > Set Styles
            SetStyles();

            // > Set the Item Selection Mode
            SelectionMode = SelectionMode.MultiExtended;

            // > Further Configuration
            IntegralHeight = false;
            BorderStyle = BorderStyle.FixedSingle;
            BackColor = Color.FromArgb(255, 25, 25, 25);
            ForeColor = Color.Gainsboro;
        }
        #endregion

        #region <Fields>
        private const int LB_RESETCONTENT = 0x0184;
        private const int LB_DELETESTRING = 0x0182;
        private const int WM_KILLFOCUS = 0x8;
        private const int WM_VSCROLL = 0x115;
        private const int WM_HSCROLL = 0x114;
        #endregion

        #region <Custom Properties> : (Vertical ScrollBar)
        private bool showVScroll = false;
        [Category("1. Custom Properties"), DisplayName("Show V ScrollBar")]
        [Description("Toggles Vertical ScrollBar Visibility")]
        [Browsable(true)]
        public bool ShowVScrollbar
        {
            get { return showVScroll; }
            set
            {
                if (value != showVScroll)
                {
                    showVScroll = value;
                    if (IsHandleCreated)
                        RecreateHandle();
                }
            }
        }
        #endregion

        #region <Custom Properties> : (Marked Item)
        private int markedIndex = -1;
        [Category("1. Custom Properties"), DisplayName("Marked Index")]
        [Description("Gets or Sets the Playlist Marked Item Index")]
        [Browsable(false)] // Set to False: Jimi Advised
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] // Jimi Advised
        public int MarkedIndex
        {
            get { return markedIndex; }
            set
            {
                if (value != markedIndex)
                {
                    markedIndex = value;

                    // Raise the MarkedItemChanged Event
                    OnMarkedItemChanged?.Invoke(markedIndex);

                    Invalidate();
                }
            }
        }

        // Read-Only: Just Return the Marked Item; (To Set: use 'MarkedIndex' Property).
        public object MarkedItem
        {
            get { return Items[markedIndex]; }
        }

        private Color markedItemForeColor = Color.Khaki;
        [Category("1. Custom Properties"), DisplayName("Marked Item ForeColor")]
        [Description("Gets or Sets Marked Item ForeColor")]
        [Browsable(true)]
        public Color MarkedItemForeColor
        {
            get { return markedItemForeColor; }
            set
            {
                if (value != markedItemForeColor)
                {
                    markedItemForeColor = value;
                    Invalidate();
                }
            }
        }

        private Color markedItemBackColor = Color.FromArgb(255, 25, 25, 25);
        [Category("1. Custom Properties"), DisplayName("Marked Item BackColor")]
        [Description("Gets or Sets Marked Item BackColor")]
        [Browsable(true)]
        public Color MarkedItemBackColor
        {
            get { return markedItemBackColor; }
            set
            {
                if (value != markedItemBackColor)
                {
                    markedItemBackColor = value;
                    Invalidate();
                }
            }
        }
        #endregion

        #region <Custom Properties> : (Selected Item)
        private Color selectionBackColor = Color.DimGray;
        [Category("1. Custom Properties"), DisplayName("Selection BackColor")]
        [Description("Gets or Sets ListBox Selection Rectangle BackColor")]
        [Browsable(true)]
        public Color SelectionBackColor
        {
            get { return selectionBackColor; }
            set
            {
                if (value != selectionBackColor)
                {
                    selectionBackColor = value;
                    Invalidate();
                }
            }
        }

        private Color selectionForeColor = Color.White;
        [Category("1. Custom Properties"), DisplayName("Selection ForeColor")]
        [Description("Gets or Sets ListBox Selection Rectangle ForeColor")]
        [Browsable(true)]
        public Color SelectionForeColor
        {
            get { return selectionForeColor; }
            set
            {
                if (value != selectionForeColor)
                {
                    selectionForeColor = value;
                    Invalidate();
                }
            }
        }
        #endregion

        #region <Custom Events>
        public delegate void MarkedItemChanged(int index);
        public event MarkedItemChanged OnMarkedItemChanged;
        #endregion

        #region <Overriden Events> : (Drawing)
        protected override void OnPaint(PaintEventArgs e)
        {
            var rect = ClientRectangle;
            IntPtr hdc = e.Graphics.GetHdc();
            DrawThemeParentBackground(this.Handle, hdc, ref rect);
            e.Graphics.ReleaseHdc(hdc);

            using (var _reg = new Region(e.ClipRectangle))
            {
                if (Items.Count > 0)
                {
                    for (int i = 0; i < Items.Count; i++)
                    {
                        rect = GetItemRectangle(i);

                        if (e.ClipRectangle.IntersectsWith(rect))
                        {
                            if ((SelectionMode == SelectionMode.One && SelectedIndex == i) ||
                                (SelectionMode == SelectionMode.MultiSimple && SelectedIndices.Contains(i)) ||
                                (SelectionMode == SelectionMode.MultiExtended && SelectedIndices.Contains(i)))

                                OnDrawItem(new DrawItemEventArgs(e.Graphics, Font, rect, i, DrawItemState.Selected, ForeColor, BackColor));

                            else
                                OnDrawItem(new DrawItemEventArgs(e.Graphics, Font, rect, i, DrawItemState.Default, ForeColor, BackColor));

                            _reg.Complement(rect);
                        }
                    }
                }
            }
        }

        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            if (e.Index < 0) return;

            var rec = e.Bounds;
            var g = e.Graphics;

            // > Retrieves the Item Fore Color (Color for Default Text or Marked Item) and Assigns it to this Variable
            var foreColor = markedIndex == e.Index ? markedItemForeColor : ForeColor;
            
            if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
                using (var selectionBackColor = new SolidBrush(SelectionBackColor))
                    g.FillRectangle(selectionBackColor, rec);

            e.DrawFocusRectangle();

            using (var textColor = new SolidBrush(foreColor))
            using (var strFormat = new StringFormat(StringFormat.GenericTypographic))
            {
                strFormat.Alignment = StringAlignment.Near;
                strFormat.LineAlignment = StringAlignment.Center;
                
                g.DrawString(GetItemText(Items[e.Index]), Font, textColor, e.Bounds, strFormat); // Looks Better (But Flickers)
            }
        }



        /// <summary> Measure ListBox Selected Item (Index). </summary>
        /// <param name="e"></param>
        protected override void OnMeasureItem(MeasureItemEventArgs e)
        {
            // Remarks:
            // - Set the Height of the Item (The Width: ONLY if Needed).
            // - This is the Standard Value (Modify as Required)
            if (Items.Count > 0) { e.ItemHeight = Font.Height + 4; }

            base.OnMeasureItem(e);
        }
        #endregion

        #region <Overriden Events> : (ListBox Collection)
        protected override void OnSelectedIndexChanged(EventArgs e)
        {
            base.OnSelectedIndexChanged(e);

            Invalidate();
        }
        #endregion

        #region <Overriden Events> : (Mouse)

        /* WARNING
         * 
         * - Be Cautious Messing with Vertical Scrolling : (MouseHove, MouseLeave and MouseWheel)
         *   Messing can cause Issues.
         * 
         */

        /// <summary> Marks the Selected Item. </summary>
        /// <param name="e"></param>
        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left) { SetMarkedItem(); }

            base.OnMouseDoubleClick(e);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            Invalidate();
        }

        /// <summary> Occurs when the Mouse Scroll Wheel Rotates. </summary>
        /// <param name="e"></param>
        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);

            int v = e.Delta / 120; // Computes a Mouse Wheel Tick

            switch (MouseWheelDetentIsPositive(v))
            {
                case true:
                    if (SelectedIndex >= 0)
                        SelectedIndex--;
                    break;

                case false:
                    if (SelectedIndex < (Items.Count - 1))
                        SelectedIndex++;
                    break;
            }
        }

        #endregion

        #region <Overriden Events> : (Keyboard)
        /// <summary> Occurs when a Key is Pressed. </summary>
        /// <param name="e"></param>
        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            base.OnKeyPress(e);

            if (e.KeyChar == (char)Keys.Enter) { SetMarkedItem(); }
        }
        #endregion

        #region <Methods> : (Marked Item)
        /// <summary> Used to Hide Vertical ScrollBar. </summary>
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;

                if (!showVScroll)
                    cp.Style = cp.Style & ~0x200000;

                cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT

                return cp;
            }
        }

        /// <summary>
        /// WndProc is Overridden in order to Intercept the LB_RESETCONTENT(sent when the ObjectCollection is cleared);<br/> 
        /// and teh LB_DELETESTRING(sent when an Item is removed).
        /// This is done to reset the marked Item when the list is cleared or the marked Item is removed (otherwise an Item will remain marked when it shouldn't).
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                // List cleared
                case LB_RESETCONTENT:
                    markedIndex = -1;
                    break;

                // Item deleted
                case LB_DELETESTRING:
                    if (markedIndex == m.WParam.ToInt32())
                    {
                        markedIndex = -1;
                    }
                    break;

                case WM_KILLFOCUS:
                case WM_HSCROLL:
                case WM_VSCROLL:
                    Invalidate();
                    break;
            }

            base.WndProc(ref m);
        }

        // Toggle the State of a Marked Item, (in case you Double-Click it Twice).
        private void SetMarkedItem() => MarkedIndex = markedIndex == SelectedIndex ? -1 : SelectedIndex;
        #endregion

        #region <Methods> : (Data Validation)
        /// <summary> Determines if the Mouse Wheel Detend (Rotation Tick) Value is Either Positive or Negative. </summary>
        /// <param name="val"></param>
        /// <returns></returns>
        private bool MouseWheelDetentIsPositive(int val)
        {
            return val > 0;
        }
        #endregion
    }
}

相关问题