Click here to Skip to main content
15,895,799 members
Articles / Programming Languages / C#

DataGridViewKRBColumnSelector

Rate me:
Please Sign up or sign in to vote.
4.88/5 (11 votes)
14 Feb 2010CPOL4 min read 31.5K   1.7K   41  
DataGridView Column Selector Sample
using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Collections.Generic;

namespace CustomDataGridViewKRBColumnSelector
{
    internal class ColumnSelector : Control
    {
        #region Event - Delegate

        /// <summary>
        /// Represents an event which occurs when the menu item checked changes.
        /// </summary>
        /// <param name="sender">Event sender class</param>
        /// <param name="ea">Menu item information args</param>
        internal delegate void MenuItemEventHandler(object sender, MenuItemEventArgs ea);
        
        /// <summary>
        /// Occurs when the selector menu is closed by the user.
        /// </summary>
        internal event EventHandler Closed;
        
        /// <summary>
        /// Occurs when the menu item checked state changed.
        /// </summary>
        internal event MenuItemEventHandler MenuItemChanged;

        #endregion

        #region Symbolic Constants

        private const int MAXCOLUMNS = 10;

        #endregion

        #region Instance Members

        private int hotIndex = -2;
        private int barGradientWidth = 0;
        private Bitmap baseBitmap = null;
        private Graphics graphOne = null;
        private Rectangle exitRectangle = Rectangle.Empty;
        private List<MenuItem> items = new List<MenuItem>();
        private DataGridViewKRBColumnSelector selector = null;

        #endregion

        #region Constructor

