Note that there are some explanatory texts on larger screens.

plurals
  1. POHow can I detect when the DataGridView.RowValidating event is from a user interaction vs BindingSource list change?
    primarykey
    data
    text
    <p>I have finally tracked down a bug that I've been working on for the entire weekend, however I don't see a way to really solve it - in a clean way, that is.</p> <p>The situation is that I have a DGV control that is bound to a List of business objects. One of the columns is NOT bound to the DataSource and I have wired up the CellParsing and CellFormatting events to handle the persistence of the data for this cell. I've basically circumvented the .net databinding and implemented my own poor version. This was not intentional, it's REALLY old code that had complicated parsing and formatting requirements and I incorrectly implemented a solution based on an unbound column. I now know the correct way to handle this and have since fixed my code, however I'd still like to know how I could have solved the bug another way.</p> <p>I handle the RowValidating event and do one final validation on the row as a whole to ensure everything is cool. Of course if there is an issue I Cancel the validation and the row is not committed. This all works fine when the user is editing and adding rows through UI interaction, but creates a problem when the DataSource is set. The issue <strong>appears</strong> to be that CellFormatting is not called when the DGV updates it's internal list and builds the rows, or at least it's not called before the validating event is fired. This results in the RowValidating handler pulling a null value from the Unbound column (because CellFormatting hasn't been called and set the value yet).</p> <p>I was refreshing my knowledge on the DGV and thought that handling the CellValueNeeded event may be the ticket but setting DataGridViewCellValueEventArgs.Value didn't trigger the CellFormatting event like I hoped it would.</p> <p>I've been thinking about how to handle this situation and the only thing I have come up with is to detect when the validation is triggered from a UI event rather than the initial binding or bound list change. Not only is this a hacky solution, but don't see how it could be done.</p> <p>I've created a complete example application that will illustrate the problem. I'd be really curious to see how some of you would solve a problem like this. It's likely that there is major design smell here.</p> <pre><code>using System; using System.Collections.Generic; using System.Windows.Forms; public class Form1 : Form { private List&lt;DomainModel&gt; _sampleData; public Form1() { InitializeComponent(); _sampleData = new List&lt;DomainModel&gt;(); _sampleData.Add(new DomainModel("Widget A")); _sampleData.Add(new DomainModel("Widget B")); } private void button1_Click(object sender, EventArgs e) { Console.WriteLine("Setting DataSource"); domainModelBindingSource.DataSource = _sampleData; } private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { Console.WriteLine("CellFormatting fired for {0},{1}", e.RowIndex, e.ColumnIndex); if (e.ColumnIndex != 0 &amp;&amp; !dataGridView1.Rows[e.RowIndex].IsNewRow) { var model = domainModelBindingSource[e.RowIndex] as DomainModel; e.Value = model.Name; e.FormattingApplied = true; } } private void dataGridView1_CellParsing(object sender, DataGridViewCellParsingEventArgs e) { if (e.ColumnIndex == 1) { e.Value = e.Value.ToString(); e.ParsingApplied = true; } } private void dataGridView1_RowValidating(object sender, DataGridViewCellCancelEventArgs e) { if (dataGridView1.Rows[e.RowIndex].IsNewRow) return; object value = dataGridView1[1, e.RowIndex].Value; if (value == null || String.IsNullOrEmpty(value.ToString())) e.Cancel = true; } #region Designer stuff private System.ComponentModel.IContainer components = null; protected override void Dispose(bool disposing) { if (disposing &amp;&amp; (components != null)) components.Dispose(); base.Dispose(disposing); } #region Windows Form Designer generated code private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.dataGridView1 = new System.Windows.Forms.DataGridView(); this.nameDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.Column1 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.domainModelBindingSource = new System.Windows.Forms.BindingSource(this.components); this.button1 = new System.Windows.Forms.Button(); ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.domainModelBindingSource)).BeginInit(); this.SuspendLayout(); // // dataGridView1 // this.dataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.dataGridView1.AutoGenerateColumns = false; this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.nameDataGridViewTextBoxColumn, this.Column1}); this.dataGridView1.DataSource = this.domainModelBindingSource; this.dataGridView1.Location = new System.Drawing.Point(12, 41); this.dataGridView1.Name = "dataGridView1"; this.dataGridView1.Size = new System.Drawing.Size(437, 161); this.dataGridView1.TabIndex = 0; this.dataGridView1.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.dataGridView1_CellFormatting); this.dataGridView1.CellParsing += new System.Windows.Forms.DataGridViewCellParsingEventHandler(this.dataGridView1_CellParsing); this.dataGridView1.RowValidating += new System.Windows.Forms.DataGridViewCellCancelEventHandler(this.dataGridView1_RowValidating); // // nameDataGridViewTextBoxColumn // this.nameDataGridViewTextBoxColumn.DataPropertyName = "Name"; this.nameDataGridViewTextBoxColumn.HeaderText = "Name"; this.nameDataGridViewTextBoxColumn.Name = "nameDataGridViewTextBoxColumn"; // // Column1 // this.Column1.HeaderText = "Data (unbound)"; this.Column1.Name = "Column1"; // // domainModelBindingSource // this.domainModelBindingSource.DataSource = typeof(DomainModel); // // button1 // this.button1.Location = new System.Drawing.Point(12, 12); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(168, 23); this.button1.TabIndex = 1; this.button1.Text = "Update Data Source"; this.button1.UseVisualStyleBackColor = true; this.button1.Click += new System.EventHandler(this.button1_Click); // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(461, 214); this.Controls.Add(this.button1); this.Controls.Add(this.dataGridView1); this.Name = "Form1"; this.Text = "Form1"; ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.domainModelBindingSource)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.DataGridView dataGridView1; private System.Windows.Forms.BindingSource domainModelBindingSource; private System.Windows.Forms.Button button1; private System.Windows.Forms.DataGridViewTextBoxColumn nameDataGridViewTextBoxColumn; private System.Windows.Forms.DataGridViewTextBoxColumn Column1; #endregion } internal sealed class DomainModel { public DomainModel() { } public DomainModel(string name) { this.Name = name; } public string Name { get; set; } } static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload