winfrom编程实现DataGridView关联快捷菜单

简介:

在winfrom编程中我们经常使用表格控件DataGridView的行关联快捷菜单(也称为上下文弹出菜单)ContextMenuStrip,基本步骤如下:

在窗体上设计ContextMenuStrip快捷菜单控件;
设置DataGridView.RowTemplate.ContextMenuStrip属性为指定的快捷菜单;
在菜单弹出前捕获关联事件DataGridView.RowContextMenuStripNeeded,获得当前行与快捷菜单,并做适当处理。
但是,使用其关联事件DataGridView.RowContextMenuStripNeeded有一个重要的前提:“RowContextMenuStripNeeded 事件仅在设置了DataGridView控件的DataSource属性或者该控件的VirtualMode属性为 true 时发生。”(参考MSDN:RowContextMenuStripNeeded 事件)。

此外,上述方法还有一个不足之处:在非数据行的地方(如:表格列头或行头)不能使用RowTemplate.ContextMenuStrip快捷菜单,也捕获不到事件DataGridView.RowContextMenuStripNeeded事件。

事实上,DataGridView.ContextMenuStrip是控件本身的快捷菜单。本文介绍的定制DataGridView控件,就是直接应用其ContextMenuStrip属性,定制一个快捷菜单关联事件,实现RowTemplate.ContextMenuStrip类似功能。基本思路如下:

重写DataGridView.MouseDown(MouseEventArgs e)方法,捕获鼠标右击事件;
根据事件参数MouseEventArgs的鼠标位置,计算DataGridView当前位置的行号与列号;
定制关联事件ContextMenuStripMenu,在快捷菜单弹出前获取行号、列号与快捷菜单对象对象。


关键技术
捕获鼠标右击位置(坐标),根据该位置计算当前行号与列号,并引发自定义关联事件。如下代码是捕获鼠标右击事件(定制DataGridView控件中的代码):
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Right)
{
if (this.ContextMenuStrip != null && this.ContextMenuStripNeeded != null)
{
int rowIndex = this.GetRowIndexAt(e.Location); // 计算行号
int colIndex = this.GetColIndexAt(e.Location); // 计算列号
ContextMenuStripNeededEventArgs ee; // 事件参数
ee = new ContextMenuStripNeededEventArgs(this.ContextMenuStrip, rowIndex, colIndex);
this.OnContextMenuStripNeeded(ee); // 引发自定义事件,执行事件方法
}
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Right)
{
if (this.ContextMenuStrip != null && this.ContextMenuStripNeeded != null)
{
int rowIndex = this.GetRowIndexAt(e.Location); // 计算行号
int colIndex = this.GetColIndexAt(e.Location); // 计算列号
ContextMenuStripNeededEventArgs ee; // 事件参数
ee = new ContextMenuStripNeededEventArgs(this.ContextMenuStrip, rowIndex, colIndex);
this.OnContextMenuStripNeeded(ee); // 引发自定义事件,执行事件方法
}
}
}


只有在ContextMenuStrip属性对象非空,以及定制关联事件ContextMenuStripNeeded非空(即有事件注册者)时,才需要计算行列坐标,并由OnContextMenuStripNeeded引发调用事件处理方法。当前鼠标位置的行/列编号计算方法如下:


