ColumnChanging e.ProposedValue Bug

C

clive

Hi everyone,

I've had this problem with the CF for some time now, and I thought I'd
let the public know about it, in the hopes that it doesn't bother other
people as much as it bothered me!

It happens when you have a ColumnChanging event listener, that during
the event, changes the value of another column within the same row (or
any other row in the table for all I know).

It's hard to explain, so I thought the best way was to write a small
application to show what I mean.

If this is a .NET application, then the fix is not required.
If this is a .NET CF application, then the fix is required.

Is there a reason why this occurs and any other sort of fixes for this?

using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;

namespace ColumnChangingBug
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.MainMenu mainMenu1;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;

DataColumn gstCol;
DataColumn priceCol;
DataTable dt;
bool fix;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

DataSet ds = new DataSet();
dt = new DataTable("bug");
priceCol = new DataColumn("price", typeof(decimal));
gstCol = new DataColumn("gst", typeof(decimal));
dt.Columns.Add(priceCol);
dt.Columns.Add(gstCol);
ds.Tables.Add(dt);

dt.ColumnChanging += new
DataColumnChangeEventHandler(Form1_ColumnChanging);
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.mainMenu1 = new System.Windows.Forms.MainMenu();
this.textBox1 = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(8, 8);
this.textBox1.Multiline = true;
this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.textBox1.Size = new System.Drawing.Size(216, 216);
this.textBox1.Text = "";
//
// button1
//
this.button1.Location = new System.Drawing.Point(8, 232);
this.button1.Text = "Without Fix";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Location = new System.Drawing.Point(88, 232);
this.button2.Text = "With Fix";
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// Form1
//
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.button2);
this.Menu = this.mainMenu1;
this.Text = "Form1";

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>

static void Main()
{
Application.Run(new Form1());
}

private void AddLine(string line)
{
textBox1.Text += @"
" + line;
}

private void button1_Click(object sender, System.EventArgs e)
{
fix = false;
Execute();
}

private void button2_Click(object sender, System.EventArgs e)
{
fix = true;
Execute();
}

private void Execute()
{
decimal ten = 10m;

DataRow row = dt.NewRow();
dt.Rows.Add(row);
row[priceCol] = decimal.Zero;
row[priceCol] = ten;

if(row[priceCol] .Equals( ten ))
System.Windows.Forms.MessageBox.Show("set to 10 correctly", "Test
passed");
else
System.Windows.Forms.MessageBox.Show("set to 1 incorrectly", "Test
FAILED");
}

private void Form1_ColumnChanging(object sender,
DataColumnChangeEventArgs e)
{
decimal realProposedValue = (decimal)e.ProposedValue;
AddLine("Form1_ColumnChanging column: " + e.Column.ColumnName);
AddLine("Form1_ColumnChanging ProposedValue: " + e.ProposedValue);

if(e.Column == priceCol)
{
AddLine("Changing GST column = " + e.ProposedValue + " * 0.1");
AddLine("Current Proposed Value = " + e.ProposedValue);
AddLine("Form1_ColumnChanging RealProposedValue: " +
realProposedValue);
e.Row[gstCol] = (decimal)e.ProposedValue * 0.1m;
AddLine("Screwed up ProposedValue now = " + e.ProposedValue);
AddLine("GST = " + e.Row[gstCol]);

if(fix)
{
e.ProposedValue = realProposedValue;
AddLine("Fixed up ProposedValue = " + e.ProposedValue);
}
}
}
}
}
 
C

clive

I recon if you add in the line

Form1.MinimiseBox = false;

It would let you quit, I just threw it together in VS so neglected this
problem :)
 
I

Ilya Tumanov [MS]

Thank you for reporting this problem. I was able to reproduce it. It's
caused by reusing same DataColumnChangeEventArgs.

Since it's the same instance, setting value for a second column would change
value for a first column as well.



I would suggest you don't use this practice.

Changing column values in ColumnChanging event would lead to another
ColumnChanging event fired which might lead to another column changed which
would lead...

