Click here to Skip to main content
15,891,375 members
Articles / Programming Languages / C#

A DataGridView Column Show/Hide Popup - Menu Style

Rate me:
Please Sign up or sign in to vote.
4.65/5 (10 votes)
1 Sep 2009CPOL 71.6K   3.9K   59   16
A DataGridView column show/hide popup - Menu style.

Image 1

Introduction

I needed to allow my customers to be able to show/hide columns in a DataGridView.

Original source

DGVColumnSelector.aspx.

Background

There is a great article mentioned above (Thank you Vincenzo Rossi). It does exactly what I needed to do. (Please see the original article on how to use the ToolStripDropDown and ToolStripControlHost classes). The only thing I did not like - the usage of the check boxes - it seems to be old-fashioned... I replaced that with a menu-like control.

Using the code

Please refer to the original code if you have any questions regarding using the code. My additions are the UserControlMenu and MenuControl objects. Instead of using the original CheckedListBox, you will use UserControlMenu. You will need to define two events: OnDone and CheckedChangedEnent:

C#
UserControlMenu pUserControl1 = new UserControlMenu();

public DataGridViewColumnSelector() {
    //mCheckedListBox = new CheckedListBox();
    //mCheckedListBox.CheckOnClick = true;
    //mCheckedListBox.ItemCheck += 
    //    new ItemCheckEventHandler(mCheckedListBox_ItemCheck);

    //ToolStripControlHost mControlHost = new ToolStripControlHost(mCheckedListBox);
    pUserControl1.DoneEvent += new EventHandler(OnDone);
    pUserControl1.CheckedChangedEnent += 
              new UserControlMenu.CheckedChanged(CheckedChangedEnent);
    ToolStripControlHost mControlHost = new ToolStripControlHost(pUserControl1);
...
}

void CheckedChangedEnent(int iIndex, bool bChecked)
{
    mDataGridView.Columns[iIndex].Visible = bChecked;
}

private void OnDone(object sender, EventArgs e)
{
    mPopup.AutoClose = false;
    mPopup.Close();
    mPopup.AutoClose = true;
}

On CellMouseClick, you will need to use a new object one more time:

C#
void mDataGridView_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
    if (e.Button == MouseButtons.Right && e.RowIndex==-1 && e.ColumnIndex==-1) {
        //mCheckedListBox.Items.Clear();
        //foreach (DataGridViewColumn c in mDataGridView.Columns){
        //    mCheckedListBox.Items.Add(c.HeaderText, c.Visible);
        //}
        //int PreferredHeight = (mCheckedListBox.Items.Count * 16) + 7;
        //mCheckedListBox.Height =
        //      (PreferredHeight < MaxHeight) ? PreferredHeight : MaxHeight;
        //mCheckedListBox.Width = this.Width;
        pUserControl1.Initialize(mDataGridView);
        mPopup.Show(mDataGridView.PointToScreen(new Point (e.X,e.Y)));
    }
}

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)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralLove This!!! Pin
aaroncampf10-Feb-11 5:59
aaroncampf10-Feb-11 5:59 
Generalchecking the checkbox Pin
Mphirana11-Aug-10 23:34
Mphirana11-Aug-10 23:34 
GeneralRe: checking the checkbox Pin
Fiwel12-Aug-10 4:24
Fiwel12-Aug-10 4:24 
GeneralRe: checking the checkbox Pin
ammarmujeeb10-Oct-10 10:01
ammarmujeeb10-Oct-10 10:01 
Newsif you want using other ContextMenuStrip. I mean using two popup menu. Pin
DVasya8-Aug-10 21:11
DVasya8-Aug-10 21:11 
GeneralNew menu option Pin
pimpers9-Dec-09 16:01
pimpers9-Dec-09 16:01 
GeneralRe: New menu option Pin
Fiwel10-Dec-09 5:00
Fiwel10-Dec-09 5:00 
GeneralRe: New menu option Pin
pimpers10-Dec-09 13:06
pimpers10-Dec-09 13:06 
GeneralRe: New menu option Pin
pimpers15-Dec-09 11:59
pimpers15-Dec-09 11:59 
QuestionVB version too ??? Pin
NQ19709-Sep-09 21:50
NQ19709-Sep-09 21:50 
AnswerRe: VB version too ??? Pin
Fiwel10-Sep-09 5:39
Fiwel10-Sep-09 5:39 
I done some VB but converting is not something I enjoy doing Smile | :) .
There are bunch of "C# to VB" tools on-line, like the one I rendomly picked:
http://www.developerfusion.com/tools/convert/csharp-to-vb

