将列添加到绑定到DataGridView的DataTable不会更新视图

时间:2009-01-28 21:19:01

标签: c# winforms visual-studio-2008 datagridview datatable

我有一个从CSV文件填充的DataTable,然后使用DataGridView在内存中编辑数据。据我所知,数据的编程编辑应该在用户编辑完成的DataTable上完成。 DataGridView。

但是,当我以编程方式将列添加到DataTable时,它不会自动反映在DataGridView中,我怀疑反过来也是如此。

你如何保持这两个并发?我认为数据绑定的想法是这是自动的......

以下是相关的设置代码 - WorksheetGridView子类DataGridView

// Can access data directly
public DataTable data = new DataTable();

public WorksheetGridView()
{
    InitializeComponent();

    // Allow copying from table to clipboard
    this.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableWithoutHeaderText;

    // TODO: how to allow both row and column selects?
    //this.SelectionMode = DataGridViewSelectionMode.ColumnHeaderSelect;

    // Load up a blank DataTable to hold user inputted data
    int i;
    const int numBlankRows = Config.Application.DefaultNumRows;
    const int numBlankCols = Config.Application.DefaultNumCols;
    // TODO: Figure out how to include this as a config variable
    DBNull dfltCellContent = DBNull.Value;
    DataRow tmpRow;
    // Add columns - i used for naming
    for (i = 0; i < numBlankCols; i++)
    {
        this.AddColumn(i);
    }

    // Add rows
    for (i = 0; i < numBlankRows; i++)
    {
       tmpRow = this.data.NewRow();
       // Fill cells with something (i.e. blank cells)
       foreach (DataColumn col in this.data.Columns)
       {
           tmpRow[col.ColumnName] = DBNull.Value;
       }
       this.data.Rows.Add(tmpRow);
    }
    // Link data to the view
    this.DataSource = this.data;
}

private void AddColumn(int colIndex)
{
    // Adds a column to the data array
    DataColumn tmpCol;
    tmpCol = new DataColumn();
    tmpCol.DataType = Type.GetType(Config.Application.DataType);
    tmpCol.ColumnName = "C" + colIndex.ToString();
    tmpCol.ReadOnly = false;
    tmpCol.Unique = false;
    tmpCol.AllowDBNull = true;
    this.data.Columns.Add(tmpCol);
}

稍后我在此代码中调用AddColumn来处理粘贴制表符分隔数据时,该位不起作用

public void PasteToCells(string mode)
{
    // TODO: Write paste code
    int i;
    if (Clipboard.ContainsText())
    {
        string clipBoardContent = Clipboard.GetText();
        using (CsvReader pastedCsvReader = new CsvReader(
            new StringReader(clipBoardContent), false, '\t'))
        {
            // TODO: If more rows/cols than in data table then expand accordingly
            int numPastedCols = pastedCsvReader.FieldCount;
            int currRowIndex, currColumnIndex;
            GetSelectedInsertPoint(out currRowIndex, out currColumnIndex);
            // Make space for columns if needed
            if (mode == "insertcols")
            {
                int baseIndex = this.data.Columns.Count;
                for (i = 0; i < numPastedCols; i++)
                {
                    //this.Columns.Add
                    // TODO: Doesn't work yet - do you edit the DataGridView and reflect to DataTable or vise-versa?
                    this.AddColumn(baseIndex + i);
                }
            }
            while  (pastedCsvReader.ReadNextRecord()) 
            {
                // Make space for rows (row by row) if needed
                if (mode == "insertrows")
                {
                    this.data.NewRow();
                    // TODO: Add a row
                }
                // Populate the cells with valid pasted text
                for (i = 0; i < numPastedCols; i++)
                {
                    this.data.Rows[currRowIndex][currColumnIndex + i] = ConvertCellContent(pastedCsvReader[i]);
                }
                currRowIndex = currRowIndex + 1;
            }
        }
    }
    else
    {
        // TODO: Do nothing?
        Console.WriteLine("No text on clipboard");
    }
}

我尝试了下面给出的示例,它确实有效。但是,当我尝试这样做时,水平滚动条会短暂​​地展开和收缩,但子类DataGridView中的表格不会更新。
但是,该列位于DataTable中 - 例如,我无法添加同名的列,列数也反映了额外的列。是否有一个设计师选项可能会限制DataGridView

的更新

添加行也可以。

解决:

尽管在属性对话框中显示AutoGeneratedColumns(并在代码中明确设置),但false在设计器代码中设置为true。初始列是以编程方式生成的,因此不应该出现,因为设计器代码也继续生成最初用于调试的“设计”列。

道德:检查自动生成的代码!

除此之外,请参阅this postthis post

4 个答案:

答案 0 :(得分:2)

这听起来不对。为了测试它,我编写了一个简单的应用程序,它创建了一个DataTable并向其添加了一些数据 在button1.Click上,它将表绑定到DataGridView 然后,我添加了第二个按钮,单击该按钮时,会向基础DataTable添加另一列 当我测试它时,我点击了第二个按钮,网格immedialtey反映了更新。

为了测试反向,我添加了第三个按钮,弹出一个带有DataGridView的对话框,该对话框绑定到同一个DataTable。在运行时,我然后向第一个DataGridView添加了一些值,当我单击按钮以显示对话框时,反映了更改。

我的观点是,他们应该保持并发。当他建议您检查AutoGenerateColumns是否设置为true时,标记可能是正确的。您不需要调用DataBind,这仅适用于Web上的DataGridView。也许你可以发布你正在做的事情,因为这应该工作。

我是如何测试的:

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; }
    }
}

答案 1 :(得分:1)

AutoGenerateColumns是否设置为true?如果在初始绑定后进行更改,则还必须调用DataBind()来重新绑定更改的数据源。我知道这对于AJAX回调是正确的,我认为对于WinForms控件PostBack来说是真的。

答案 2 :(得分:1)

这是另一种解决方案,你不需要分配一个"名称"到数据网格,这样数据网格就可以成为模板元素。

(在我的例子中,数据网格是TabControl.ContentTemplate中的模板元素)

显示新列的键(在初始绑定后以编程方式添加)是强制数据网格刷新。 根据{{​​3}}中的答案,Andres建议将AutoGenerateColumns从false设置为true将强制数据网格刷新。

这意味着我只需要:

  1. AutoGenerateColumns绑定到我的对象的属性
  2. 在添加列
  3. 之前将属性设置为false
  4. 添加列后将属性设置为true。
  5. 以下是代码:

    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);
            }
     }
    

    希望这有帮助:)

答案 3 :(得分:-1)

我有同样的问题,我发布了一个DataBind()。它不是万能的灵丹妙药,但它在一些情况下帮助了我。在EditItemIndex语句之后的EditCommand和UpdateCommand事件之后,我必须通过DataView捕获信息之前将其放入,

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

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
}