        public ColumnSelector()
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint |
                ControlStyles.UserMouse | ControlStyles.FixedWidth | ControlStyles.FixedHeight, true);
        }

        public ColumnSelector(DataGridViewKRBColumnSelector selector)
            : this()
        {
            this.selector = selector;
            this.Font = this.selector.Font;
            DataGridView dataGridView = this.selector.GridView;

            if (dataGridView == null)
                throw new ArgumentNullException("DataGridView cannot be null.");
            else
            {
                for (int i = 0; i < dataGridView.Columns.Count && i <= MAXCOLUMNS - 1; i++)
                {
                    DataGridViewColumn currentColumn = dataGridView.Columns[i];
                    items.Add(new MenuItem(currentColumn.HeaderText, currentColumn.Visible));
                }

                CreatesRectangles();
            }
        }

        public ColumnSelector(DataGridViewKRBColumnSelector selector, int maxColumns)
            : this()
        {
            this.selector = selector;
            this.Font = this.selector.Font;
            DataGridView dataGridView = this.selector.GridView;

            if (dataGridView == null)
                throw new ArgumentNullException("DataGridView cannot be null.");
            else
            {
                for (int i = 0; i < dataGridView.Columns.Count && i <= maxColumns - 1; i++)
                {
                    DataGridViewColumn currentColumn = dataGridView.Columns[i];
                    items.Add(new MenuItem(currentColumn.HeaderText, currentColumn.Visible));
                }

                CreatesRectangles();
            }
        }

        #endregion

        #region Helper Methods

        private int HitTest(int x, int y)
        {
            // Nothing.
            int result = -2;

            foreach (MenuItem item in items)
            {
                if (item.ItemRectangle.Contains(x, y))
                {
                    result = items.IndexOf(item);
                    break;
                }
            }

            return result;
        }
        
        /// <summary>
        /// Trigger a menu closed event.
        /// </summary>
        private void OnClosed()
        {
            if (Closed != null)
                Closed(this, EventArgs.Empty);
        }

        private void CreatesRectangles()
        {
            Graphics g = this.CreateGraphics();

            SizeF textMaxSize = new SizeF();
            foreach (MenuItem item in items)
            {
                SizeF currentSize = g.MeasureString(item.ItemText, this.Font, Int32.MaxValue, StringFormat.GenericTypographic);
                textMaxSize = textMaxSize.Width >= currentSize.Width ? textMaxSize : currentSize;
            }

            barGradientWidth = 10 + (int)textMaxSize.Height;

            this.Size = new Size()
            {
                Width = 22 + barGradientWidth + (int)textMaxSize.Width,
                Height = 7 + ((items.Count + 1) * ((int)textMaxSize.Height + 6)) + (items.Count - 1)
            };

            Rectangle clientRectangle = this.ClientRectangle;
            clientRectangle.Offset(2, 2);
            clientRectangle.Width -= 4;
            clientRectangle.Height = (int)textMaxSize.Height + 6;

            for (int i = 0; i < items.Count; i++)
            {
                MenuItem item = items[i];

                if (i == 0)
                {
                    item.ItemRectangle = clientRectangle;
                }
                else
                {
                    // Object Initializer
                    item.ItemRectangle = new Rectangle()
                    {
                        X = clientRectangle.X,
                        Y = items[i - 1].ItemRectangle.Y + clientRectangle.Height + 1,
                        Width = clientRectangle.Width,
                        Height = clientRectangle.Height
                    };

                    if (i == items.Count - 1)
                    {
                        exitRectangle = new Rectangle()
                        {
                            X = clientRectangle.X,
                            Y = item.ItemRectangle.Y + clientRectangle.Height + 2,
                            Width = clientRectangle.Width,
                            Height = clientRectangle.Height
                        };
                    }
                }
            }
        }

        /// <summary>
        /// Trigger a menu item changed event.
        /// </summary>
        /// <param name="sender">Event sender class</param>
        /// <param name="ea">Menu item information args</param>
        private void OnMenuItemChanged(object sender, MenuItemEventArgs ea)
        {
            if (MenuItemChanged != null)
                MenuItemChanged(sender, ea);
        }

        #endregion

        #region Override Methods

        protected override void OnPaint(PaintEventArgs pe)
        {
            if (baseBitmap == null)
            {
                baseBitmap = new Bitmap(this.Width, this.Height);
                graphOne = Graphics.FromImage(baseBitmap);

                DrawBarGradient(graphOne);
                DrawBackground(graphOne);
                DrawBorder(graphOne);
            }

            using (Bitmap overlay = new Bitmap(baseBitmap))
            using (Graphics gr = Graphics.FromImage(overlay))
            {
                DrawMenuItems(gr);
                pe.Graphics.DrawImage(overlay, this.ClientRectangle, 0, 0, overlay.Width, overlay.Height, GraphicsUnit.Pixel);
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (exitRectangle.Contains(e.Location))
            {
                if (hotIndex != -1)
                {
                    hotIndex = -1;

                    // RePaint column selector.
                    this.Invalidate();
                }
            }
            else
            {
                int index = HitTest(e.X, e.Y);
                if (index != hotIndex)
                {
                    hotIndex = index;

                    // RePaint column selector.
                    this.Invalidate();
                }
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                switch (hotIndex)
                {
                    case -2:
                        break;
                    case -1:
                        OnClosed();  // Trigger a menu closed event.
                        break;
                    default:
                        // Trigger a menu item changed event.
                        OnMenuItemChanged(this, new MenuItemEventArgs(hotIndex));

                        // Update menu item.
                        MenuItem item = items[hotIndex];
                        item.IsChecked = !item.IsChecked;

                        // RePaint column selector.
                        this.Invalidate();
                        break;
                }
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (graphOne != null)
                    graphOne.Dispose();

                if (baseBitmap != null)
                    baseBitmap.Dispose();

                foreach (MenuItem item in items)
                    item.Dispose();
            }

            base.Dispose(disposing);
        }
        
        #endregion

        #region Virtual Methods

        protected virtual void DrawBarGradient(Graphics g)
        {
            Rectangle barGradientRct = this.ClientRectangle;
            barGradientRct.Inflate(-1, -1);
            barGradientRct.Width = barGradientWidth;

            CustomDataGridViewKRBColumnSelector.DataGridViewKRBColumnSelector.GradientBar gradient = this.selector.BarGradient;
            using (LinearGradientBrush brush = new LinearGradientBrush(barGradientRct, gradient.ColorStart, gradient.ColorEnd, gradient.GradientStyle))
            {
                g.FillRectangle(brush, barGradientRct);
            }
        }

        protected virtual void DrawBackground(Graphics g)
        {
            using (SolidBrush brush = new SolidBrush(this.selector.BackColor))
            {
                g.FillRectangle(brush, barGradientWidth + 1, 1, (this.Width - 1) - (barGradientWidth + 1), this.Height - 2);
            }
        }

        protected virtual void DrawBorder(Graphics g)
        {
            using (Pen pen = new Pen(this.selector.BorderColor))
            {
                g.DrawRectangle(pen, 0, 0, this.Width - 1, this.Height - 1);
            }
        }

        protected virtual void DrawMenuItems(Graphics g)
        {
            // The text doesn't wrap but instead disappears under the right edge of the menu item rectangle.
            StringFormat sf = new StringFormat(StringFormatFlags.NoWrap);
            sf.Alignment = this.selector.Alignment;     // Horizontal Alignment
            sf.LineAlignment = StringAlignment.Center;  // Vertical Alignment

            for (int i = 0; i < items.Count; i++)
            {
                MenuItem currentItem = items[i];
                Rectangle rct = currentItem.ItemRectangle;

                // Object Initializer
                Rectangle textRct = new Rectangle()
                {
                    X = barGradientWidth + 5,
                    Y = rct.Y,
                    Width = this.Width - (barGradientWidth + 4),
                    Height = rct.Height
                };

                if (i == hotIndex)
                {
                    using (Pen pen = new Pen(this.selector.MenuItem.ItemBorderColor))
                    {
                        g.DrawRectangle(pen, rct.X, rct.Y, rct.Width - 1, rct.Height - 1);
                    }

                    rct.Inflate(-1, -1);
                    using (LinearGradientBrush brush = new LinearGradientBrush(rct, this.selector.MenuItem.ColorStart,
                        this.selector.MenuItem.ColorEnd, this.selector.MenuItem.GradientStyle))
                    {
                        g.FillRectangle(brush, rct);
                    }

                    // Draw Checkbox border and gradient area if current menu item is checked.
                    if (currentItem.IsChecked)
                    {
                        rct.Inflate(-1, -1);
                        rct.Width = barGradientWidth - 8;
                        using (Pen pen = new Pen(this.selector.HoverAreaCheckBox.BorderColor))
                        {
                            g.DrawRectangle(pen, rct.X, rct.Y, rct.Width - 1, rct.Height - 1);
                        }

                        rct.Inflate(-1, -1);
                        using (LinearGradientBrush brush = new LinearGradientBrush(rct, this.selector.HoverAreaCheckBox.ColorStart,
                            this.selector.HoverAreaCheckBox.ColorEnd, this.selector.HoverAreaCheckBox.GradientStyle))
                        {
                            g.FillRectangle(brush, rct);
                            rct.Inflate(1, 1);
                            rct.X += 1;
                            ControlPaint.DrawMenuGlyph(g, rct, MenuGlyph.Checkmark, this.selector.HoverAreaCheckBox.TickColor, Color.Transparent);
                        }
                    }

                    // Draw MenuItem Text.
                    using (SolidBrush brush = new SolidBrush(this.selector.MenuItem.ItemHoverForeColor))
                    {
                        g.DrawString(currentItem.ItemText, this.Font, brush, textRct, sf);
                    }
                }
                else
                {
                    // Draw Checkbox border and gradient area if current menu item is checked.
                    if (currentItem.IsChecked)
                    {
                        rct.Inflate(-2, -2);
                        rct.Width = barGradientWidth - 8;
                        using (Pen pen = new Pen(this.selector.AreaCheckBox.BorderColor))
                        {
                            g.DrawRectangle(pen, rct.X, rct.Y, rct.Width - 1, rct.Height - 1);
                        }

                        rct.Inflate(-1, -1);
                        using (LinearGradientBrush brush = new LinearGradientBrush(rct, this.selector.AreaCheckBox.ColorStart,
                            this.selector.AreaCheckBox.ColorEnd, this.selector.AreaCheckBox.GradientStyle))
                        {
                            g.FillRectangle(brush, rct);
                            rct.Inflate(1, 1);
                            rct.X += 1;
                            ControlPaint.DrawMenuGlyph(g, rct, MenuGlyph.Checkmark, this.selector.AreaCheckBox.TickColor, Color.Transparent);
                        }
                    }

                    // Draw MenuItem Text.
                    using (SolidBrush brush = new SolidBrush(this.selector.MenuItem.ItemForeColor))
                    {
                        g.DrawString(currentItem.ItemText, this.Font, brush, textRct, sf);
                    }
                }
            }

            // Draw exit button border, fill and it's string.
            CustomDataGridViewKRBColumnSelector.DataGridViewKRBColumnSelector.ButtonExit exitButton = this.selector.ExitButton;
            Rectangle exitRct = exitRectangle;
            ControlPaint.DrawBorder3D(g, exitRct, exitButton.ButtonBorderStyle, Border3DSide.All);
            Size size3d = SystemInformation.Border3DSize;
            exitRct.Inflate(-size3d.Width, -size3d.Height);
            using (SolidBrush brush = new SolidBrush(hotIndex == -1 ? exitButton.HoverBackColor : exitButton.BackColor))
            {
                g.FillRectangle(brush, exitRct);
                brush.Color = hotIndex == -1 ? exitButton.HoverForeColor : exitButton.ForeColor;
                g.DrawString("Exit", this.Font, brush, exitRct, sf);
            }
        }

        #endregion

        #region MenuItemClass

        private class MenuItem : IDisposable
        {
            #region Instance Members

            private bool isChecked = false;                     // Initializer
            private string itemText = null;                     // Initializer
            private Rectangle itemRectangle = Rectangle.Empty;  // Initializer

            #endregion

            #region Constructor

            public MenuItem(string itemText)
            {
                this.itemText = itemText;
            }

            public MenuItem(string itemText, bool isChecked)
            {
                this.itemText = itemText;
                this.isChecked = isChecked;
            }

            #endregion

            #region Property

            /// <summary>
            /// Determines whether the menu item(or column visibility) is checked or not.
            /// </summary>
            public bool IsChecked
            {
                get { return isChecked; }
                set
                {
                    if (!value.Equals(isChecked))
                        isChecked = value;
                }
            }

            /// <summary>
            /// Displays the current datagridview column name.
            /// </summary>
            public string ItemText
            {
                get { return itemText; }
                set
                {
                    if (!value.Equals(itemText))
                        itemText = value;
                }
            }

            /// <summary>
            /// Gets or Sets the current menu item rectangle.
            /// </summary>
            public Rectangle ItemRectangle
            {
                get { return itemRectangle; }
                set
                {
                    if (!value.Equals(itemRectangle))
                        itemRectangle = value;
                }
            }

            #endregion

            #region IDisposable Members

            /// <summary>
            /// Dispose this class.
            /// </summary>
            public void Dispose()
            {
                GC.SuppressFinalize(this);
            }

            #endregion
        }

        #endregion
    }

    internal class MenuItemEventArgs : EventArgs
    {
        #region Instance Members

        private int columnIndex = -1;

        #endregion

        #region Constructor

        public MenuItemEventArgs(int columnIndex)
        {
            this.columnIndex = columnIndex;
        }

        #endregion

        #region Property

        public int ColumnIndex
        {
            get { return columnIndex; }
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) ARELTEK
Turkey Turkey
Since 1998...

MCPD - Enterprise Application Developer

“Hesaplı hareket ettiğini zanneden ve onunla iftihar eyliyen dar kafalar; kurtulmağa, yükselmeğe elverişli hiç bir eser vücüda getirmezler. Kurtuluş ve yükselişi, ancak varlığına dayanan ve mülkü milletin gizli kapalı hazinelerini verimli hale getirmesini bilen, şahsi menfaatini millet menfaati uğruna feda eden, ruhu idealist, dimağı realist şahsiyetlerde aramalıdır.”

Nuri Demirağ, 1947

Comments and Discussions