DataGridView User defined Display and Hide Column Function in Right click Menu
- 2021-11-10 09:21:21
- OfStack
WinForm program form columns can be customized display and hide, is a common function, for the user experience is very good. After a period of exploration, the author finally realized the function and effect he wanted. Now record the following process:
1. Create a new custom control named PopupMenuControl.
2. Under the InitializeComponent () method in the PopupMenuControl. Designet file, register the following events:
this.Paint += new System.Windows.Forms.PaintEventHandler(this.PopupMenuControl_Paint);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.PopupMenuControl_MouseDown);
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PopupMenuControl_MouseMove);
3. Code of PopupMenuControl:
public partial class PopupMenuControl : UserControl
{ public delegate void CheckedChanged(int hitIndex, bool isChecked); // Check Change Delegation
public event CheckedChanged CheckedChangedEvent; // Check Change Event
PopupMenuHelper popupMenuHelper = null; // Menu help class, mainly responsible for menu drawing.
public PopupMenuControl()
{
InitializeComponent();
}
public void Initialize(DataGridView dgvTarget)
{
// Menu Help Class Instantiation
popupMenuHelper = new PopupMenuHelper();
// Add column headers to the items
foreach (DataGridViewColumn column in dgvTarget.Columns)
{
popupMenuHelper.AddItem(column.HeaderText, column.Visible);
}
// Menu drawing
popupMenuHelper.Prepare(CreateGraphics());
Width = popupMenuHelper.Width;
Height = popupMenuHelper.Height;
}
/// <summary>
/// Draw
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PopupMenuControl_Paint(object sender, PaintEventArgs e)
{
popupMenuHelper.Draw(e.Graphics);
}
/// <summary>
/// Mouse over
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PopupMenuControl_MouseMove(object sender, MouseEventArgs e)
{
if (popupMenuHelper.IsMouseMove(e.X, e.Y))
{
popupMenuHelper.Draw(CreateGraphics());
}
}
/// <summary>
/// Mouse press
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PopupMenuControl_MouseDown(object sender, MouseEventArgs e)
{
if (popupMenuHelper.IsMouseDown(e.X, e.Y))
{
int hitIndex = popupMenuHelper.HitIndex;
if (hitIndex != -1)
{
bool isChecked = popupMenuHelper.IsCheckedChange(hitIndex, CreateGraphics());
OnCheckedChanged(hitIndex, isChecked);
}
}
}
/// <summary>
/// Check Change
/// </summary>
/// <param name="iIndex"></param>
/// <param name="bChecked"></param>
public virtual void OnCheckedChanged(int hitIndex, bool isChecked)
{
CheckedChangedEvent?.Invoke(hitIndex, isChecked);
}
}
4. This involves a help class of PopupMenuHelper. This help class mainly realizes the function of menu drawing for PopupMenuControl controls. Its code is as follows:
class PopupMenuHelper
{
//变量
private PopupMenuItem hotItem = null; //当前Item
private List<PopupMenuItem> items = new List<PopupMenuItem>(); //Item集合
private Bitmap bitmap; //位图
private Graphics graphics; //图像
private static readonly int BasicConst = 24; //Item:高度、Image宽度
private static readonly int BasicGap = 3; //4周间距
private static readonly int BasicRows = 3; //最大行数
private static readonly int BasicSide = 10; //Item:CheckBox边长(建议用偶数)
private int totality = 1; //分割总数
private int[] eachWidth = null; //各个宽度
//属性
public int Width { get { return bitmap.Width; } } //宽度
public int Height { get { return bitmap.Height; } } //高度
//PopupMenuItem类
private class PopupMenuItem
{
//属性
public string ItemText { get; set; } //Item文本
public bool IsChecked { get; set; } //勾选状态
//构造函数
public PopupMenuItem(string itemText) : this(itemText, false)
{
}
public PopupMenuItem(string itemText, bool isChecked)
{
ItemText = itemText;
IsChecked = isChecked;
}
}
//无参构造函数
public PopupMenuHelper()
{
}
/// <summary>
/// 被点击Item的Index
/// </summary>
public int HitIndex
{
get
{
return items.IndexOf(hotItem);
}
}
/// <summary>
/// 勾选改变状态
/// </summary>
/// <param name="hitIndex">被点击Item的Index</param>
/// <param name="g">图像</param>
/// <returns></returns>
public bool IsCheckedChange(int hitIndex, Graphics g)
{
items[hitIndex].IsChecked = !items[hitIndex].IsChecked;
Draw(g);
return items[hitIndex].IsChecked;
}
/// <summary>
/// 添加Item
/// </summary>
/// <param name="itemText">Item文本</param>
/// <param name="isChecked">Item勾选状态</param>
public void AddItem(string itemText, bool isChecked)
{
items.Add(new PopupMenuItem(itemText, isChecked));
}
/// <summary>
/// 绘制菜单准备
/// </summary>
/// <param name="g">图像</param>
public void Prepare(Graphics g)
{
//获取菜单的宽度及高度
totality = (int)Math.Ceiling((double)items.Count / BasicRows);
eachWidth = new int[totality];
int totalWidth = 0, totalHeight = 0;
double maxTextWidth = 0;
if (totality == 1)
{
totalHeight = items.Count * BasicConst + 2 * BasicGap;
foreach (PopupMenuItem item in items)
{
//SizeF:存储有序浮点数对,通常为矩形的宽度和高度。
SizeF sizeF = g.MeasureString(item.ItemText, SystemInformation.MenuFont);
maxTextWidth = Math.Max(maxTextWidth, sizeF.Width);
}
totalWidth = (int)Math.Ceiling((double)maxTextWidth) + BasicConst + 2 * BasicGap;
eachWidth[0] = (int)Math.Ceiling((double)maxTextWidth) + BasicConst;
}
else
{
totalHeight = BasicRows * BasicConst + 2 * BasicGap;
int rows = 0, cols = 1;
foreach (PopupMenuItem item in items)
{
rows++;
//SizeF:存储有序浮点数对,通常为矩形的宽度和高度。
SizeF sizeF = g.MeasureString(item.ItemText, SystemInformation.MenuFont);
maxTextWidth = Math.Max(maxTextWidth, sizeF.Width);
if (cols < totality)
{
//1..[totality-1]列
if (rows == BasicRows)
{
totalWidth += (int)Math.Ceiling((double)maxTextWidth) + BasicConst;
eachWidth[cols - 1] = (int)Math.Ceiling((double)maxTextWidth) + BasicConst;
maxTextWidth = 0;
cols++;
rows = 0;
}
}
else
{
//totality列
if ((cols - 1) * BasicRows + rows == items.Count)
{
totalWidth += (int)Math.Ceiling((double)maxTextWidth) + BasicConst + 2 * BasicGap;
eachWidth[cols - 1] = (int)Math.Ceiling((double)maxTextWidth) + BasicConst;
}
}
}
}
//图像初始化
bitmap = new Bitmap(totalWidth, totalHeight);
graphics = Graphics.FromImage(bitmap);
}
/// <summary>
/// 绘制菜单
/// </summary>
/// <param name="g"></param>
public void Draw(Graphics g)
{
Rectangle area = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
graphics.Clear(SystemColors.Menu);
DrawBackground(graphics, area);
DrawItems(graphics);
g.DrawImage(bitmap, area, area, GraphicsUnit.Pixel);
}
/// <summary>
/// 绘制菜单背景
/// </summary>
/// <param name="g"></param>
/// <param name="area"></param>
private void DrawBackground(Graphics g, Rectangle area)
{
//描边
using (Pen borderPen = new Pen(Color.FromArgb(112, 112, 112)))
g.DrawRectangle(borderPen, area);
//Image及Text
int left = BasicGap, top = BasicGap;
if (totality == 1)
{
Rectangle imageArea = new Rectangle(left, top, BasicConst, items.Count * BasicConst);
using (Brush backBrush = new SolidBrush(Color.FromArgb(240, 240, 240)))
g.FillRectangle(backBrush, imageArea);
Rectangle textArea = new Rectangle(left + BasicConst, top, eachWidth[0], items.Count * BasicConst);
using (Brush backBrush = new SolidBrush(Color.FromArgb(255, 255, 255)))
g.FillRectangle(backBrush, textArea);
}
else
{
for (int i = 0; i < totality; i++)
{
Rectangle imageArea = new Rectangle(left, top, BasicConst, BasicRows * BasicConst);
using (Brush backBrush = new SolidBrush(Color.FromArgb(240, 240, 240)))
g.FillRectangle(backBrush, imageArea);
Rectangle textArea = new Rectangle(left + BasicConst, top, eachWidth[i], BasicRows * BasicConst);
using (Brush backBrush = new SolidBrush(Color.FromArgb(255, 255, 255)))
g.FillRectangle(backBrush, textArea);
left += eachWidth[i];
}
}
}
/// <summary>
/// 绘制所有菜单Item
/// </summary>
/// <param name="g">图像</param>
private void DrawItems(Graphics g)
{
int left = BasicGap, top = BasicGap;
int rows = 0, cols = 1;
foreach (PopupMenuItem item in items)
{
if (totality == 1)
{
DrawSingleItem(g, left, ref top, eachWidth[0], item, item == hotItem);
}
else
{
rows++;
DrawSingleItem(g, left, ref top, eachWidth[cols - 1], item, item == hotItem);
//1..[totality-1]列
if (rows % BasicRows == 0)
{
left += eachWidth[cols - 1];
top = BasicGap;
cols++;
rows = 0;
}
}
}
}
/// <summary>
/// 绘制单个菜单Item
/// </summary>
/// <param name="g">图像</param>
/// <param name="top">图像Top</param>
/// <param name="item">菜单Item</param>
/// <param name="isHotItem">是否为当前菜单Item</param>
private void DrawSingleItem(Graphics g, int left, ref int top,int width, PopupMenuItem item, bool isHotItem)
{
//Item区域
Rectangle drawRect = new Rectangle(left, top, width, BasicConst);
top += BasicConst;
//Text区域
Rectangle itemTextArea = new Rectangle
(
drawRect.Left + BasicConst,
drawRect.Top,
drawRect.Width - BasicConst,
drawRect.Height
);
//背景色及描边色
if (isHotItem)
{
//HotItem
Rectangle hotItemArea = new Rectangle(drawRect.Left, drawRect.Top, drawRect.Width, drawRect.Height);
using (SolidBrush backBrush = new SolidBrush(Color.FromArgb(214, 235, 255)))
g.FillRectangle(backBrush, hotItemArea);
using (Pen borderPen = new Pen(Color.FromArgb(51, 153, 255)))
g.DrawRectangle(borderPen, hotItemArea);
}
//Text处理
StringFormat itemTextFormat = new StringFormat();
//NoClip:允许显示字形符号的伸出部分和延伸到矩形外的未换行文本。
//NoWrap:在矩形内设置格式时,禁用自动换行功能。
itemTextFormat.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.NoWrap;
//Near:指定文本靠近布局对齐。
itemTextFormat.Alignment = StringAlignment.Near;
//Center:指定文本在布局矩形中居中对齐(呃,感觉不是很垂直居中,偏上了1些)。
itemTextFormat.LineAlignment = StringAlignment.Center;
//Show:显示热键前缀。
itemTextFormat.HotkeyPrefix = HotkeyPrefix.Show;
SolidBrush textBrush = new SolidBrush(SystemColors.MenuText);
g.DrawString(item.ItemText, SystemInformation.MenuFont, textBrush, itemTextArea, itemTextFormat);
//Checkbox处理
if (item.IsChecked)
{
int checkBoxGap = (int)((drawRect.Height - BasicSide) / 2);
int checkBoxLeft = drawRect.Left + checkBoxGap;
int checkBoxTop = drawRect.Top + checkBoxGap;
//将checkBoxArea的Top减1,与文本的对齐效果稍微好1些。
Rectangle checkBoxArea = new Rectangle(checkBoxLeft, checkBoxTop - 1, BasicSide, BasicSide);
using (Brush checkBoxBrush = new SolidBrush(Color.FromArgb(214, 235, 255)))
g.FillRectangle(checkBoxBrush, checkBoxArea);
using (Pen checkBoxPen = new Pen(Color.FromArgb(51, 153, 255)))
g.DrawRectangle(checkBoxPen, checkBoxArea);
using (Pen checkBoxTick = new Pen(Color.FromArgb(51, 153, 255)))
{
g.DrawLine(checkBoxTick, new Point(checkBoxLeft, checkBoxTop - 1 + (int)(BasicSide / 2)), new Point(checkBoxLeft + (int)(BasicSide / 2), checkBoxTop - 1 + BasicSide));
g.DrawLine(checkBoxTick, new Point(checkBoxLeft + (int)(BasicSide / 2), checkBoxTop - 1 + BasicSide), new Point(checkBoxLeft + BasicSide + BasicGap, checkBoxTop - 1 - BasicGap));
}
}
}
/// <summary>
/// 点击测试
/// </summary>
/// <param name="X">X坐标</param>
/// <param name="Y">Y坐标</param>
/// <returns></returns>
private PopupMenuItem HitTest(int X, int Y)
{
if (X < 0 || X > Width || Y < 0 || Y > Height)
{
return null;
}
int left = BasicGap, top = BasicGap;
int rows = 0, cols = 1;
foreach (PopupMenuItem item in items)
{
if (totality == 1)
{
rows++;
if (X > left && X < left + eachWidth[0] && Y > top + (rows - 1) * BasicConst && Y < top + rows * BasicConst)
{
return item;
}
}
else
{
rows++;
if (X > left && X < left + eachWidth[cols - 1] && Y > top + (rows - 1) * BasicConst && Y < top + rows * BasicConst)
{
return item;
}
//1..[totality-1]列
if (rows % BasicRows == 0)
{
left += eachWidth[cols - 1];
top = BasicGap;
cols++;
rows = 0;
}
}
}
return null;
}
/// <summary>
/// 是否是鼠标移过
/// </summary>
/// <param name="X">X坐标</param>
/// <param name="Y">Y坐标</param>
/// <returns></returns>
public bool IsMouseMove(int X, int Y)
{
PopupMenuItem popupMenuItem = HitTest(X, Y);
if (popupMenuItem != hotItem)
{
hotItem = popupMenuItem;
return true;
}
else
{
return false;
}
}
/// <summary>
/// 是否是鼠标按下
/// </summary>
/// <param name="X">X坐标</param>
/// <param name="Y">Y坐标</param>
/// <returns></returns>
public bool IsMouseDown(int X, int Y)
{
PopupMenuItem popupMenuItem = HitTest(X, Y);
return popupMenuItem != null;
}
}
This class realizes the function of multi-menu page: that is, if DataGridView fields are very many, it can be displayed by producing multi-column menus, and the program is controlled by BasicRows variables.
5. Create a new DataGridViewColumnSelector class. The function of this class is to connect DataGridView with PopupMenuControl. Its code is as follows:
/// <summary>
/// DataGridView Right-click menu to customize display and hide columns
/// </summary>
class DataGridViewColumnSelector
{
private DataGridView dgvTarget = null; // To be processed DataGridView Object
private ToolStripDropDown dropDown; // Used to load PopupMenu Control
PopupMenuControl popupMenuControl = new PopupMenuControl(); //PopupMenu Control
// Parametric constructor
public DataGridViewColumnSelector()
{
// Registration PopupMenu Control event
popupMenuControl.CheckedChangedEvent += new PopupMenuControl.CheckedChanged(OnCheckedChanged);
// Use container load PopupMenu Control ( Equivalent to the container type ToolStripItem)
ToolStripControlHost controlHost = new ToolStripControlHost(popupMenuControl);
controlHost.Padding = Padding.Empty;
controlHost.Margin = Padding.Empty;
controlHost.AutoSize = false;
// Loading PopupMenu Control
dropDown = new ToolStripDropDown();
dropDown.Padding = Padding.Empty;
dropDown.AutoClose = true;
dropDown.Items.Add(controlHost);
}
// Parametric constructor
public DataGridViewColumnSelector(DataGridView dataGridView) : this()
{
DataGridView = dataGridView;
}
//DataGridView Attribute
public DataGridView DataGridView
{
get { return dgvTarget; }
set
{
// Remove cell click event
if (dgvTarget != null) { dgvTarget.CellMouseClick -= new DataGridViewCellMouseEventHandler(DataGridView_CellMouseClick); }
dgvTarget = value;
// Register cell click event
if (dgvTarget != null) { dgvTarget.CellMouseClick += new DataGridViewCellMouseEventHandler(DataGridView_CellMouseClick); }
}
}
/// <summary>
/// Right-click the title bar pop-up menu
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DataGridView_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button == MouseButtons.Right && e.RowIndex == -1)
{
popupMenuControl.Initialize(dgvTarget);
// Display the menu at the cursor position
dropDown.Show(Cursor.Position);
}
}
/// <summary>
/// Check the event execution method
/// </summary>
/// <param name="hitIndex"></param>
/// <param name="isCheck"></param>
private void OnCheckedChanged(int hitIndex, bool isChecked)
{
dgvTarget.Columns[hitIndex].Visible = isChecked;
}
}
6. All the functions have been realized. The following start to build an WinForm program to test the results, for the convenience of testing the data source of DataGridView read by xml file.
From the SQL Server database to find a data table to generate XML, the file is saved as Test. xml. (Copy the Test. xml file under the Debug folder)
SELECT TOP 10 MO_NO,MRP_NO,QTY,BIL_NO
FROM MF_MO
WHERE MO_DD='2019-11-07'
ORDER BY MO_NO
FOR XML PATH ('Category'),TYPE,ROOT('DocumentElement')
7. Create a new WinForm program, named Main, and drag in an DataGridView control. The Main_Load method is as follows:
private void Main_Load(object sender, EventArgs e)
{
try
{
//xml File path
string path = @"Test.xml";
// Read a file
DataSet ds = new DataSet();
if (File.Exists(path))
{
ds.ReadXml(path);
}
dataGridView1.DataSource = ds.Tables.Count > 0 ? ds.Tables[0] : null;
// Machining dataGridView1
#region Add header test
dataGridView1.Columns[0].HeaderText = " Order number ";
dataGridView1.Columns[1].HeaderText = " Finished product number ";
dataGridView1.Columns[2].HeaderText = " Production quantity ";
dataGridView1.Columns[3].HeaderText = " Source number ";
#endregion
DataGridViewColumnSelector columnSelector = new DataGridViewColumnSelector(dataGridView1);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, " Prompt ", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
8. Execute the program and right-click on any DataGridView title bar to pop up the menu:
Summarize