首页 > 编程 > .NET > 正文

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

2020-01-17 20:30:22
字体:
来源:转载
供稿:网友

 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右键菜单自定义显示及隐藏列功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对武林网网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表