【C#】让DataGridView输入中实时更新数据源中的计算列
本文适用Winform开发,且DataGridView的数据源为DataTable/DataView的情况。
理解前提:熟知DataTable、DataView
求:更好方案
考虑这样一个场景:
某DataTable(下称dt)的B列是计算列(设置了Expression属性),是根据A列的数据计算而来,该dt被绑定到某个DataGridView(下称dgv),A、B两列都要在dgv中显示,其中A列可编辑(ReadOnly=false)。需求是对A列进行编辑时(输入或删除),B列能实时变化。例如下面的例子:

【目标文件名】是根据【款号】和【色号】计算而来(连接字符串),当编辑款号/色号时,目标文件名能实时变化。
熟悉dgv的猿友都知道,如果不做特别处理,是达不到上述效果的。原因是dgv默认是等焦点离开编辑单元格(CurrentCell),才会提交更改到数据源,而且就算焦点离开,但如果焦点仍在同一行(即CurrentCell改变,但CurrentRow没变)的话,该行的源行也仍然处在编辑状态(DataRowView.IsEdit为true),计算列也同样不会更新。非得是焦点离开这一行(去到别的行,或者其它控件),计算列才会更新。——这段话信息量略大,不熟悉dgv提交机制的猿友可能得借助下面进一步的说明才能明白~老鸟请绕道。先认识几个概念:
- dgv单元格:DataGridViewCell
- dgv行:DataGridViewRow
- dgv行的源行:DataRowView。当dgv绑定数据源后,它的每一行就对应了数据源中的一行(或叫一项),这就是我所谓的【源行】。可以通过DataGridViewRow.DataBoundItem属性获得,该属性类型是object,当dgv的数据源为DataTable或DataView(下称dv)时,DataBoundItem的真实类型就是DataRowView,可以理解为DataView的行。而dv又是根据dt来的,所以dv背后又对应一个dt,所以DataRowView背后也对应一个DataRow,可通过DataRowView.Row获得该DataRow。简单表示就是,DataGridViewRow(访问DataBoundItem属性)→DataRowView(访问Row属性)→DataRow
- dgv有单元格的概念和实体类(DataGridViewCell),但dt和dv没有,后者只到行这一级,虽然可以通过DataRow[x]或DataRowView[x]访问单元格的值,但在类层级上并不存在DataCell这样的表示单元格的实体类,也就是dt和dv的编辑/提交等操作是以【行】为单元
下面是dgv的常规提交流程:
①编辑dgv单元格→②完成编辑(离开焦点)→③提交数据源(源行仍处于编辑状态)→④焦点离开dgv行→⑤源行结束编辑状态→⑥源行更新计算列(其实完整流程还包括别的环节,比如单元格数据验证,但这里只说与提交直接相关的环节)。
可以看到,计算列得到更新的关键有两处:
- dgv单元格的数据要提交到数据源相应单元格
- 源行结束编辑状态
按常规提交流程,必须使焦点离开单元格所在的行(只离开单元格都不行哦)才能达到目的,而我们的需求是,编辑的过程中就要实时更新,不要说离开行,连单元格都不想离开。
一、解决实时更新计算列的问题
可以通过dgv的CurrentCellDirtyStateChanged事件达到目的:
private void dgv_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
//判断当前单元格是否存在未提交的更改,只有存在才继续。
//此判断有必要,因为下面的dgv.CommitEdit也会触发该事件,但此时IsCurrentCellDirty已为false,
//如果不做判断,将会重复进入,造成无谓消耗
if (dgv.IsCurrentCellDirty)
{
//将单元格值提交给数据源,dgv.EndEdit()也能做到提交,但那样会使单元格结束编辑状态
//而dgv.CommitEdit()则会保持编辑状态
//参数是提供给DataError等事件的原因
dgv.CommitEdit(DataGridViewDataErrorContexts.Commit); //人工结束源行的编辑状态。只有这样,源行的计算列才会更新
(dgv.CurrentRow.DataBoundItem as DataRowView).EndEdit(); //或者执行DataRow的EndEdit()也能达到同样目的
//(dgv.CurrentRow.DataBoundItem as DataRowView).Row.EndEdit();
}
}
通过这个事件做了上面要做的两个事,即①将dgv单元格值更新到数据源;②结束源行编辑状态。按说到这里就搞掂了,事实上也的确能使计算列实时反映输入,但却存在另一个体验层面的问题,就是单元格会在每次键入后内容全选,如图:

