WinForm程序中表单的列可自定义显示及隐藏,是一种常见的功能,对于用户体验来说是非常好的。笔者经过一段时间的摸索,终于实现了自己想要的功能及效果,现记录一下过程:
1、新建一个自定义控件,命名为:PopupMenuControl。
2、在PopupMenuControl.Designet文件中的InitializeComponent()方法下面,注册以下事件:
1
2
3
|
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的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
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控件实现菜单绘制的功能,其代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
|
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,其代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
/// <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文件夹下面)
1
2
3
4
5
|
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方法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
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右键菜单自定义显示及隐藏列功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!
原文链接:https://www.cnblogs.com/atomy/archive/2019/11/12/11812458.html