it producesed the following result. Try it...

Imports System
Imports System.Collections
Imports System.Text
Imports System.Drawing.Imaging
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Drawing.Drawing2D
Imports System.Drawing.Text

Namespace DGVColumnSelector
Class MenuControl
Private Shared m_iImageColumnWidth As Integer = 24
Private Shared m_iExtraWidth As Integer = 15
Private Class MenuCommand
Public ReadOnly Property Height() As Integer
Get
Return If(Separator, 5, 21)
End Get
End Property
Public ReadOnly Property Separator() As Boolean
Get
Return m_csText = "-"
End Get
End Property

Private m_csText As String
'private int m_iIndex;
Private m_bChecked As Boolean
Private m_bDone As Boolean
Public ReadOnly Property Text() As String
Get
Return m_csText
End Get
End Property
'public int Index { get { return m_iIndex; } }
Public ReadOnly Property Done() As Boolean
Get
Return m_bDone
End Get
End Property
Public Property Checked() As Boolean
Get
Return m_bChecked
End Get
Set(ByVal value As Boolean)
m_bChecked = value
End Set
End Property
'public MenuCommand(string csText, int iIndex, bool bChecked)
Public Sub New(ByVal csText As String, ByVal bChecked As Boolean)
Me.New(csText, bChecked, False)
End Sub
Public Sub New(ByVal csText As String)
Me.New(csText, False, False)
End Sub
Public Sub New(ByVal csText As String, ByVal bChecked As Boolean, ByVal bDone As Boolean)
m_csText = csText
'm_iIndex = iIndex;
m_bChecked = bChecked
m_bDone = bDone
End Sub
End Class

Private m_pTracMenuItem As MenuCommand = Nothing
Private m_pMenuCommands As New ArrayList()

Private m_pMemBitmap As Bitmap
' = new Bitmap(panel1.Width, panel1.Height, PixelFormat.Format32bppArgb);
Private m_pMemGraphics As Graphics

Public ReadOnly Property Width() As Integer
Get
Return m_pMemBitmap.Width
End Get
End Property
Public ReadOnly Property Height() As Integer
Get
Return m_pMemBitmap.Height
End Get
End Property

Public ReadOnly Property Done() As Boolean
Get
Return m_pTracMenuItem IsNot Nothing AndAlso m_pTracMenuItem.Done
End Get
End Property
Public ReadOnly Property HitIndex() As Integer
Get
Return m_pMenuCommands.IndexOf(m_pTracMenuItem)
End Get
End Property

Public Function ChangeChecked(ByVal iIndex As Integer, ByVal g As Graphics) As Boolean
Dim pMenuCommand As MenuCommand = TryCast(m_pMenuCommands(iIndex), MenuCommand)
pMenuCommand.Checked = Not pMenuCommand.Checked
Draw(g)
Return pMenuCommand.Checked
End Function

Public Sub Add(ByVal csText As String, ByVal bChecked As Boolean)
m_pMenuCommands.Add(New MenuCommand(csText, bChecked))
End Sub

Public Sub Prepare(ByVal g As Graphics)
m_pMenuCommands.Add(New MenuCommand("-"))
Dim pDone As New MenuCommand("Done", False, True)
m_pMenuCommands.Add(pDone)

Dim iHeight As Integer = 4
'(2 + 2 top + bottom);
Dim fWidth As Single = 0
For Each pMenuCommand As MenuCommand In m_pMenuCommands
iHeight += pMenuCommand.Height
Dim pSizeF As SizeF = g.MeasureString(pMenuCommand.Text, SystemInformation.MenuFont)
fWidth = Math.Max(fWidth, pSizeF.Width)
Next
Dim iWidth As Integer = CInt(fWidth) + m_iImageColumnWidth + m_iExtraWidth

m_pMemBitmap = New Bitmap(iWidth, iHeight)
m_pMemGraphics = Graphics.FromImage(m_pMemBitmap)
End Sub

