I created a little form with a TabControl on it and a combobox.

On the  first page i added a DataGridView with 2 columns, not bound to any data (data entered directly).

Also, for the grid i added an event handler for CellValidating in which i test if the data from the first row is numeric, if not i show a message box with an error and return e.Cancel = true so the DataGridView maintains focus.

Now, if i do the following :

1 - edit cell on first column and enter a wrong value (a text)

2 - click on TabControl to change to the other tab page, error is shown that the value is not correct, close the error

3 - modify the value to be correct (enter a number)

4 - change to any of the other cells in the DataGridView and try to edit them with the mouse (double click to edit), it just doesn't work.

The only solution to "fix" this is to change to the other tabPage on the TabControl and back, this way the DataGridView regains its ability to edit cells with the mouse.

Is there any solution to this ?

Note: If i don't show the message box in the CellValidating function then the problem doesn't occur. I know i could show an errormessage on the row of the column, but the application i'm writing requires that i display messageboxes for all errors (and i can't make the grid work differently from all other controls on the other dialogs just because of this bug).

So, is there any way to fix this ?

Here's my code (at least the part that relates to the problem)

  1. public Form1()
  2. {
  3. InitializeComponent();
  4.  
  5. // add some data to the grid so it's not empty
  6.  
  7. // first row
  8. DataGridViewRow row = new DataGridViewRow();
  9. DataGridViewTextBoxCell value = new DataGridViewTextBoxCell();
  10. DataGridViewTextBoxCell text = new DataGridViewTextBoxCell();
  11. value.Value = "12";
  12. text.Value = "some text";
  13.  
  14. row.Cells.Add(value);
  15. row.Cells.Add(text);
  16.  
  17. dataGridView1.Rows.Add(row);
  18.  
  19. // second row
  20. row = new DataGridViewRow();
  21. value = new DataGridViewTextBoxCell();
  22. text = new DataGridViewTextBoxCell();
  23. value.Value = "1000";
  24. text.Value = "another text";
  25.  
  26. row.Cells.Add(value);
  27. row.Cells.Add(text);
  28.  
  29. dataGridView1.Rows.Add(row);
  30. }
  31.  
  32. private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
  33. {
  34. // first row must be numeric value
  35. if (e.ColumnIndex == 0)
  36. {
  37. try
  38. {
  39. double x = Convert.ToDouble(e.FormattedValue);
  40. }
  41. catch (System.Exception ex)
  42. {
  43. MessageBox.Show("Error, value is not a double");
  44. // if we can't convert then bail out
  45. e.Cancel = true;
  46. }
  47. }
  48. }
  49.  
  50. ...
  51. private void InitializeComponent()
  52. {
  53. ...
  54. this.dataGridView1.CellValidating += new System.Windows.Forms.DataGridViewCellValidatingEventHandler(this.dataGridView1_CellValidating);
  55. ...
  56. }

该问题源自MSDN Bug

http://social.msdn.microsoft.com/Forums/windows/en-US/c3634471-49cb-4274-afa4-c4bcd184b52c/datagridview-in-tabcontrol-and-cellvalidating-lead-to-problems

如果觉得英文不爽,这有这个问题的中文版

http://bbs.csdn.net/topics/390492161?page=1#post-394804656

下面是我对这个问题的规避方法

首先确定问题是在切换页签引发的校验中,弹出弹窗,导致焦点失去,从而致使datagridview Cell无法再进入编辑模式。

所以可以通过在校验过程弹窗后,重新fouce。

  1. private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
  2. {if(e.ColumnIndex==-||e.RowIndex==-)
  3. {
  4. return;
  5. }
  6. if(string.IsNullOrEmpty(e.FormattedValue.ToString()))
  7. {
  8. return;
  9. }
  10. int tempValue = -;
  11. if(!int.TryParse(e.FormattedValue.ToString(),out tempValue))
  12. {
  13. MessageBox.Show("Erro Data.");
  14. dataGridView1.Focus();
  15. e.Cancel = true;
  16. dataGridView1.CancelEdit();
  17. }
  18. }

