2009-01-28 9 views
4

J'ai un DataTable qui est rempli à partir d'un fichier CSV puis, en utilisant un DataGridView, les données sont éditées en mémoire. Autant que je comprenne l'édition programmatique des données devrait être faite sur le DataTable où l'édition d'utilisateur est faite via. le DataGridView.L'ajout de colonnes à un DataTable lié à un DataGridView ne met pas à jour la vue

Toutefois, lorsque j'ajoute des colonnes par programme au DataTable, il n'est pas reflété automatiquement dans le DataGridView et je suppose que l'inverse est également vrai.

Comment gardez-vous les deux concurrents? Je pensais que l'idée de liaison de données était que c'était automatique ...

Voici le code de configuration pertinente - WorksheetGridView sous-classes 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); 
} 

Le bit qui ne fonctionne pas est plus tard quand je l'appelle pour AddColumn instance dans ce code pour gérer coller des données délimitées par des tabulations,


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

J'ai essayé l'exemple donné ci-dessous et cela fonctionne. Toutefois, lorsque j'essaie de le faire, la barre de défilement horizontale se développe et se contracte brièvement, mais la table du sous-groupe DataGridView n'est pas mise à jour. La colonne est cependant dans le DataTable si - par exemple je ne peux pas ajouter une colonne du même nom, le nombre de colonnes reflète aussi les colonnes supplémentaires. Y at-il peut-être une option concepteur qui pourrait restreindre la mise à jour de la DataGridView?

L'ajout de lignes fonctionne également.

RESOLU:

Le AutoGeneratedColumns a été mis à false dans le code de concepteur en dépit d'être true dans les propriétés de dialogue (et définir explicitement dans le code). Les colonnes initiales ont été générées par programme et ne devraient donc pas être apparues, mais cela n'a pas été pris en compte puisque le code du concepteur a également continué à générer des colonnes «conçues dans» qui étaient initialement utilisées pour le débogage. Moralité: Vérifiez le code généré automatiquement!

En plus de cela, voir this post et this post

Répondre

2

Cela ne semble pas correct. Pour le tester, j'ai écrit une application simple, qui crée un DataTable et y ajoute des données. Sur le bouton 1, cliquez pour lier la table à DataGridView. Ensuite, j'ai ajouté un second bouton, qui, une fois cliqué, ajoute une autre colonne au DataTable sous-jacent. Quand je l'ai testé, et j'ai cliqué sur le deuxième bouton, la grille immedialtey reflète la mise à jour. Pour tester l'inverse, j'ai ajouté un troisième bouton qui ouvre une boîte de dialogue avec un DataGridView qui se lie à la même datatable. Au moment de l'exécution, j'ai ensuite ajouté des valeurs à la première vue datagridview, et lorsque j'ai cliqué sur le bouton pour faire apparaître la boîte de dialogue, les modifications ont été reflétées. Mon point est, ils sont censés rester concurrentes. Mark a peut-être raison lorsqu'il vous a suggéré de vérifier si AutoGenerateColumns est défini sur true. Vous n'avez pas besoin d'appeler DataBind, c'est seulement pour un GridView sur le web.Peut-être que vous pouvez poster de ce que vous faites, parce que cela devrait fonctionner.

Comment je l'ai testé:

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

En particulier, vérifiez la Valeur AutoGenerateColumns dans le code du concepteur. Réponse sélectionnée puisque l'exemple a beaucoup aidé au débogage! – Brendan

1

est-AutoGenerateColumns à true? Si vous effectuez des modifications après la liaison initiale, vous devrez également appeler DataBind() pour relier la source de données modifiée. Je sais que cela est vrai pour un rappel AJAX, je pense c'est vrai pour un contrôle WinForms PostBack.

+0

AutoGenerateColumns est vrai – Brendan

-1

J'ai eu la même question et j'ai publié un DataBind(). Ce n'est pas la solution miracle pour tout, mais c'est ce qui m'a aidé dans quelques cas. Je devais le mettre en avant la capture par un DataView, après les événements EditCommand et UpdateCommand immédiatement après la déclaration EditItemIndex,

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

et

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 
} 
+0

DataGridView qui fait partie de System.Windows.Forms n'a pas de méthode DataBind. Vous pensez à GridView qui fait partie de System.Web.UI.WebControls. L'OP parle de WinForms. – BFree

1

Voici une autre solution que vous ne avez pas besoin d'affecter un "nom" à la grille de données, de sorte que la grille de données peut être un élément de modèle.

(Dans mon cas, la grille de données est un élément de modèle à l'intérieur TabControl.ContentTemplate)

La clé pour afficher la nouvelle colonne (ajoutée par programme après la liaison initiale) oblige le DataGrid à rafraîchir. De la réponse dans Force WPF DataGrid to regenerate itself, Andres a suggéré que le paramètre AutoGenerateColumns de false à true forcera DataGrid à actualiser.

Ce qui signifie que je besoin simplement:

  1. Liez le AutoGenerateColumns à une propriété de mon objet
  2. Réglez le propery false avant d'ajouter la colonne
  3. Définissez la propery true après la colonne ajoutée.

Voici le 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 aide :)

Questions connexes