private int GetColIndexAt(Point mouseLocation)
{
int colIndex = -1;
int colOffset = 0;
int gridWidth = 1;
int padding = 1;
if (this.RowHeadersVisible)
{
colOffset += this.RowHeadersWidth;
padding = 0;
}
if (colOffset + padding < mouseLocation.X) // 超过表列头的范围(不含顶头的边框)
{
int curCol = this.FirstDisplayedScrollingColumnIndex;
for (int k = 0; k <= this.DisplayedColumnCount(true); k++)
{
if (curCol >= this.Columns.Count)
{
break;
}
if (this.Columns[curCol].Visible)
{
colOffset += this.Columns[curCol].Width;
}
if (colOffset + padding + gridWidth > mouseLocation.X) // x为当前边框位置
{
colIndex = curCol;
break;
}
curCol++;
}
}
return colIndex;
}
private int GetRowIndexAt(Point mouseLocation)
{
int rowIndex = -1;
int rowOffset = 0;
int gridWidth = 1;
int padding = 1;
if (this.ColumnHeadersVisible)
{
rowOffset += this.ColumnHeadersHeight;
padding = 0;
}
if (rowOffset + padding < mouseLocation.Y) // 超过表列头的范围(不含顶头的边框)
{
int curRow = this.FirstDisplayedScrollingRowIndex;
for (int k = 0; k <= this.DisplayedRowCount(true); k++)
{
if (curRow >= this.Rows.Count)
{
break;
}
if (this.Rows[curRow].Visible)
{
rowOffset += this.Rows[curRow].Height;
}
if (rowOffset + padding + gridWidth > mouseLocation.Y) // y为当前边框位置
{
rowIndex = curRow;
break;
}
curRow++;
}
}
return rowIndex;
}
private int GetColIndexAt(Point mouseLocation)
{
int colIndex = -1;
int colOffset = 0;
int gridWidth = 1;
int padding = 1;
if (this.RowHeadersVisible)
{
colOffset += this.RowHeadersWidth;
padding = 0;
}
if (colOffset + padding < mouseLocation.X) // 超过表列头的范围(不含顶头的边框)
{
int curCol = this.FirstDisplayedScrollingColumnIndex;
for (int k = 0; k <= this.DisplayedColumnCount(true); k++)
{
if (curCol >= this.Columns.Count)
{
break;
}
if (this.Columns[curCol].Visible)
{
colOffset += this.Columns[curCol].Width;
}
if (colOffset + padding + gridWidth > mouseLocation.X) // x为当前边框位置
{
colIndex = curCol;
break;
}
curCol++;
}
}
return colIndex;
}
private int GetRowIndexAt(Point mouseLocation)
{
int rowIndex = -1;
int rowOffset = 0;
int gridWidth = 1;
int padding = 1;
if (this.ColumnHeadersVisible)
{
rowOffset += this.ColumnHeadersHeight;
padding = 0;
}
if (rowOffset + padding < mouseLocation.Y) // 超过表列头的范围(不含顶头的边框)
{
int curRow = this.FirstDisplayedScrollingRowIndex;
for (int k = 0; k <= this.DisplayedRowCount(true); k++)
{
if (curRow >= this.Rows.Count)
{
break;
}
if (this.Rows[curRow].Visible)
{
rowOffset += this.Rows[curRow].Height;
}
if (rowOffset + padding + gridWidth > mouseLocation.Y) // y为当前边框位置
{
rowIndex = curRow;
break;
}
curRow++;
}
}
return rowIndex;
}


代码中,参数mouseLocation来自MouseEventArgs的Location属性,this.FirstDisplayedScrollingRowIndex表示当前显示的第一行的行号,this.DisplayedRowCount(true)获取显示的全部行数,参数true表示要包括最后部分显示的行。

按照惯例,-1表示当前鼠标位置位于所有行或列之外,如:表的列头、行头等地方。




本文转自94cool博客园博客,原文链接:http://www.cnblogs.com/94cool/archive/2009/09/09/1563030.html,如需转载请自行联系原作者


相关文章
|
5天前
如何实现右击DataGridView的表格出现快捷菜单进行操作
如何实现右击DataGridView的表格出现快捷菜单进行操作
17 0
|
20天前
|
开发工具
如何使用 Excel VBA 编程,点击按钮后跳转到有数据填充的最末一行
如何使用 Excel VBA 编程,点击按钮后跳转到有数据填充的最末一行
16 2
|
8月前
|
小程序 JavaScript 容器
小程序封装拖拽菜单组件(uniapp拖拽排序,自定义菜单)
movable-area 是 uniapp 的可移动区域组件。它用于定义可移动视图容器,在其内部可拖拽移动子视图。
350 0
基于C#的ArcEngine二次开发56:双击属性表跳转目标要素并闪烁
基于C#的ArcEngine二次开发56:双击属性表跳转目标要素并闪烁
基于C#的ArcEngine二次开发56:双击属性表跳转目标要素并闪烁
|
Java Android开发
移动应用程序设计基础——点菜单列表实现
进一步理解Android各种控件的使用,加深控件的属性、方法的使用,熟练掌握ListView控件的使用,熟练掌握对话框的使用。 实现点菜单列表 1.1布局结构 列表布局分为两大部分,上半部分显示列表内容,底部显示所有菜品的总价; 菜品项如图所示包括 1.图片,图片格式120*120; 2.标题,居中,android:textAppearance="?android:attr/textAppearanceLarge", 3.菜品介绍内容,最多显示3行,超过部分用…表示,android:textAppearan
155 0
移动应用程序设计基础——点菜单列表实现
AppleWatch开发入门五——菜单控件的使用
AppleWatch开发入门五——菜单控件的使用
173 0
AppleWatch开发入门五——菜单控件的使用
|
SQL JavaScript 前端开发
【自然框架】之鼠标点功能现(二):表单控件的“应用”—— 代码?只写需要的!
  【自然框架】之鼠标点功能现(一):单表的增删改查(即上次5月23日活动的一个主题)【Demo、源码下载】           看了大家的回复,好像不少人误会了,我为了突出“鼠标点,功能现”,所以没有说代码,没有贴代码,这就让一些人认为我想要完全抛弃VS,自己写一个“平台”来代替,不好意思,您高估我了,我可达不到。
843 0
|
Web App开发
艾伟:WinForm控件开发总结(七)-----为复杂属性的子属性提供编辑功能
前面的几篇文章中,我们给控件添加一个复杂的类型Scope,并且给它的类型提供的一个类型转换器,现在我们可以在属性浏览器中编辑它的值,并且它的值也被串行化的源代码里了。但是你有没有发现,在属性浏览器里编辑这个属性的值还是不太方便。
666 0