1

私はついに週末を通して取り組んできたバグを突き止めましたが、それを本当に解決する方法がわかりません。つまり、クリーンな方法で。

状況は、ビジネスオブジェクトのリストにバインドされているDGVコントロールを持っているということです。列の1つはデータソースにバインドされておらず、このセルのデータの永続性を処理するためにCellParsingイベントとCellFormattingイベントを接続しました。私は基本的に.netデータバインディングを回避し、自分の貧弱なバージョンを実装しました。これは意図的なものではなく、解析とフォーマットの要件が複雑な非常に古いコードであり、バインドされていない列に基づくソリューションを誤って実装しました。これを処理する正しい方法がわかったので、コードを修正しましたが、別の方法でバグを解決する方法を知りたいと思います。

RowValidatingイベントを処理し、行全体に対して1つの最終検証を実行して、すべてがクールであることを確認します。もちろん、問題がある場合は検証をキャンセルし、行はコミットされません。これはすべて、ユーザーがUIインタラクションを介して行を編集および追加している場合は正常に機能しますが、データソースが設定されている場合は問題が発生します。問題、DGVが内部リストを更新して行を作成するときにCellFormattingが呼び出されないか、少なくとも検証イベントが発生する前に呼び出されないことであると思われます。これにより、RowValidatingハンドラーがUnbound列からnull値をプルします(CellFormattingが呼び出されておらず、値がまだ設定されていないため)。

DGVに関する知識を更新し、CellValueNeededイベントの処理がチケットになる可能性があると考えましたが、DataGridViewCellValueEventArgs.Valueを設定しても、期待したようにCellFormattingイベントがトリガーされませんでした。

私はこの状況を処理する方法を考えていました。私が思いついたのは、最初のバインディングやバインドされたリストの変更ではなく、UIイベントから検証がトリガーされたときを検出することだけです。これはハッキーな解決策であるだけでなく、それがどのように行われるのかわかりません。

問題を説明する完全なサンプルアプリケーションを作成しました。このような問題をどのように解決してくれるのか、本当に興味があります。ここには大きなデザインの匂いがあるようです。

using System;
using System.Collections.Generic;
using System.Windows.Forms;

public class Form1 : Form
{
    private List<DomainModel> _sampleData;
    public Form1()
    {
        InitializeComponent();

        _sampleData = new List<DomainModel>();
        _sampleData.Add(new DomainModel("Widget A"));
        _sampleData.Add(new DomainModel("Widget B"));
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Console.WriteLine("Setting DataSource");
        domainModelBindingSource.DataSource = _sampleData;
    }

    private void dataGridView1_CellFormatting(object sender,
        DataGridViewCellFormattingEventArgs e)
    {
        Console.WriteLine("CellFormatting fired for {0},{1}",
            e.RowIndex, e.ColumnIndex);

        if (e.ColumnIndex != 0 && !dataGridView1.Rows[e.RowIndex].IsNewRow)
        {
            var model = domainModelBindingSource[e.RowIndex] as DomainModel;
            e.Value = model.Name;
            e.FormattingApplied = true;
        }
    }

    private void dataGridView1_CellParsing(object sender, DataGridViewCellParsingEventArgs e)
    {
        if (e.ColumnIndex == 1)
        {
            e.Value = e.Value.ToString();
            e.ParsingApplied = true;
        }
    }

    private void dataGridView1_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
    {
        if (dataGridView1.Rows[e.RowIndex].IsNewRow)
            return;

        object value = dataGridView1[1, e.RowIndex].Value;
        if (value == null || String.IsNullOrEmpty(value.ToString()))
            e.Cancel = true;
    }

    #region Designer stuff

