2012-04-23 42 views
1

我终于找到了整个周末一直在努力的一个错误,但是我没有看到真正解决这个问题的方法 - 以一种干净的方式,就是这样。如何检测DataGridView.RowValidating事件是否来自用户交互vs BindingSource列表更改?

的情况是,我有一个绑定到业务对象列表的DGV控制。其中一列没有绑定到DataSource,我连接了CellParsing和CellFormatting事件来处理这个单元格的数据的持久性。我基本上已经绕过了.net数据绑定并实现了我自己的糟糕版本。这不是故意的,它是真正的旧代码,它具有复杂的解析和格式要求,并且我错误地实现了基于未绑定列的解决方案。我现在知道了解决这个问题的正确方法,并且修复了我的代码,但是我仍然想知道如何以另一种方式解决这个错误。

我处理RowValidating事件并做出一个最终验证该行作为一个整体,以确保一切都很酷。当然,如果有问题,我取消验证,行不承诺。当用户通过UI交互进行编辑和添加行时,这一切都可以正常工作,但是在设置DataSource时会产生问题。问题出现是因为在DGV更新其内部列表并构建行时未调用CellFormatting,或者至少在激发验证事件之前未调用它。这会导致RowValidating处理程序从Unbound列中提取空值(因为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()); 
    } 
} 

回答

0

我曾提到,我已经“解决了这一问题。”这是不完全准确。我将它固定在测试应用程序中,有效验证了我计划使用的解决方案,但实际上并未实现它。一旦我尝试了,我意识到为什么我按照我最初的方式做了事情:未绑定列中使用的数据是对象的只读集合,为了设置它们,我需要在对象上调用一个单独的方法。最佳做法和所有...

嘛,当然读的唯一属性,不能在DGV编辑,所以我在水里死了。这使我回到我的原始设计,而研究我偶然发现了this column/blog by Brian Noyes

他提到的事件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; 
} 

,它然后把手拿开格式化责任CellFormatting,我可以做我的工作。同样,我可以解析数据并构造所需的对象以传递给我的绑定对象“SetListData”方法。

一切都好!

相关问题