Question

Si un ensemble de données contient une colonne qui est une estampille temporelle ou d'une autre valeur binaire, son DataGridView associé déclenche une ArgumentException, lors de l'affichage de données dans cette colonne. Cela est, supposons que vous avez une table contenant une colonne binaire tel que:

CREATE TABLE [dbo].[DataTest](
    [IdStuff] INT IDENTITY(1,1) NOT NULL,
    [ProblemColumn] TIMESTAMP NOT NULL )

Dans Visual Studio 2008, ajoutez une nouvelle source de données pointant vers la table suspect. Faites glisser la table de l'explorateur de source de données sur la surface du concepteur visuel d'une nouvelle WinForm pour créer automatiquement un DataGridView, BindingSource, etc. Exécutez l'application et vous obtiendrez une exception d'exécution. Sonne comme un défaut, non?

Si vous examinez la collection Colonnes du DataGridView, vous trouverez qu'il définit le type de colonne à DataGridViewImageColumn. Pourquoi? Parce que, selon Microsoft, .NET suppose que les colonnes binaires sont des images. En effet, Microsoft affirme que ce comportement est! Voir le rapport de défaut sur Microsoft Connect: http://connect.microsoft.com /VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=93639

On pourrait supprimer la boîte de dialogue d'erreur en traitant l'événement DataError pour le DataGridView, comme la boîte de dialogue indique poliment, mais pose la question. Je veux trouver un moyen d'éviter d'avoir une condition d'erreur en premier lieu. Autrement dit, je veux avoir un DataGridViewTextColumn montrant une représentation textuelle des données binaires, par exemple "0x1234a8e9433bb2". Et je suis à la recherche d'une solution générique, puisque mon code actuel n'utilise pas une table spécifique comme dans mon exemple ci-dessus. Au contraire, je mets une requête quelque peu arbitraire dans un dataAdapter.SelectCommand, puis appelez

dataAdapter.Fill(dataTable)

pour générer automatiquement mon dataTable. Comme il est le DataGridView qui a le bug (IMHO), je pense que je dois vérifier les colonnes du tableau de données (c.-à-dataTable.Columns [n] .DataType.Name.Equals ( « Byte [] »)?) et convertir tous les tableaux d'octets à leurs formes de texte manuellement avant de connecter le dataTable au DataGridView avec

bindingSource.DataSource = dataTable;

Ma question alors:

Y at-il un moyen plus simple ou plus élégante pour afficher les colonnes binaires dans un DataGridView?

(Notez que ce problème existe à la fois VS 2005 et VS 2008, .NET 2.0 et .NET 3.5.)

Était-ce utile?

La solution 2

