Преодоление проблемы .NET с отображением двоичных столбцов в DataGridView

StackOverflow https://stackoverflow.com/questions/1869853

Вопрос

Если набор данных содержит столбец, представляющий собой временную метку или другое двоичное значение, связанный с ним DataGridView выдает исключение ArgumentException при отображении любых данных в этом столбце.То есть предположим, что у вас есть некоторая таблица, содержащая двоичный столбец, такой как:

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

В Visual Studio 2008 добавьте новый источник данных, указывающий на подозрительную таблицу.Перетащите таблицу из обозревателя источников данных на поверхность визуального конструктора новой WinForm, чтобы автоматически создать DataGridView, BindingSource и т.д.Запустите приложение, и вы получите исключение во время выполнения.Звучит как дефект, не так ли?

Если вы изучите коллекцию Columns DataGridView, вы обнаружите, что она устанавливает тип столбца в DataGridViewImageColumn.Почему?Потому что, согласно Microsoft, .NET предполагает, что двоичные столбцы являются изображениями.Действительно, Microsoft утверждает, что такое поведение является преднамеренным!Смотрите этот отчет о дефекте в Microsoft Connect: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx ?Идентификатор обратной связи=93639

Можно было бы подавить диалоговое окно с ошибкой, обработав событие DataError для DataGridView, как вежливо указано в диалоговом окне, но это вызывает вопрос.Я хочу найти способ избежать возникновения ошибки в первую очередь.То есть я хочу иметь DataGridViewTextColumn, показывающий текстовое представление двоичных данных, например"0x1234a8e9433bb2".И я ищу общее решение, поскольку мой фактический код не использует конкретную таблицу, как в моем примере выше.Скорее я помещаю несколько произвольный запрос в DataAdapter.Выберите команду, затем вызовите

dataAdapter.Fill(dataTable)

чтобы автоматически сгенерировать мою таблицу данных.Поскольку именно DataGridView содержит ошибку (IMHO), я думаю, что мне нужно проверить столбцы таблицы данных (т.Е.DataTable.Столбцы[n].Тип данных.Имя.Равно("Байт[]")?) и преобразуйте любые массивы байтов в их текстовые формы вручную, прежде чем я подключу DataTable к DataGridView с помощью

bindingSource.DataSource = dataTable;

Тогда мой вопрос:

Есть ли более простой или элегантный способ отображения двоичных столбцов в DataGridView?

(Обратите внимание, что эта проблема существует как с VS 2005, так и с VS 2008, .NET 2.0 и .NET 3.5.)

Это было полезно?

Решение 2

Подстегнутый ответом Quandary, плюс имея достаточно времени с момента публикации моего вопроса, чтобы взглянуть на него по-новому :-), я придумал достаточно чистое решение под видом MorphBinaryColumns приведенный ниже метод, встроенный в полную программу тестирования образцов (за исключением для конструктора VS сгенерирован код из моей WinForm, содержащий один DataGridView).

MorphBinaryColumns проверяет коллекцию столбцов и для каждого, который является двоичным столбцом, генерирует новый столбец со значением, преобразованным в шестнадцатеричную строку, затем заменяет исходный столбец, заменяя его новым, сохраняя исходный порядок столбцов.

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

Другие советы

Добавление нескольких улучшений к вышеуказанному подходу.#1 обработка нулевых двоичных столбцов, #2 улучшенная производительность при преобразовании большого количества столбцов (с использованием одного и того же построителя строк снова и снова), #3 максимальная длина отображения 8000, чтобы избежать преобразования действительно больших двоичных столбцов в строку...#4 создание имени временного столбца с использованием guid, чтобы избежать конфликтов имен в случае, если есть столбец с именем «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();
    }
}

Возможно, вы найдете это полезным:http://social.msdn.microsoft.com/Forums/en/winformsdatacontrols/thread/593606df-0bcb-49e9-8e55-497024699743

В основном:

  • Получение данных из базы данных в datatable
  • затем добавляем новый столбец (typeof(string))
  • затем запишите двоичное содержимое (используя bytearray в шестнадцатеричную строку) в этот новый столбец

  • затем привязка к данным.

Это просто и раздражает, но эффективно решает проблему.

Как насчет того, чтобы основывать свой запрос на представлении, которое выполняет CAST для этого столбца?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top