也就是如果要连续输入,必须在每次输入后用鼠标或方向键取消全选并将光标定位到正确的位置~这不蛋疼吗,必须解决!首先为什么会全选的原因不明,我猜是由于数据源的更新反过来影响dgv所致。尝试过用CellEnter、CellBeginEdit、EditingControlShowing、dgv.EditingControl等东西都不理想,不是根本没用,就是输入焦点不对,总之着实折腾了一番,最后总算另辟蹊径,完美解决。
二、解决键入后自动全选的问题
我是从控件消息这块打的主意,dgv的单元格实际上承载了某种编辑控件(如TextBox,CheckBox),所以甭管它是什么原因全选,最后总该是收到了什么消息它才全选,那么我就用spy++截获消息,果然有发现:

粗略一看,是EM_SETSEL,经过了解,就是EM_SETSEL,所以接下来要做的就是自定义一个文本编辑控件,让它忽略这个消息,完了让这个控件成为dgv单元格中的文本编辑控件。了解一番,有如下套路:
- 编写承载控件。需继承基础控件,并实现System.Windows.Forms.IDataGridViewEditingControl接口。由于我只是想屏蔽现有控件的某个消息,并不是要从头编写功能控件,所以直接继承DataGridViewCell承载的文本框控件DataGridViewTextBoxEditingControl即可,因为该控件已经实现上述接口:
public class DataGridViewTextBoxUnSelectableEditingControl : DataGridViewTextBoxEditingControl
{
protected override void WndProc(ref Message m)
{
//EM_SETSEL消息的常量是0xb1
if (m.Msg == 0xb1) { return; } base.WndProc(ref m);
}
} - 编写承载上述控件的DataGridViewCell。需继承自DataGridViewCell或其子类。同样,本例我只需继承自DataGridViewTextBoxCell即可:
public class DataGridViewTextBoxUnSelectableCell : DataGridViewTextBoxCell
{
//仅需重写该属性,指明承载的控件类型即可
public override Type EditType
{
get
{
return typeof(DataGridViewTextBoxUnSelectableEditingControl);
}
}
} - 设置要使用上述单元格的dgv列(DataGridViewColumn)的CellTemplate属性,为上述单元格的实例,多个列可以设为同一实例。CellTemplate最好尽早设置,比如在窗体构造函数中,紧跟InitializeComponent()方法设置;
InitializeComponent(); var cell = new DataGridViewTextBoxUnSelectableCell();
dgv.Columns[].CellTemplate = cell;//将要使用特殊单元格的列的CellTemplate指定为单元格实例
dgv.Columns[].CellTemplate = cell;//多个列可以共用一个实例
...
对于本例而言,做完上述工作即可解决dgv单元格全选的问题。完整的自定义单元格控件的套路请自行参考MSDN。
应猿友要求,放上demo:http://pan.baidu.com/s/1qWzKf60
-文毕-
【C#】让DataGridView输入中实时更新数据源中的计算列的更多相关文章
- iOS UITextView 输入内容实时更新cell的高度
iOS UITextView 输入内容实时更新cell的高度 2014-12-26 11:37 编辑: suiling 分类:iOS开发 来源:Vito Zhang'blog 11 4741 UIT ...
- MFC For循环中实时更新显示Edit内容
在for(){}循环中如果有处理函数,然后需要显示的时候,简单的UpdateData(false);是不行的: for (int i=0;i<10000;i++) { m_nT1.Format( ...
- 安卓端网页浏览过程中实时更新title的web实现
$(function () { var scrollTop = 0, //缓存上一次触发scroll的时候的scrollTop值 appendIndex = 0, //由于第23行append这个操作 ...
- Dev GridControl数据修改后实时更新数据源
1: /// <summary> 2: /// 嵌入的ComboxEdit控件选择值变化事件 3: /// </summary> 4: /// <param n ...
- Dev GridControl数据修改后实时更新数据源(转)
1: /// <summary> 2: /// 嵌入的ComboxEdit控件选择值变化事件 3: /// </summary> 4: /// <param nam ...
- WebSocket 实时更新mysql数据到页面
使用websocket的初衷是,要实时更新mysql中的报警信息到web页面显示 没怎么碰过web,代码写的是真烂,不过也算是功能实现了,放在这里也是鞭策自己,web也要多下些功夫 准备 引入依赖 & ...
- SqlServer中计算列详解
计算列区别于需要我们手动或者程序给予赋值的列,它的值来源于该表中其它列的计算值.比如,一个表中包含有数量列Number与单价列Price,我们就可以创建计算列金额Amount来表示数量*单价的结果值, ...
- 【SqlServer】SqlServer中的计算列
计算列区别于需要我们手动或者程序给予赋值的列,它的值来源于该表中其它列的计算值.比如,一个表中包含有数量列Number与单价列Price,我们就可以创建计算列金额Amount来表示数量*单价的结果值, ...
- python---django中form组件(2)自定制属性以及表单的各种验证,以及数据源的实时更新,以及和数据库关联使用ModelForm和元类
自定义属性以及各种验证 分析widget: class TestForm(forms.Form): user = fields.CharField( required = True, widget = ...
随机推荐
- Android多线程分析之四:MessageQueue的实现
Android多线程分析之四:MessageQueue的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处 在前面两篇文章<Androi ...
- js模版引擎handlebars.js实用教程——结束语
返回目录 有了这些功能,[ajax+json+Handlebars]替代[vo+el表达式]不成问题,新时代的曙光已经来临,最佳解决方案在此,您还等什么? 教程到此结束...祝读者学习愉快... 小菜 ...
- InputStream与InputStreamReader的区别
InputStream是字节流,多用于读取二进制数据 InputStreamReader是字符流,多用于读取文本文件.有不同的编码方式,如utf8等.可以在构造的时候指定编码方式. 例如,两者都有一个 ...
- 在AWS中创建NAT节点
NAT, Network Address Translation,即网络地址转换.当内部网络的主机想要访问外网,但是又不想直接暴露给公网,可以通过NAT节点来访问外网.这样做有两个好处,第一是内网的主 ...
- CSS水平垂直居中的几种方法
直接进入主题! 一.脱离文档流元素的居中 方法一:margin:auto法 CSS代码: div{ width: 400px; height: 400px; position: relative; b ...
- 爱上MVC~在Views的多级文件夹
回到目录 在MVC里,你的控制器对应的视图一般是在Views目录,而如果希望在Views里再分几个模块文件夹默认是不允许的,我们需要做一下设置,就可以实现Views下的多次文件夹层次了,例如,我们有产 ...
- salesforce 零基础学习(十八)WorkFlow介绍及用法
说起workflow大家肯定都不陌生,这里简单介绍一下salesforce中什么情况下使用workflow. 当你分配许多任务,定期发送电子邮件,记录修改时,可以通过自动配置workflow来完成以上 ...
- java 线程协作 wait(等待)与 notiy(通知)
一.wait().notify()和notifyAll() 为了更好的支持多线程之间的协作,JDK提供了三个重要的本地方法 //调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对 ...
- Jenkins的系统设置
1.登录Jenkins进入以下界面: 2.点击 系统管理 3.点击 系统设置 (下图的系统设置是已经设置好的) 4.这里设置主要设置 Jenkins URL (这里我是测试的所以使用默认的)与 邮件的 ...
- CSS3新技能学习笔记
说来惭愧自认为对css了解,但在项目中却很少有正确的使用css,如果面向对象的css吧,其实也不是不想用而是css天生就是面向对象的,高度可重用,但是如果把每个都单独提取,难免会有过多的class以及 ...