I'd better stop now since you sure see the picture.



I would suggest using computed column instead, it's more elegant and less
code.



Anyway, this problem has been fixed in CF V2.

So if you're willing to try your luck with recursive events, you can do it
on V2.



Best regards,



Ilya

This posting is provided "AS IS" with no warranties, and confers no rights.

Hi everyone,

I've had this problem with the CF for some time now, and I thought I'd
let the public know about it, in the hopes that it doesn't bother other
people as much as it bothered me!

It happens when you have a ColumnChanging event listener, that during
the event, changes the value of another column within the same row (or
any other row in the table for all I know).

It's hard to explain, so I thought the best way was to write a small
application to show what I mean.

If this is a .NET application, then the fix is not required.
If this is a .NET CF application, then the fix is required.

Is there a reason why this occurs and any other sort of fixes for this?

using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;

namespace ColumnChangingBug
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.MainMenu mainMenu1;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;

DataColumn gstCol;
DataColumn priceCol;
DataTable dt;
bool fix;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

DataSet ds = new DataSet();
dt = new DataTable("bug");
priceCol = new DataColumn("price", typeof(decimal));
gstCol = new DataColumn("gst", typeof(decimal));
dt.Columns.Add(priceCol);
dt.Columns.Add(gstCol);
ds.Tables.Add(dt);

dt.ColumnChanging += new
DataColumnChangeEventHandler(Form1_ColumnChanging);
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.mainMenu1 = new System.Windows.Forms.MainMenu();
this.textBox1 = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(8, 8);
this.textBox1.Multiline = true;
this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.textBox1.Size = new System.Drawing.Size(216, 216);
this.textBox1.Text = "";
//
// button1
//
this.button1.Location = new System.Drawing.Point(8, 232);
this.button1.Text = "Without Fix";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Location = new System.Drawing.Point(88, 232);
this.button2.Text = "With Fix";
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// Form1
//
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.button2);
this.Menu = this.mainMenu1;
this.Text = "Form1";

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>

static void Main()
{
Application.Run(new Form1());
}

private void AddLine(string line)
{
textBox1.Text += @"
" + line;
}

private void button1_Click(object sender, System.EventArgs e)
{
fix = false;
Execute();
}

private void button2_Click(object sender, System.EventArgs e)
{
fix = true;
Execute();
}

private void Execute()
{
decimal ten = 10m;

DataRow row = dt.NewRow();
dt.Rows.Add(row);
row[priceCol] = decimal.Zero;
row[priceCol] = ten;

if(row[priceCol] .Equals( ten ))
System.Windows.Forms.MessageBox.Show("set to 10 correctly", "Test
passed");
else
System.Windows.Forms.MessageBox.Show("set to 1 incorrectly", "Test
FAILED");
}

private void Form1_ColumnChanging(object sender,
DataColumnChangeEventArgs e)
{
decimal realProposedValue = (decimal)e.ProposedValue;
AddLine("Form1_ColumnChanging column: " + e.Column.ColumnName);
AddLine("Form1_ColumnChanging ProposedValue: " + e.ProposedValue);

if(e.Column == priceCol)
{
AddLine("Changing GST column = " + e.ProposedValue + " * 0.1");
AddLine("Current Proposed Value = " + e.ProposedValue);
AddLine("Form1_ColumnChanging RealProposedValue: " +
realProposedValue);
e.Row[gstCol] = (decimal)e.ProposedValue * 0.1m;
AddLine("Screwed up ProposedValue now = " + e.ProposedValue);
AddLine("GST = " + e.Row[gstCol]);

if(fix)
{
e.ProposedValue = realProposedValue;
AddLine("Fixed up ProposedValue = " + e.ProposedValue);
}
}
}
}
}
 
C

clive

Fantastic

I remember a long time ago when the program was in its early
development that there was a really good reason why I didn't use
Expression columns.

I think it had to do with problems when updating the database but I
can't remember. It had problems in both the Desktop and CF versions. If
I can find out why it was on Monday, I'll post it up here.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top