Private Function HitTest(ByVal X As Integer, ByVal Y As Integer) As MenuCommand
If X < 0 OrElse X > Width OrElse Y < 0 OrElse Y > Height Then
Return Nothing
End If

Dim iHeight As Integer = 2
For Each pMenuCommand As MenuCommand In m_pMenuCommands
If Y > iHeight AndAlso Y < iHeight + pMenuCommand.Height Then
Return If(pMenuCommand.Separator, Nothing, pMenuCommand)
End If
iHeight += pMenuCommand.Height
Next
Return Nothing
End Function

Public Function HitTestMouseMove(ByVal X As Integer, ByVal Y As Integer) As Boolean
Dim pMenuCommand As MenuCommand = HitTest(X, Y)
If pMenuCommand <> m_pTracMenuItem Then
m_pTracMenuItem = pMenuCommand
Return True
Else
Return False
End If
End Function
Public Function HitTestMouseDown(ByVal X As Integer, ByVal Y As Integer) As Boolean
Dim pMenuCommand As MenuCommand = HitTest(X, Y)
Return pMenuCommand IsNot Nothing
End Function

Public Sub Draw(ByVal g As Graphics)
Dim area As New Rectangle(0, 0, m_pMemBitmap.Width, m_pMemBitmap.Height)

m_pMemGraphics.Clear(SystemColors.Control)

' Draw the background area
DrawBackground(m_pMemGraphics, area)

' Draw the actual menu items
DrawAllCommands(m_pMemGraphics)

g.DrawImage(m_pMemBitmap, area, area, GraphicsUnit.Pixel)
End Sub

Private Sub DrawBackground(ByVal g As Graphics, ByVal rectWin As Rectangle)
Dim main As New Rectangle(0, 0, rectWin.Width, rectWin.Height)


Dim xStart As Integer = 1
Dim yStart As Integer = 2
Dim yHeight As Integer = main.Height - yStart - 1

' Paint the main area background
Using backBrush As Brush = New SolidBrush(Color.FromArgb(249, 248, 247))
g.FillRectangle(backBrush, main)
End Using

' Draw single line border around the main area
Using mainBorder As New Pen(Color.FromArgb(102, 102, 102))
g.DrawRectangle(mainBorder, main)
End Using

Dim imageRect As New Rectangle(xStart, yStart, m_iImageColumnWidth, yHeight)

' Draw the first image column
Using openBrush As Brush = New LinearGradientBrush(imageRect, Color.FromArgb(248, 247, 246), Color.FromArgb(215, 211, 204), 0F)
g.FillRectangle(openBrush, imageRect)
End Using
End Sub

Private Sub DrawAllCommands(ByVal g As Graphics)
Dim iTop As Integer = 2
For Each pMenuCommand As MenuCommand In m_pMenuCommands
DrawSingleCommand(g, iTop, pMenuCommand, pMenuCommand = m_pTracMenuItem)
Next
End Sub

Private Sub DrawSingleCommand(ByVal g As Graphics, ByRef iTop As Integer, ByVal pMenuCommand As MenuCommand, ByVal hotCommand As Boolean)
Dim iHeight As Integer = pMenuCommand.Height
Dim drawRect As New Rectangle(1, iTop, Width, iHeight)
iTop += iHeight

' Remember some often used values
Dim textGapLeft As Integer = 4
Dim imageLeft As Integer = 4

' Calculate some common values
Dim imageColWidth As Integer = 24

' Is this item a separator?
If pMenuCommand.Separator Then
' Draw the image column background
Dim imageCol As New Rectangle(drawRect.Left, drawRect.Top, imageColWidth, drawRect.Height)

' Draw the image column
Using openBrush As Brush = New LinearGradientBrush(imageCol, Color.FromArgb(248, 247, 246), Color.FromArgb(215, 211, 204), 0F)
g.FillRectangle(openBrush, imageCol)
End Using

' Draw a separator
Using separatorPen As New Pen(Color.FromArgb(166, 166, 166))
' Draw the separator as a single line
g.DrawLine(separatorPen, drawRect.Left + imageColWidth + textGapLeft, drawRect.Top + 2, drawRect.Right - 7, drawRect.Top + 2)
End Using
Else
Dim leftPos As Integer = drawRect.Left + imageColWidth + textGapLeft

