6

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に設定されていることがわかります。なんで?なぜなら、Microsoftによれば、.NETはバイナリ列がイメージであると想定しているからです。実際、Microsoftは、この動作は仕様によるものであると断言しています。Microsoft Connectに関するこの欠陥レポートを参照してください:http ://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID = 93639

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

dataAdapter.Fill(dataTable)

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

bindingSource.DataSource = dataTable;

私の質問:

DataGridViewにバイナリ列を表示するためのより簡単またはより洗練された方法はありますか?

(この問題は、VS2005とVS2008、.NET2.0と.NET3.5の両方に存在することに注意してください。)

4

4 に答える 4

7

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

/// <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();
    }
}
于 2012-12-17T17:30:58.283 に答える
4

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<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;
    }
  }
}
于 2011-07-20T18:34:40.497 に答える
1

これが役立つ場合があります: http ://social.msdn.microsoft.com/Forums/en/winformsdatacontrols/thread/593606df-0bcb-49e9-8e55-497024699743

基本的に:

  • DBからデータテーブルへのデータの取得
  • 次に、新しい列を追加します(typeof(string))
  • 次に、バイナリコンテンツを(bytearrayを使用して16進文字列に)その新しい列に書き込みます

  • 次にdatabind。

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

于 2011-07-20T11:32:47.443 に答える
0

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

于 2009-12-08T21:12:08.873 に答える