DataGridView右鍵菜單自定義顯示及隱藏列功能
WinForm程序中表單的列可自定義顯示及隱藏,是一種常見的功能,對于用戶體驗來說是非常好的。筆者經(jīng)過一段時間的摸索,終于實現(xiàn)了自己想要的功能及效果,現(xiàn)記錄一下過程:
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控件實現(xiàn)菜單繪制的功能,其代碼如下:
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; //最大行數(shù)
private static readonly int BasicSide = 10; //Item:CheckBox邊長(建議用偶數(shù))
private int totality = 1; //分割總數(shù)
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; } //勾選狀態(tài)
//構(gòu)造函數(shù)
public PopupMenuItem(string itemText) : this(itemText, false)
{
}
public PopupMenuItem(string itemText, bool isChecked)
{
ItemText = itemText;
IsChecked = isChecked;
}
}
//無參構(gòu)造函數(shù)
public PopupMenuHelper()
{
}
/// <summary>
/// 被點擊Item的Index
/// </summary>
public int HitIndex
{
get
{
return items.IndexOf(hotItem);
}
}
/// <summary>
/// 勾選改變狀態(tài)
/// </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勾選狀態(tài)</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:存儲有序浮點數(shù)對,通常為矩形的寬度和高度。
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:存儲有序浮點數(shù)對,通常為矩形的寬度和高度。
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區(qū)域
Rectangle drawRect = new Rectangle(left, top, width, BasicConst);
top += BasicConst;
//Text區(qū)域
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:在矩形內(nèi)設置格式時,禁用自動換行功能。
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;
}
}
這個類實現(xiàn)了多菜單頁面的功能:即如果DataGridView字段非常的多,可通過產(chǎn)生多列菜單來顯示,程序是通過BasicRows變量來控制。
5、新建一個DataGridViewColumnSelector類,此類的功能主要是銜接DataGridView與PopupMenuControl,其代碼如下:
/// <summary>
/// DataGridView右鍵菜單自定義顯示及隱藏列
/// </summary>
class DataGridViewColumnSelector
{
private DataGridView dgvTarget = null; //待處理的DataGridView對象
private ToolStripDropDown dropDown; //用于加載PopupMenu控件
PopupMenuControl popupMenuControl = new PopupMenuControl(); //PopupMenu控件
//無參構(gòu)造函數(shù)
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);
}
//有參構(gòu)造函數(shù)
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>
/// 勾選事件執(zhí)行方法
/// </summary>
/// <param name="hitIndex"></param>
/// <param name="isCheck"></param>
private void OnCheckedChanged(int hitIndex, bool isChecked)
{
dgvTarget.Columns[hitIndex].Visible = isChecked;
}
}
6、以上這些,已經(jīng)實現(xiàn)了全部的功能。下面開始建一個WinForm程序來測試結(jié)果,為方便測試將DataGridView的數(shù)據(jù)源由xml文件讀取。
從SQL Server數(shù)據(jù)庫隨便找張數(shù)據(jù)表生成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 = "生產(chǎn)數(shù)量";
dataGridView1.Columns[3].HeaderText = "來源單號";
#endregion
DataGridViewColumnSelector columnSelector = new DataGridViewColumnSelector(dataGridView1);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
8、執(zhí)行程序,在任意DataGridView標題欄右擊,即可彈出菜單:
總結(jié)
以上所述是小編給大家介紹的DataGridView右鍵菜單自定義顯示及隱藏列功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對我們網(wǎng)站的支持!
如果你覺得本文對你有幫助,歡迎轉(zhuǎn)載,煩請注明出處,謝謝!
上一篇:VS2019下opencv4.1.2配置圖文教程(永久配置)
欄 目:ASP.NET
本文標題:DataGridView右鍵菜單自定義顯示及隱藏列功能
本文地址:http://www.jygsgssxh.com/a1/ASP_NET/10847.html
您可能感興趣的文章


閱讀排行
本欄相關
- 01-11vscode extension插件開發(fā)詳解
- 01-11VsCode插件開發(fā)之插件初步通信的方法
- 01-11如何給asp.net core寫個簡單的健康檢查
- 01-11.net core高吞吐遠程方法如何調(diào)用組件
- 01-11淺析.Net Core中Json配置的自動更新
- 01-11.NET開發(fā)人員關于ML.NET的入門學習
- 01-11.NET Core 遷移躺坑記續(xù)集之Win下莫名其
- 01-11.net core webapi jwt 更為清爽的認證詳解
- 01-11docker部署Asp.net core應用的完整步驟
- 01-11ASP.NET Core靜態(tài)文件的使用方法
隨機閱讀
- 01-11ajax實現(xiàn)頁面的局部加載
- 01-10SublimeText編譯C開發(fā)環(huán)境設置
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-10C#中split用法實例總結(jié)
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10delphi制作wav文件的方法
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 04-02jquery與jsp,用jquery