但是fouce后会导致tab页签发生切换,而不是我们想要的留在该页签(这里是因为焦点重获得,导致某个标志位值被修改,tabControl未进入留在本页签的代码分支),所以需要为tabControl写Deselecting事件来“要求停留在本页签”,这里需要一个标志位,来实现如果校验通过,那么页签正常切换,如果失败不能切换。

  1. private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
  2. {
  3. m_IsInvalid = false;
  4. if(e.ColumnIndex==-||e.RowIndex==-)
  5. {
  6. return;
  7. }
  8. if(string.IsNullOrEmpty(e.FormattedValue.ToString()))
  9. {
  10. return;
  11. }
  12. int tempValue = -;
  13. if(!int.TryParse(e.FormattedValue.ToString(),out tempValue))
  14. {
  15. MessageBox.Show("Erro Data.");
  16. dataGridView1.Focus();
  17. e.Cancel = true;
  18. m_IsInvalid = true;
  19. dataGridView1.CancelEdit();
  20. }
  21. }
  1. private void tabControl1_Deselecting(object sender, TabControlCancelEventArgs e)
  2. {
  3. if (e.TabPage == tabPage2)
  4. {
  5. if (m_IsInvalid)
  6. {
  7. e.Cancel = true;
  8. }
  9. }
  10. }
  1. private void dataGridView1_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
  2. {
  3. m_IsInvalid = false;
  4. if (e.ColumnIndex == - || e.RowIndex == -)
  5. {
  6. return;
  7. }
  8. MessageBox.Show("Erro Data.");
  9. dataGridView1.Focus();
  10. e.Cancel = true;
  11. m_IsInvalid = true;
  12. dataGridView1.CancelEdit();
  13. }

这样就规避了页签切换校验失败,导致无法点击鼠标编辑datagridview cell的问题。

当然,坑一般是连着的,datagridview的CausesValidation一旦设为true,那么就是一旦焦点失去,它就触发校验,这样用户体验感非常差

想想看,我表格填错了,想直接点cancel取消掉,都要各种弹窗,无法正常Cancel。所以我们一般做了这样的策略
就是如果是在表格内进行操作,那么焦点失去立即校验。如果是在表格外,那么取消这一坑爹设定。

  1. private void dataGridView1_MouseLeave(object sender, EventArgs e)
  2. {
  3. dataGridView1.CausesValidation = false;
  4. }
  5.  
  6. private void dataGridView1_MouseEnter(object sender, EventArgs e)
  7. {
  8. dataGridView1.CausesValidation = true;
  9. }

好,这样,我们的Cancel,关闭窗体的X,就能正常运作了。

但是,但是。。。。我们发现页签切换点击,也是在表格外,这样就会导致页签切换时,触发一个行校验后,再点击页签切换,就不会触发表格校验,同时因为我们之前设置的全局变量导致页签一直无法切换,也没有提示。

我们必须在点击页签切换时,将dataGridView1.CausesValidation从false重新赋值为true。很悲剧的是,这样的事件微软没有给出。所以必须要重写下tabControl控件

  1. public class CustomTabControl:TabControl
  2. {
  3. public event EventHandler<CancelEventArgs> PreClicked;
  4.  
  5. protected override void WndProc(ref Message m)
  6. {
  7. if (m.Msg == )
  8. {
  9. var cancelArg = new CancelEventArgs(false);
  10. OnPreClicked(cancelArg);
  11. if (cancelArg.Cancel)
  12. {
  13. return;
  14. }
  15. }
  16. base.WndProc(ref m);
  17. }
  18. private void OnPreClicked(CancelEventArgs e)
  19. {
  20. var handler = PreClicked;
  21. if(handler!=null)
  22. {
  23. handler(this,e );
  24. }
  25. }
  26. }

Form1窗体:

  1. private void tabControl1_PreClicked(object sender, CancelEventArgs e)
  2. {
  3. dataGridView1.CausesValidation = true;
  4. }

这样所有就能按照设计正常运行了。

这是Demo代码