Sous l'impulsion de la réponse de Quandary, plus avoir suffisamment de temps depuis l'affichage à ma question d'avoir une nouvelle perspective :-), je suis venu avec une solution raisonnablement propre sous la forme de la méthode MorphBinaryColumns ci-dessous, intégré dans un test d'échantillon complet programme ( à l'exception de mon WinForm contenant un seul DataGridView pour le concepteur de VS code généré).

MorphBinaryColumns examine la collection de colonnes et, pour chaque colonne qui est un binaire, génère une nouvelle colonne à la valeur convertie en une chaîne hexadécimale, puis permute la colonne d'origine en le remplaçant par le nouveau, en conservant l'ordre des colonnes d'origine.

public partial class Form1 : Form
{
  public Form1()
  {
    InitializeComponent();
  }

  private void Form1_Load(object sender, EventArgs e)
  {
    var sqlCnn = new SqlConnection("..."); // fill in your connection string
    string strsql = "select ... from ..."; // fill in your query

    var dataAdapter = new SqlDataAdapter();
    var dataTable = new DataTable();
    dataAdapter.SelectCommand = new SqlCommand(strsql, sqlCnn);
    dataAdapter.Fill(dataTable);
    MorphBinaryColumns(dataTable);
    dataGridView1.DataSource = dataTable;
  }

  private void MorphBinaryColumns(DataTable table)
  {
    var targetNames =  table.Columns.Cast<DataColumn>()
      .Where(col => col.DataType.Equals(typeof(byte[])))
      .Select(col => col.ColumnName).ToList();
    foreach (string colName in targetNames)
    {
      // add new column and put it where the old column was
      var tmpName = "new";
      table.Columns.Add(new DataColumn(tmpName, typeof (string)));
      table.Columns[tmpName].SetOrdinal(table.Columns[colName].Ordinal);

      // fill in values in new column for every row
      foreach (DataRow row in table.Rows)
      {
        row[tmpName] = "0x" + string.Join("",
          ((byte[]) row[colName]).Select(b => b.ToString("X2")).ToArray());
      }

      // cleanup
      table.Columns.Remove(colName);
      table.Columns[tmpName].ColumnName = colName;
    }
  }
}

Autres conseils

Ajout de quelques améliorations à l'approche ci-dessus. # 1 manipulation colonnes binaires nuls, # 2 a amélioré les performances lors de la conversion de nombreuses colonnes (en utilisant même générateur de chaîne au-dessus de et plus), n ° 3 de la longueur d'affichage maximale de 8000 pour éviter la conversion de très grandes colonnes binaires à chaîne ... # 4 créer colonne temp nom à l'aide d'un guid pour éviter les collisions de noms en cas il y a une colonne « temp » ...

/// <summary>
/// Maximum length of binary data to display (display is truncated after this length)
/// </summary>
const int maxBinaryDisplayString = 8000;

/// <summary>
/// Accepts datatable and converts all binary columns into textual representation of a binary column
/// For use when display binary columns in a DataGridView
/// </summary>
/// <param name="t">Input data table</param>
/// <returns>Updated data table, with binary columns replaced</returns>
private DataTable FixBinaryColumnsForDisplay(DataTable t)
{
    List<string> binaryColumnNames = t.Columns.Cast<DataColumn>().Where(col => col.DataType.Equals(typeof(byte[]))).Select(col => col.ColumnName).ToList();
    foreach (string binaryColumnName in binaryColumnNames)
    {
        // Create temporary column to copy over data
        string tempColumnName = "C" + Guid.NewGuid().ToString();
        t.Columns.Add(new DataColumn(tempColumnName, typeof(string)));
        t.Columns[tempColumnName].SetOrdinal(t.Columns[binaryColumnName].Ordinal);

        // Replace values in every row
        StringBuilder hexBuilder = new StringBuilder(maxBinaryDisplayString * 2 + 2);
        foreach (DataRow r in t.Rows)
        {
            r[tempColumnName] = BinaryDataColumnToString(hexBuilder, r[binaryColumnName]);
        }

        t.Columns.Remove(binaryColumnName);
        t.Columns[tempColumnName].ColumnName = binaryColumnName;
    }
    return t;
}
/// <summary>
/// Converts binary data column to a string equivalent, including handling of null columns
/// </summary>
/// <param name="hexBuilder">String builder pre-allocated for maximum space needed</param>
/// <param name="columnValue">Column value, expected to be of type byte []</param>
/// <returns>String representation of column value</returns>
private string BinaryDataColumnToString(StringBuilder hexBuilder, object columnValue)
{
    const string hexChars = "0123456789ABCDEF";
    if (columnValue == DBNull.Value)
    {
        // Return special "(null)" value here for null column values
        return "(null)";
    }
    else
    {
        // Otherwise return hex representation
        byte[] byteArray = (byte[])columnValue;
        int displayLength = (byteArray.Length > maxBinaryDisplayString) ? maxBinaryDisplayString : byteArray.Length;
        hexBuilder.Length = 0;
        hexBuilder.Append("0x");
        for(int i = 0; i<displayLength; i++)
        {
            hexBuilder.Append(hexChars[(int)byteArray[i] >> 4]);
            hexBuilder.Append(hexChars[(int)byteArray[i] % 0x10]);
        }
        return hexBuilder.ToString();
    }
}

Vous trouverez peut-être ce utile: http://social.msdn.microsoft .com / Forums / fr / winformsdatacontrols / fil / 593606df-0bcb-49e9-8e55-497024699743

En gros:

  • Obtenir les données de la DB à datatable
  • puis en ajoutant une nouvelle colonne (typeof (string))
  • puis écrire le contenu binaire sur (en utilisant bytearray à chaîne hex) dans cette nouvelle colonne

  • puis DataBind.

Il est simple et ennuyeux, mais il résout efficacement le problème.

Que diriez-vous baser votre requête sur une vue qui fait un CAST pour cette colonne?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top