    private System.ComponentModel.IContainer components = null;
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
            components.Dispose();

        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    private void InitializeComponent()
    {
            this.components = new System.ComponentModel.Container();
            this.dataGridView1 = new System.Windows.Forms.DataGridView();
            this.nameDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
            this.Column1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
            this.domainModelBindingSource = new System.Windows.Forms.BindingSource(this.components);
            this.button1 = new System.Windows.Forms.Button();
            ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.domainModelBindingSource)).BeginInit();
            this.SuspendLayout();
            // 
            // dataGridView1
            // 
            this.dataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.dataGridView1.AutoGenerateColumns = false;
            this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
            this.nameDataGridViewTextBoxColumn,
            this.Column1});
            this.dataGridView1.DataSource = this.domainModelBindingSource;
            this.dataGridView1.Location = new System.Drawing.Point(12, 41);
            this.dataGridView1.Name = "dataGridView1";
            this.dataGridView1.Size = new System.Drawing.Size(437, 161);
            this.dataGridView1.TabIndex = 0;
            this.dataGridView1.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.dataGridView1_CellFormatting);
            this.dataGridView1.CellParsing += new System.Windows.Forms.DataGridViewCellParsingEventHandler(this.dataGridView1_CellParsing);
            this.dataGridView1.RowValidating += new System.Windows.Forms.DataGridViewCellCancelEventHandler(this.dataGridView1_RowValidating);
            // 
            // nameDataGridViewTextBoxColumn
            // 
            this.nameDataGridViewTextBoxColumn.DataPropertyName = "Name";
            this.nameDataGridViewTextBoxColumn.HeaderText = "Name";
            this.nameDataGridViewTextBoxColumn.Name = "nameDataGridViewTextBoxColumn";
            // 
            // Column1
            // 
            this.Column1.HeaderText = "Data (unbound)";
            this.Column1.Name = "Column1";
            // 
            // domainModelBindingSource
            // 
            this.domainModelBindingSource.DataSource = typeof(DomainModel);
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(12, 12);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(168, 23);
            this.button1.TabIndex = 1;
            this.button1.Text = "Update Data Source";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(461, 214);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.dataGridView1);
            this.Name = "Form1";
            this.Text = "Form1";
            ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.domainModelBindingSource)).EndInit();
            this.ResumeLayout(false);
    }

    #endregion

    private System.Windows.Forms.DataGridView dataGridView1;
    private System.Windows.Forms.BindingSource domainModelBindingSource;
    private System.Windows.Forms.Button button1;
    private System.Windows.Forms.DataGridViewTextBoxColumn nameDataGridViewTextBoxColumn;
    private System.Windows.Forms.DataGridViewTextBoxColumn Column1;

    #endregion
}

internal sealed class DomainModel
{
    public DomainModel() { }

    public DomainModel(string name)
    {
        this.Name = name;
    }

    public string Name { get; set; }
}

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}
4

1 に答える 1

1

私はすでに「問題を修正した」と述べましたが、これは完全には正確ではありませんでした。テストアプリケーションで修正し、使用する予定のソリューションを効果的に検証しましたが、実際には実装していませんでした。一度試してみると、なぜ元のようにしたのかがわかりました。バインドされていない列で使用されるデータは読み取り専用のオブジェクトのコレクションであり、それらを設定するには、オブジェクトに対して別のメソッドを呼び出す必要があります。ベストプラクティスとすべて...

もちろん、読み取り専用のプロパティはDGVで編集できないため、私は水中で死んでしまいました。これにより、元のデザインに戻ることができました。調査中に、BrianNoyesによるこのコラム/ブログに出くわしました。

彼はRowsAddedイベントについて言及しており、それが私に必要なすべてです!これが私の問題の解決策です:

private void dataGridView1_RowsAdded(object sender, 
    DataGridViewRowsAddedEventArgs e)
{
    if (dataGridView1.Rows[e.RowIndex].IsNewRow)
    {
        return;
    }

    dataGridView1[1, e.RowIndex].Value = 
        (dataGridView1.Rows[e.RowIndex].DataBoundItem as DomainModel).Name;
}

このソリューションが気に入っているのは、バインドされている場合と同じようにデータをCellに配置し、フォーマットの責任をCellFormattingに渡して、作業を行えるようにするためです。同様に、データを解析し、バインドされたオブジェクトの「SetListData」メソッドに渡すために必要なオブジェクトを作成できます。

よかった!

于 2012-04-23T07:23:35.823 に答える