DataGridView in TabControl and CellValidating lead to problems的更多相关文章

  1. The `XXXX` target overrides the `HEADER_SEARCH_PATHS` build setting defined in `Pods/Target Support Files/Pods-game-desktop/Pods-game-desktop.release.xcconfig'. This can lead to prob

    The `game-desktop [Release]` target overrides the `HEADER_SEARCH_PATHS` build setting defined in `Po ...

  2. DataGridView使用

    DataGridView控件概述 DataGridView 控件代码目录(Windows 窗体) 未绑定数据列 定义:可能想要显示并非来自数据源的一列数据,这种列称为未绑定列. 数据格式示例 如何:设 ...

  3. Android Lint Checks

    Android Lint Checks Here are the current list of checks that lint performs as of Android Studio 2.3 ...

  4. iOS cocoapods升级及问题

    安装 安装RubyCocoaPods基于Ruby语言开发而成,因此安装CocoaPods前需要安装Ruby环境.幸运的是Mac系统默认自带Ruby环境,如果没有请自行查找安装.检测是否安装Ruby:$ ...

  5. CocoaPods的使用及安装

    本文转自:http://www.jianshu.com/p/6e5c0f78200a 一.什么是CocoaPods CocoaPods是iOS项目的依赖管理工具,该项目源码在Github上管理.开发i ...

  6. android 官方文档 JNI TIPS

    文章地址  http://developer.android.com/training/articles/perf-jni.html JNI Tips JNI is the Java Native I ...

  7. iOS 如何在一个已经存在多个project的workspace中引入cocoapods管理第三方类库

    一种新的第三方库管理工具:Carthage 如何使用Carthage管理iOS依赖库 Podfile Syntax Reference v1.1.0.rc.3 https://guides.cocoa ...

  8. cocoapod集成失败,无法找到头文件的解决办法

    在终端更新pod的时候,提示警告: target overrides the `OTHER_LDFLAGS` build setting defined in `Pods/Target Support ...

  9. ubuntu-利用pdnsd-TCP方式获取IP-拒绝DNS污染

    那,自从国内技术出现了DNS污染问题呢,时常导致很多国外网站访问不正常,所以通过参考一些博客所属避免DNS污染的方法,决定搭建一个Ubuntu JeOS下的DNS缓存服务器,该服务器利用TCP方式获取 ...

随机推荐

  1. 巧用vsprintf将浮点数等转化字符串

    直接上代码 #include <stdarg.h> ]; int vspf(char *fmt, ...) { va_list argptr; int cnt; va_start(argp ...

  2. How to Programmatically Impersonate Users in SharePoint

      Sometimes when creating SharePoint web or console applications, you may need to execute specific c ...

  3. RabbitMQ消息队列1: Detailed Introduction 详细介绍

    1. 历史 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然在同步消息通讯的世界里有 ...

  4. 构建winform控件数据缓存器

    DataBindingHelper使用手册 1.引用Rabbit.Core.dll文件 也就是我自己编写的功能库Rabbit.Core.dll呵呵. Rabbi.Core.DLL密码:dgqv     ...

  5. 使用UltraISO制作U盘启动盘——转载

    现在流行用U盘来安装系统,但要用U盘来安装系统的前提条件下是如何将镜像文件写入到U盘里,UltraISO能很好的满足你的需求. 步骤/方法  鼠标右键“以管理员身份运行”UltraISO图标    打 ...

  6. springmvc spring mybatis插入mysql中文乱码

    springmvc 插入mysql数据库中文乱码问题: 1.将页面中的编码改成utf-8 2.用SQLyog右击->改变数据库 以上两步可以保证页面数据编码一致 3.在mybatis连接的地方加 ...

  7. QT中的C/S通信问题:关于服务器端readyread()信号不发射

    在windows下用QT测试C/S通信的时候,遇到服务器端不能读取客户端发来的数据的问题. 后来设置断点检查错误,发现是readyread信号不触发的原因. 后来在客户端写socket后面加了一句so ...

  8. AJAX避免服务器调用上个页面缓存的办法

    GET 请求 一个简单的 GET 请求: xmlhttp.open("GET","demo_get.asp",true); xmlhttp.send(); 亲自 ...

  9. 错误提示sudo: no tty present and no askpass program specified Sorry, try again.

    php调用shell脚本的svnup.php文件内容: <?set_time_limit(0);//$output = array();$ret = 0;exec("/usr/bin/ ...

  10. Altium Designer15 卡在登陆界面解决办法:

    Altium Designer15 卡在登陆界面解决办法: 在我的电脑系统盘中找到下面目录(注:如果看不到,需要取消隐藏文件选项.) C:\Documents and Settings\Adminis ...