Pergunta

Se um DataSet contém uma coluna que é um timestamp ou outro valor binário, a sua associado DataGridView lança um ArgumentException, ao exibir todos os dados nessa coluna. Ou seja, assumir que tem alguma tabela contendo uma coluna binária, tais como:

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

No Visual Studio 2008, adicionar um novo apontar fonte de dados para a mesa suspeito. Arraste a tabela a partir do gerenciador de fonte de dados sobre a superfície designer visual de uma nova WinForm para criar automaticamente um DataGridView, BindingSource, etc. Execute o aplicativo e você receberá uma exceção de tempo de execução. Soa como um defeito, certo?

Se você examinar a coleção Colunas do DataGridView você vai descobrir que ele define o tipo de coluna para DataGridViewImageColumn. Por quê? Porque, de acordo com a Microsoft, .NET assume que colunas binárias são imagens. Na verdade, a Microsoft afirma que esse comportamento é por design! Veja este defeito relatório no Microsoft Connect: http://connect.microsoft.com /VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=93639

Pode-se suprimir o diálogo de erro ao manipular o evento DataError para o DataGridView, como o diálogo indica educadamente, mas que levanta a questão. Eu quero encontrar uma maneira de evitar ter uma condição de erro, em primeiro lugar. Ou seja, eu quero ter um DataGridViewTextColumn mostrando uma representação textual dos dados binários, por exemplo, "0x1234a8e9433bb2". E eu estou procurando uma solução genérica, já que meu código real não usa uma tabela específica como no meu exemplo acima. Em vez eu coloquei uma consulta algo arbitrário em um dataAdapter.SelectCommand, em seguida, chamar

dataAdapter.Fill(dataTable)

para gerar automaticamente o meu dataTable. Uma vez que é o DataGridView que tem o bug (IMHO), estou pensando que eu preciso verificar as colunas da tabela de dados (ou seja, dataTable.Columns [n] .DataType.Name.Equals ( "byte []")?) e converter qualquer matrizes de bytes para suas formas de texto manualmente antes de eu ligar o dataTable ao DataGridView com

bindingSource.DataSource = dataTable;

A minha pergunta então:

Existe uma maneira mais simples ou mais elegante para exibir colunas binários em um DataGridView?

(Nota que este problema existe tanto com VS 2005 e VS 2008, .NET 2.0 e .NET 3.5.)

Foi útil?

Solução 2

Estimulado pela resposta de Quandary, além de ter permitido tempo suficiente desde a postar minha pergunta para ter uma nova perspectiva :-), eu vim com uma solução razoavelmente limpa sob o disfarce do método MorphBinaryColumns abaixo, incorporado em um teste de amostra completa programa (, exceto para código do designer gerado de VS do meu WinForm contendo um único DataGridView).

MorphBinaryColumns examina a coleção de coluna e, para cada que é uma coluna binária, gera uma nova coluna com o valor convertido para uma cadeia hexadecimal, então swaps fora da coluna original substituindo-o por um novo, preservando a ordem coluna original.

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

Outras dicas

Adicionar algumas melhorias para abordagem acima. # 1 tratamento de NULL colunas binários, nº 2 melhor desempenho ao converter lotes de colunas (usando mesmo construtor corda mais e mais), nº 3 comprimento máximo de exibição de 8000 para evitar a conversão realmente grandes colunas binários para string ... # 4 criando coluna temporário nome usando um guid para colisões de nomes evitar, caso haja uma coluna chamada "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();
    }
}

Você pode encontrar este útil: http://social.msdn.microsoft .com / Fórum / en / winformsdatacontrols / thread / 593606df-0bcb-49e9-8e55-497024699743

Basicamente:

  • Obtendo os dados do banco de dados para a tabela de dados
  • em seguida, adicionando uma nova coluna (typeof (string))
  • , em seguida, escrever o conteúdo binário longo (usando bytearray para string hex) para que a nova coluna

  • , em seguida, vincular.

É simples e chato, mas ele resolve eficazmente o problema.

Como sobre baseando sua consulta em uma visão que faz um elenco para essa coluna?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top