c# - tabla - Agregar columnas a una DataTable vinculada a DataGridView no actualiza la vista
crear tabla en windows form c# (4)
¿AutoGenerateColumns está establecido en verdadero? Si realiza cambios después de la vinculación inicial, también deberá llamar a DataBind () para volver a enlazar la fuente de datos modificada. Sé que esto es cierto para una devolución de llamada AJAX, creo que es cierto para un control de WinForms PostBack.
Tengo una DataTable que se rellena desde un archivo CSV y luego, con DataGridView, los datos se editan en la memoria. Por lo que yo entiendo, la edición programática de los datos debe hacerse en la DataTable donde la edición del usuario se realiza a través de. el DataGridView.
Sin embargo, cuando agrego columnas programáticamente a DataTable, no se refleja automáticamente en DataGridView y sospecho que el inverso también es verdadero.
¿Cómo se mantienen los dos concurrentes? Pensé que la idea del enlace de datos era que esto era automático ...
Aquí está el código de configuración relevante: subclases de 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);
}
El bit que no funciona es más tarde cuando llamo AddColumn
por ejemplo en este código para manejar el pegado de datos delimitados por tabuladores,
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");
}
}
Probé el ejemplo que figura a continuación y funciona. Sin embargo, cuando intento hacer esto, la barra de desplazamiento horizontal se expande y se contrae brevemente, pero la tabla del DataGridView
subclasificado no se actualiza. Sin embargo, la columna está en el DataTable
; por ejemplo, no puedo agregar una columna del mismo nombre, el recuento de columnas también refleja las columnas adicionales. ¿Existe quizás una opción de diseñador que pueda restringir la actualización de DataGridView
?
También agregar filas funciona bien.
SOLUCIONADO
AutoGeneratedColumns
se estableció en false
en el código de diseñador a pesar de ser true
en el cuadro de diálogo de propiedades (y se establece explícitamente en el código). Las columnas iniciales se generaron mediante programación y, por lo tanto, no deberían haber aparecido, pero esto no se recogió porque el código del diseñador también siguió generando columnas ''diseñadas en'' que originalmente se usaban para la depuración.
Moral: ¡comprueba el código autogenerado!
Además de esto, mira esta publicación y esta publicación
Esto no suena bien Para probarlo, escribí una aplicación simple, que crea una DataTable y le agrega algunos datos. En el botón 1, haga clic en vincula la tabla al DataGridView. Luego, agregué un segundo botón que, al hacer clic, agrega otra columna al DataTable subyacente. Cuando lo probé e hice clic en el segundo botón, la cuadrícula reflejó la actualización. Para probar lo contrario, agregué un tercer botón que abre un cuadro de diálogo con un DataGridView que se une a la misma tabla de datos. En el tiempo de ejecución, agregué algunos valores a la primera vista de cuadrícula de datos, y cuando hice clic en el botón para que apareciera el diálogo, se reflejaron los cambios.
Mi punto es que se supone que deben permanecer concurrentes. Mark puede estar en lo cierto cuando sugirió que compruebe si AutoGenerateColumns está establecido en verdadero. No es necesario que llame a DataBind, eso es solo para un GridView en la web. Tal vez puedas publicar lo que estás haciendo, porque esto DEBERÍA funcionar.
Cómo lo probé:
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; }
}
}
Tuve el mismo problema y emití un DataBind (). No es la solución mágica para todo, pero es lo que me ayudó en algunos casos. Tuve que ponerlo antes de capturar información a través de un DataView, después de los eventos EditCommand y UpdateCommand inmediatamente después de la instrucción EditItemIndex,
protected void datalistUWSolutions_EditCommand(object source, DataListCommandEventArgs e)
{
datalistUWSolutions.EditItemIndex = e.Item.ItemIndex;
datalistUWSolutions.DataBind(); // refresh the grid.
}
y
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
}
Aquí hay otra solución que no necesita asignar un "nombre" a la cuadrícula de datos, de modo que la cuadrícula de datos puede ser un elemento de plantilla.
(En mi caso, la cuadrícula de datos es un elemento de plantilla dentro de TabControl.ContentTemplate)
La clave para mostrar la nueva columna (agregada mediante programación después del enlace inicial) está forzando a la cuadrícula de datos a actualizarse. De la respuesta en Force WPF DataGrid para regenerarse , Andrés sugirió que establecer AutoGenerateColumns
de falso a verdadero forzará la actualización de la cuadrícula de datos.
Lo que significa que simplemente necesito:
- Enlace las
AutoGenerateColumns
a una propiedad de mi objeto - Establezca la propiedad en falso antes de agregar la columna
- Establezca la propiedad en verdadero después de la columna agregada.
Aquí está el código:
XAML:
<DataGrid AutoGenerateColumns="{Binding toggleToRefresh}"
ItemsSource="{Binding dataTable}"
/>
DO#:
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);
}
}
Espero que esto ayude :)