views:

680

answers:

3

I have a problem when assigning the DataSource propery on a DataGridView control. My DataSource is a DataTable's DefaultView and, as expected, columns are automatically created in the DataGridView to match those in the DataTable when I assign it.

What happens next is that the columns seem to be automatically removed and recreated a further 2 times by the DataGridView. Why would this happen?

In a form's constructor:

//A DataTable is created with 5 columns
//The DataTable is populated with some rows.

myDgv.AutoGenerateColumns = true;
myDgv.DataSource = myDataTable.DefaultView;
// myDgv.ColumnAdded event is fired 5 times.
// WHY: myDgv.ColumnRemoved event is fired 5 times.
// WHY: myDgv.ColumnAdded event is fired 5 times.
// WHY: myDgv.ColumnRemoved event is fired 5 times.
// WHY: myDgv.ColumnAdded event is fired 5 times.


Edit: Added a (hopefully) self contained example. If I set breakpoints in the event handlers, I hit the 'Added' one 6 times and the 'Removed' one 4 times. The DataTable contains 2 columns and I never ask for any columns to be removed in my code.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace asdasdgf
{
    public class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            DataTable dt = new DataTable();
            dt.Columns.Add("Col1", typeof(int));
            dt.Columns.Add("Col2", typeof(string));

            foreach (int i in Enumerable.Range(0, 10))
            {
                var row = dt.NewRow();
                row["Col1"] = i;
                row["Col2"] = "stackoverflow";
                dt.Rows.Add(row);
            }

            dataGridView1.ColumnAdded += new DataGridViewColumnEventHandler(dataGridView1_ColumnAdded);
            dataGridView1.ColumnRemoved += new DataGridViewColumnEventHandler(dataGridView1_ColumnRemoved);

            dataGridView1.DataSource = dt.DefaultView;
        }

        void dataGridView1_ColumnRemoved(object sender, DataGridViewColumnEventArgs e)
        {
            // Break here
        }

        void dataGridView1_ColumnAdded(object sender, DataGridViewColumnEventArgs e)
        {
            // Break here
        }

        // Form1.Designer.cs contents:
        #region Windows Form Designer generated code
        private System.ComponentModel.IContainer components = null;
        private System.Windows.Forms.DataGridView dataGridView1;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }



        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.dataGridView1 = new System.Windows.Forms.DataGridView();
            ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
            this.SuspendLayout();
            // 
            // dataGridView1
            // 
            this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            this.dataGridView1.Location = new System.Drawing.Point(12, 41);
            this.dataGridView1.Name = "dataGridView1";
            this.dataGridView1.Size = new System.Drawing.Size(240, 150);
            this.dataGridView1.TabIndex = 0;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(284, 264);
            this.Controls.Add(this.dataGridView1);
            this.Name = "Form1";
            this.Text = "Form1";
            ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
            this.ResumeLayout(false);

        }

        #endregion
    }
}
+1  A: 

I have an app that uses a DataGridView to display the contents of a DataTable, but I link them like this:

dataGridView.DataSource = dataTable;

Can you try this?

EDIT:

I also have the following in the auto generated code:

this.dataGridView.EditMode = System.Windows.Forms.DataGridViewEditMode.EditProgrammatically;

Not sure why this would make a difference though.

ChrisF
The same thing happens. Either I'm doing something fundamentally wrong, or there's something weird going on here - there's hardly any code in the example in the question and it still happens
frou
+1  A: 

A few months ago at work, we were building a Grid control that would have built in filtering capabilities (similar to excel). Initially we used a datagridview and built around that. This issue you're brining up now was the number one biggest thorn in our side! Since we were using a DGV underneath, and hooking into its events to trigger various things we needed to do, it was an absolute nightmare. The DGV is a great control, but under the covers it does some wonky sh**!! The best workaround at the end of the day was to auto generate the columns ourselves. Yea, it was a pain but at least we had full control.

In our final version though, we ended up scratching that all together, and went with the inheritance route. We inherited from the DGV and it made our lives ALOT easier. Now, I'm not sure what you're trying to accomplish here, but if you're building your own grid, try inheritance first! As for the answer to your question, you're not doing anything wrong. The datagridview is just kooky like that. If you're not building a control around the DGV, and just need those events, I say try to stay away from column added / removed. See if you can instead use bindingcompleted instead.

BFree
Thanks for the advice. I think it's been coaxed in to working (see above), but I'll keep in mind that it can be a bit haywire!
frou
+1  A: 

After a lot of fiddling, this seems to be the way to go.

In your form's Load event handler:

dgv.AutoGenerateColumns = false;
dgv.DataSource = myDataSource;

In your form's Shown event handler:

dgv.AutoGenerateColumns = true;

Giving the desired result of the ColumnAdded event being fired once for each column, and the ColumnRemoved event not being fired at all. (If AutoGenerateColumns = true is set in the Load event handler, it will do this annoying Add-Remove-Add dance.)

frou