4

I have a DataTable which is populated from a CSV file then, using a DataGridView the data is edited in memory. As far as I understand the programmatic editing of the data should be done on the DataTable where the user editing is done via. the DataGridView.

However when I add columns programmatically to the DataTable, it is not reflected automatically in the DataGridView and I suspect the converse is also true.

How do you keep the two concurrent? I thought the idea of data binding was that this was automatic...

Also adding rows works fine.


SOLVED:

The AutoGeneratedColumns was set to false in the designer code despite being true in the properties dialog (and set explicitly in the code). The initial columns were generated programmatically and so should not have appeared however this was not picked up on since the designer code also continued to generate 'designed in' columns that were originally used for debugging.

Moral: Check the autogenerated code!

In addition to this, see this post and this post

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
Brendan
  • 18,771
  • 17
  • 83
  • 114

4 Answers4

2

This doesn't sound right. To test it out, I wrote a simple app, that creates a DataTable and adds some data to it.
On the button1.Click it binds the table to the DataGridView.
Then, I added a second button, which when clicked, adds another column to the underlying DataTable.
When I tested it, and I clicked the second button, the grid immedialtey reflected the update.

To test the reverse, I added a third button which pops up a dialog with a DataGridView that gets bound to the same DataTable. At runtime, I then added some values to the first DataGridView, and when I clicked the button to bring up the dialog, the changes were reflected.

My point is, they are supposed to stay concurrent. Mark may be right when he suggested you check if AutoGenerateColumns is set to true. You don't need to call DataBind though, that's only for a DataGridView on the web. Maybe you can post of what you're doing, because this SHOULD work.

How I tested it:

DataTable table = new DataTable();
public Form1()
{
    InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
    table.Columns.Add("Name");
    table.Columns.Add("Age", typeof(int));
    table.Rows.Add("Alex", 26);
    table.Rows.Add("Jim", 36);
    table.Rows.Add("Bob", 34);
    table.Rows.Add("Mike", 47);
    table.Rows.Add("Joe", 61);

    this.dataGridView1.DataSource = table;
}

private void button2_Click(object sender, EventArgs e)
{
    table.Columns.Add("Height", typeof(int));
    foreach (DataRow row in table.Rows)
    {
        row["Height"] = 100;
    }
}

private void button3_Click(object sender, EventArgs e)
{
    GridViewer g = new GridViewer { DataSource = table };
    g.ShowDialog();
}

public partial class GridViewer : Form //just has a DataGridView on it
{
    public GridViewer()
    {
    InitializeComponent();
    }

    public object DataSource
    {
        get { return this.dataGridView1.DataSource; }
        set { this.dataGridView1.DataSource = value; }
    }
}
Jimi
  • 29,621
  • 8
  • 43
  • 61
BFree
  • 102,548
  • 21
  • 159
  • 201
  • In particular, check the AutoGenerateColumns value in the designer code. Answer selected since the example helped a great deal in debugging! – Brendan Jan 31 '09 at 16:29
1

Here is another solution which you dont need to assign a "name" to the data grid, so that the data grid can be a template element.

(In my case, data grid is a template element inside TabControl.ContentTemplate)

The key to show the new column (added programmatically after the initial binding) is forcing the datagrid to refresh. From the answer in Force WPF DataGrid to regenerate itself, Andres suggested setting AutoGenerateColumns from false to true will force datagrid to refresh.

Which means I simply need to:

  1. Bind the AutoGenerateColumns to a property of my object
  2. Set the propery to false before adding column
  3. Set the propery to true after added column.

Here is the code:

XAML:

<DataGrid AutoGenerateColumns="{Binding toggleToRefresh}"
          ItemsSource="{Binding dataTable}"
          />

C#:

 public class MyTabItem : ObservableObject 
 {
        private DataTable _dataTable = new DataTable();
        public DataTable dataTable
        {
            get { return _dataTable; }
        }

        private bool _toggleToRefresh = true;
        public bool toggleToRefresh
        {
            get { return _toggleToRefresh; }
            set
            {
                if (_toggleToRefresh != value)
                {
                    _toggleToRefresh = value;
                    RaisePropertyChanged("toggleToRefresh");
                }
            }
        }

        public void addDTColumn()
        {
            toggleToRefresh = false;
            string newColumnName = "x" + dataTable.Columns.Count.ToString();
            dataTable.Columns.Add(newColumnName, typeof(double));
            foreach (DataRow row in dataTable.Rows)
            {
                row[newColumnName] = 0.0;
            }
            toggleToRefresh = true;
        }

        public void addDTRow()
        {
            var row = dataTable.NewRow();
            foreach (DataColumn col in dataTable.Columns)
            {
                row[col.ColumnName] = 0.0;
            }
            dataTable.Rows.Add(row);
        }
 }

Hope this help :)

Community
  • 1
  • 1
LennonLam
  • 215
  • 1
  • 2
  • 10
1

Is AutoGenerateColumns set to true? If you are making changes after the initial Binding you'll also have to call DataBind() to rebind the changed datasource. I know this is true for an AJAX callback, I think it is true for a WinForms control PostBack.

Mark Brittingham
  • 28,545
  • 12
  • 80
  • 110
-1

I had the same issue and I issued a DataBind(). It's not the silver bullet for everything, but it's what helped me in a few cases. I had to put it in before capturing information through a DataView, after the EditCommand and UpdateCommand events immediately after the EditItemIndex statement,

protected void datalistUWSolutions_EditCommand(object source, DataListCommandEventArgs e)
{
  datalistUWSolutions.EditItemIndex = e.Item.ItemIndex;
  datalistUWSolutions.DataBind(); // refresh the grid.
}

and

protected void datalistUWSolutions_UpdateCommand(object source, DataListCommandEventArgs e)
{
  objDSSolutions.UpdateParameters["Name"].DefaultValue = ((Label)e.Item.FindControl("lblSolutionName")).Text;
  objDSSolutions.UpdateParameters["PriorityOrder"].DefaultValue = ((Label)e.Item.FindControl("lblOrder")).Text;
  objDSSolutions.UpdateParameters["Value"].DefaultValue = ((TextBox)e.Item.FindControl("txtSolutionValue")).Text;
  objDSSolutions.Update();
  datalistUWSolutions.EditItemIndex = -1; // Release the edited record
  datalistUWSolutions.DataBind();         // Redind the records for refesh the control
}
SnapJag
  • 797
  • 4
  • 14
  • The DataGridView that's part of System.Windows.Forms does not have a DataBind method. You're thinking of the GridView that's part of System.Web.UI.WebControls. The OP is talking about WinForms. – BFree Jan 28 '09 at 22:43