' Should the command be drawn selected?
If hotCommand Then
Dim selectArea As New Rectangle(drawRect.Left + 1, drawRect.Top, drawRect.Width - 9, drawRect.Height - 1)

Using selectBrush As New SolidBrush(Color.FromArgb(182, 189, 210))
g.FillRectangle(selectBrush, selectArea)
End Using

Using selectPen As New Pen(Color.FromArgb(10, 36, 106))
g.DrawRectangle(selectPen, selectArea)
End Using
Else
Dim imageCol As New Rectangle(drawRect.Left, drawRect.Top, imageColWidth, drawRect.Height)

' Paint the main background color
Using backBrush As Brush = New SolidBrush(Color.FromArgb(249, 248, 247))
g.FillRectangle(backBrush, New Rectangle(drawRect.Left + 1, drawRect.Top, drawRect.Width - 9, drawRect.Height))
End Using

Using openBrush As Brush = New LinearGradientBrush(imageCol, Color.FromArgb(248, 247, 246), Color.FromArgb(215, 211, 204), 0F)
g.FillRectangle(openBrush, imageCol)
End Using
End If

' Calculate text drawing rectangle
Dim strRect As New Rectangle(leftPos, drawRect.Top, Width - imageColWidth - textGapLeft - 5, drawRect.Height)

' Left align the text drawing on a single line centered vertically
' and process the & character to be shown as an underscore on next character
Dim format As New StringFormat()
format.FormatFlags = StringFormatFlags.NoClip Or StringFormatFlags.NoWrap
format.Alignment = StringAlignment.Near
format.LineAlignment = StringAlignment.Center
format.HotkeyPrefix = HotkeyPrefix.Show

Dim textBrush As New SolidBrush(SystemColors.MenuText)
g.DrawString(pMenuCommand.Text, SystemInformation.MenuFont, textBrush, strRect, format)

' The image offset from top of cell is half the space left after
' subtracting the height of the image from the cell height
Dim imageTop As Integer = drawRect.Top + (drawRect.Height - 16) / 2

' Should a check mark be drawn?
If pMenuCommand.Checked Then
Dim boxPen As New Pen(Color.FromArgb(10, 36, 106))
Dim boxBrush As Brush

If hotCommand Then
boxBrush = New SolidBrush(Color.FromArgb(133, 146, 181))
Else
boxBrush = New SolidBrush(Color.FromArgb(212, 213, 216))
End If

Dim boxRect As New Rectangle(imageLeft - 1, imageTop - 1, 16 + 2, 16 + 2)

' Fill the checkbox area very slightly
g.FillRectangle(boxBrush, boxRect)

' Draw the box around the checkmark area
g.DrawRectangle(boxPen, boxRect)

boxPen.Dispose()
boxBrush.Dispose()

Dim pPen As New Pen(Color.Black, 1)
g.DrawLine(pPen, New Point(imageLeft + 5, imageTop + 8), New Point(imageLeft + 5 + 2, imageTop + 8 + 2))
g.DrawLine(pPen, New Point(imageLeft + 5, imageTop + 9), New Point(imageLeft + 5 + 2, imageTop + 9 + 2))
g.DrawLine(pPen, New Point(imageLeft + 5 + 2, imageTop + 8 + 2), New Point(imageLeft + 5 + 2 + 4, imageTop + 8 + 2 - 4))
g.DrawLine(pPen, New Point(imageLeft + 5 + 2, imageTop + 9 + 2), New Point(imageLeft + 5 + 2 + 4, imageTop + 9 + 2 - 4))
End If
End If
End Sub
End Class
End Namespace
Questionwhy only on cell/row index=-1? Pin
Huisheng Chen1-Sep-09 15:21
Huisheng Chen1-Sep-09 15:21 
AnswerRe: why only on cell/row index=-1? Pin
Fiwel2-Sep-09 4:41
Fiwel2-Sep-09 4:41 
AnswerRe: why only on cell/row index=-1? Pin
Fiwel2-Sep-09 5:42
Fiwel2-Sep-09 5:42 
GeneralIndicate Filter Pin
Ivo Closs1-Sep-09 15:19
Ivo Closs1-Sep-09 15:19 
GeneralRe: Indicate Filter Pin
Fiwel2-Sep-09 5:33
Fiwel2-Sep-09 5:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.