DataGridView右键菜单自定义显示及隐藏列功能

 WinForm程序中表单的列可自定义显示及隐藏,是一种常见的功能,对于用户体验来说是非常好的。笔者经过一段时间的摸索,终于实现了自己想要的功能及效果,现记录一下过程:

    1、新建一个自定义控件,命名为:PopupMenuControl。

    2、在PopupMenuControl.Designet文件中的InitializeComponent()方法下面,注册以下事件:

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、PopupMenuControl的代码:

public partial class PopupMenuControl : UserControl

{ public delegate void CheckedChanged(int hitIndex, bool isChecked); //勾选改变委托

public event CheckedChanged CheckedChangedEvent; //勾选改变事件

PopupMenuHelper popupMenuHelper = null; //菜单帮助类,主要负责菜单绘制。

public PopupMenuControl()

{

InitializeComponent();

}

public void Initialize(DataGridView dgvTarget)

{

//菜单帮助类实例化

popupMenuHelper = new PopupMenuHelper();

//将列标题添加到items

foreach (DataGridViewColumn column in dgvTarget.Columns)

{

popupMenuHelper.AddItem(column.HeaderText, column.Visible);

}

//菜单绘制

popupMenuHelper.Prepare(CreateGraphics());

Width = popupMenuHelper.Width;

Height = popupMenuHelper.Height;

}

/// <summary>

/// 绘制

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void PopupMenuControl_Paint(object sender, PaintEventArgs e)

{

popupMenuHelper.Draw(e.Graphics);

}

/// <summary>

/// 鼠标移过

/// </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>

/// 鼠标按下

/// </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>

/// 勾选改变

/// </summary>

/// <param name="iIndex"></param>

/// <param name="bChecked"></param>

public virtual void OnCheckedChanged(int hitIndex, bool isChecked)

{

CheckedChangedEvent?.Invoke(hitIndex, isChecked);

}

}

    4、这上面涉及到一个PopupMenuHelper的帮助类,此帮助类主要是为PopupMenuControl控件实现菜单绘制的功能,其代码如下:

   

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; //四周间距

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:指定文本在布局矩形中居中对齐(呃,感觉不是很垂直居中,偏上了一些)。

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,与文本的对齐效果稍微好一些。

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;

}

}

    这个类实现了多菜单页面的功能:即如果DataGridView字段非常的多,可通过产生多列菜单来显示,程序是通过BasicRows变量来控制。

    5、新建一个DataGridViewColumnSelector类,此类的功能主要是衔接DataGridView与PopupMenuControl,其代码如下:

/// <summary>

/// DataGridView右键菜单自定义显示及隐藏列

/// </summary>

class DataGridViewColumnSelector

{

private DataGridView dgvTarget = null; //待处理的DataGridView对象

private ToolStripDropDown dropDown; //用于加载PopupMenu控件

PopupMenuControl popupMenuControl = new PopupMenuControl(); //PopupMenu控件

//无参构造函数

public DataGridViewColumnSelector()

{

//注册PopupMenu控件事件

popupMenuControl.CheckedChangedEvent += new PopupMenuControl.CheckedChanged(OnCheckedChanged);

//使用容器承载PopupMenu控件(相当于容器类型的ToolStripItem)

ToolStripControlHost controlHost = new ToolStripControlHost(popupMenuControl);

controlHost.Padding = Padding.Empty;

controlHost.Margin = Padding.Empty;

controlHost.AutoSize = false;

//加载PopupMenu控件

dropDown = new ToolStripDropDown();

dropDown.Padding = Padding.Empty;

dropDown.AutoClose = true;

dropDown.Items.Add(controlHost);

}

//有参构造函数

public DataGridViewColumnSelector(DataGridView dataGridView) : this()

{

DataGridView = dataGridView;

}

//DataGridView属性

public DataGridView DataGridView

{

get { return dgvTarget; }

set

{

//去除单元格点击事件

if (dgvTarget != null) { dgvTarget.CellMouseClick -= new DataGridViewCellMouseEventHandler(DataGridView_CellMouseClick); }

dgvTarget = value;

//注册单元格点击事件

if (dgvTarget != null) { dgvTarget.CellMouseClick += new DataGridViewCellMouseEventHandler(DataGridView_CellMouseClick); }

}

}

/// <summary>

/// 右键点击标题栏弹出菜单

/// </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);

//将菜单显示在光标位置

dropDown.Show(Cursor.Position);

}

}

/// <summary>

/// 勾选事件执行方法

/// </summary>

/// <param name="hitIndex"></param>

/// <param name="isCheck"></param>

private void OnCheckedChanged(int hitIndex, bool isChecked)

{

dgvTarget.Columns[hitIndex].Visible = isChecked;

}

}

    6、以上这些,已经实现了全部的功能。下面开始建一个WinForm程序来测试结果,为方便测试将DataGridView的数据源由xml文件读取。

          从SQL Server数据库随便找张数据表生成XML,文件保存为Test.xml。(请将Test.xml文件拷贝到Debug文件夹下面)

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、新建一个WinForm程序,命名为Main,并拖入一个DataGridView控件,Main_Load方法如下:

       

private void Main_Load(object sender, EventArgs e)

{

try

{

//xml文件路径

string path = @"Test.xml";

//读取文件

DataSet ds = new DataSet();

if (File.Exists(path))

{

ds.ReadXml(path);

}

dataGridView1.DataSource = ds.Tables.Count > 0 ? ds.Tables[0] : null;

//加工dataGridView1

#region 加列标题测试

dataGridView1.Columns[0].HeaderText = "制令单号";

dataGridView1.Columns[1].HeaderText = "成品编号";

dataGridView1.Columns[2].HeaderText = "生产数量";

dataGridView1.Columns[3].HeaderText = "来源单号";

#endregion

DataGridViewColumnSelector columnSelector = new DataGridViewColumnSelector(dataGridView1);

}

catch (Exception ex)

{

MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);

}

}

    8、执行程序,在任意DataGridView标题栏右击,即可弹出菜单:

总结

以上所述是小编给大家介绍的DataGridView右键菜单自定义显示及隐藏列功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!

如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

以上是 DataGridView右键菜单自定义显示及隐藏列功能 的全部内容, 来源链接: utcz.com/z/350748.html

回到顶部