Domanda

Se un DataSet contiene una colonna che è un timestamp o altro valore binario, il suo DataGridView associato genera un ArgumentException, quando si visualizzano i dati in quella colonna. Cioè, si supponga di avere un po 'di tabella contenente una colonna binaria come ad esempio:

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

In Visual Studio 2008, aggiungere una nuova origine dati che punta al tavolo sospetto. Trascinare la tabella dalla esploratore origine dati sulla superficie visual designer di una nuova WinForm per creare automaticamente un DataGridView, BindingSource, ecc Eseguire l'applicazione e si otterrà un'eccezione di runtime. Suona come un difetto, giusto?

Se si esamina la collezione Colonne del DataGridView troverete che si imposta il tipo di colonna per DataGridViewImageColumn. Perché? Poiché, secondo Microsoft, NET presuppone che le colonne binarie sono immagini. Infatti, Microsoft afferma che questo comportamento è di progettazione! Vedere questo rapporto difetto sul Microsoft Connect: http://connect.microsoft.com /VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=93639

Si potrebbe sopprimere la finestra di errore gestendo l'evento DataError per la DataGridView, come la finestra di dialogo indica educatamente, ma che pone la domanda. Voglio trovare un modo per evitare di avere una condizione di errore, in primo luogo. Cioè, voglio avere un DataGridViewTextColumn che mostra una rappresentazione testuale dei dati binari, per esempio "0x1234a8e9433bb2". E io sto cercando una soluzione generica, dato che il mio codice vero e non utilizza una tabella specifica come nel mio esempio di cui sopra. Piuttosto ho messo una query alquanto arbitraria in una dataAdapter.SelectCommand, quindi richiamare

dataAdapter.Fill(dataTable)

per generare automaticamente il mio dataTable. Dal momento che è la DataGridView che ha il (secondo me) errore, penso che ho bisogno di controllare le colonne della tabella di dati (ad esempio dataTable.Columns [n] .DataType.Name.Equals ( "Byte []")?) e convertire qualsiasi array di byte per le loro forme di testo manualmente prima collego il dataTable al DataGridView con

bindingSource.DataSource = dataTable;

La mia domanda allora:

C'è un modo più semplice e più elegante per visualizzare le colonne binari in un DataGridView?

(Si noti che questo problema esiste sia con VS 2005 e VS 2008, .NET 2.0 e .NET 3.5.)

È stato utile?

Soluzione 2

Spronato dalla risposta di Quandary, oltre a aver permesso il tempo sufficiente dal distacco la mia domanda per avere una prospettiva :-) fresca, mi si avvicinò con una soluzione ragionevolmente pulito nelle vesti del metodo MorphBinaryColumns di seguito, incorporato in un test di esempio completo programma ( tranne per il progettista di VS codice generato dal mio WinForm contenente un singolo DataGridView).

MorphBinaryColumns esamina la raccolta colonna e, per ciascuna che è una colonna binaria, genera una nuova colonna con il valore convertito in una stringa esadecimale, quindi scambia la colonna originale sostituendolo con quello nuovo, preservando l'ordine colonna originale.

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

Altri suggerimenti

aggiunta di alcuni miglioramenti approccio sopra. # 1 movimentazione colonne nullo binarie, # 2 migliori prestazioni quando si converte un sacco di colonne (utilizzando stessa stringa builder ripetutamente), # 3 lunghezza massima esposizione del 8000 per evitare la conversione davvero grandi colonne binari stringa ... # 4 creando colonna Temp nome utilizzando un GUID per evitare conflitti di nomi nel caso in cui v'è una colonna denominata "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();
    }
}

Si potrebbe rivelarsi utile: http://social.msdn.microsoft .com / Forum / it / winformsdatacontrols / thread / 593606df-0bcb-49e9-8e55-497024699743

In sostanza:

  • Come i dati dal DB al DataTable
  • quindi l'aggiunta di una nuova colonna (typeof (string))
  • quindi scrivere il contenuto binario sopra (usando ByteArray esadecimale stringa) in quella nuova colonna

  • quindi databind.

E 'semplice e fastidioso, ma si risolve efficacemente il problema.

Che ne dite di basare la query su una vista che fa un cast per quella colonna?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top