6


3

DataGridViewにバイナリ列を表示する.NETの問題を克服する

DataSetにタイムスタンプまたはその他のバイナリ値である列が含まれている場合、その列にデータを表示すると、関連付けられたDataGridViewはArgumentExceptionをスローします。 つまり、次のようなバイナリ列を含むテーブルがあると仮定します。

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

Visual Studio 2008で、疑わしいテーブルを指す新しいデータソースを追加します。 データソースエクスプローラーから新しいWinFormのビジュアルデザイナー画面にテーブルをドラッグして、DataGridView、BindingSourceなどを自動的に作成します。 アプリケーションを実行すると、ランタイム例外が発生します。 欠陥のようですね。

DataGridViewのColumnsコレクションを調べると、列タイプがDataGridViewImageColumnに設定されていることがわかります。 Why? なぜなら、Microsoftによれば、.NETはバイナリ列が画像であると想定しているためです。 実際、Microsoftはこの動作が仕様によるものであることを確認しています! Microsoft Connectに関する次の障害レポートを参照してください:http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=93639

ダイアログが丁寧に示しているように、DataGridViewのDataErrorイベントを処理することでエラーダイアログを抑制することができますが、それは質問を頼みます。 そもそもエラー状態を回避する方法を見つけたいです。 つまり、バイナリデータのテキスト表現を示すDataGridViewTextColumnが必要です。 「0x1234a8e9433bb2」。 また、実際のコードでは上記の例のように特定のテーブルを使用していないため、一般的なソリューションを探しています。 むしろ、やや任意のクエリをdataAdapter.SelectCommandに入れてから呼び出します

dataAdapter.Fill(dataTable)

dataTableを自動生成します。 (IMHO)バグがあるのはDataGridViewであるため、データテーブルの列を確認する必要があると考えています(つまり、 dataTable.Columns [n] .DataType.Name.Equals( "Byte []")? )そして、dataTableをDataGridViewに接続する前に、バイト配列をテキスト形式に手動で変換します

bindingSource.DataSource = dataTable;

私の質問:

DataGridViewにバイナリ列を表示するより簡単な方法またはよりエレガントな方法はありますか?

(この問題はVS 2005とVS 2008、.NET 2.0および.NET 3.5の両方に存在することに注意してください。)

4 Answer


6


上記のアプローチにいくつかの改善を追加します。 #1 nullバイナリ列の処理、#2多数の列の変換時のパフォーマンスの向上(同じ文字列ビルダーを繰り返し使用)、#3本当に大きなバイナリ列の文字列への変換を回避するための最大表示長8000 …​ #4「temp」という名前の列がある場合に、名前の衝突を避けるためにGUIDを使用して一時列名を作成する…​

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

///
/// Accepts datatable and converts all binary columns into textual representation of a binary column
/// For use when display binary columns in a DataGridView
///
/// Input data table
/// Updated data table, with binary columns replaced
private DataTable FixBinaryColumnsForDisplay(DataTable t)
{
    List binaryColumnNames = t.Columns.Cast().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;
}
///
/// Converts binary data column to a string equivalent, including handling of null columns
///
/// String builder pre-allocated for maximum space needed
/// Column value, expected to be of type byte []
/// String representation of column value
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> 4]);
            hexBuilder.Append(hexChars[(int)byteArray[i] % 0x10]);
        }
        return hexBuilder.ToString();
    }
}


3


Quandaryの答えに拍車をかけ、さらに、新しい質問を投稿してから十分な時間をとって十分な時間をとって、次の「MorphBinaryColumns」メソッドを装って、完全なサンプルテストプログラムに組み込まれた、かなりきれいなソリューションを思い付きました。 (VSデザイナーの単一のDataGridViewを含むWinFormから生成されたコードを除く)。

MorphBinaryColumnsは列コレクションを調べ、バイナリ列であるそれぞれに対して、値を16進文字列に変換した新しい列を生成し、元の列を交換して、元の列の順序を保持します。

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()
      .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


これは役に立つかもしれません:http://social.msdn.microsoft.com/Forums/en/winformsdatacontrols/thread/593606df-0bcb-49e9-8e55-497024699743

基本的に:

  • DBからデータテーブルへのデータの取得

  • 次に、新しい列を追加します(typeof(string))

  • 次に、バイナリコンテンツを上書きします(bytearray to hex stringを使用して) その新しい列に

  • 次にデータバインド。

シンプルで迷惑ですが、問題を効果的に解決します。


0


その列に対してCASTを実行するビューに基づいてクエリを作成するのはどうですか?