Clearing errors sometimes changes values

  • Thread starter Thread starter Jon Skeet [C# MVP]
  • Start date Start date
J

Jon Skeet [C# MVP]

I've got a problem which *may* be with the Compact Framework, but may
be in the main framework as well - I'm having a very difficult time
reliably reproducing it, so I thought I'd ask here for further advice.

The principle objects involved are:

o A datarow, with a column in it (call it X)
o A row handler which has some validation logic in it
o A control (derived from ComboBox) with a bound property in it, bound
to column X

We go into the form fine - the possible values and the default value
are all set up appropriately, and the user can select one of the
values. The property gets changed correctly.

When the user hits the OK button, the row handler is called. It
validates the proposed value. This is where the problems occur. The
proposed value is acceptable, and the row handler correctly detects
that. It then sets the row error for the column to null, to make sure
that if there were any previous errors, they are cleared.

Unfortunately, this fires a record changed event, and *sometimes* the
property value in the control is then set back to the row's "current"
value. It doesn't happen every time, and I'm having a hard time working
out what's going on there - any suggestions for a direction in which to
turn are warmly welcomed.

It's a slight shame that the record changed event fires in the first
place, and we could avoid it, although I don't think we should have to.
An error is cleared by setting it to either null or an empty string,
but GetColumnError always returns an empty string if there's no error -
so to naive code, setting the value to null *is* changing it, even
though there's no actual change in any data including the value of
GetColumnErrror afterwards. Even though we can avoid it here, we may
not be able to elsewhere, and sometimes there *will* have been an error
previously - so we need to understand why the old value is being shown
sometimes.

Has anyone else run into this before?
 
Unfortunately, this fires a record changed event, and *sometimes* the
property value in the control is then set back to the row's "current"
value. It doesn't happen every time, and I'm having a hard time working
out what's going on there - any suggestions for a direction in which to
turn are warmly welcomed.

<snip>

Slight progress. It appears that the problem only surfaces if the row
(or rather row view) being edited isn't the first in the DataView it's
being plucked from.

With that information, it should be somewhat easier to construct a
short but complete test case. We'll see :)
 
Jon Skeet said:
Slight progress. It appears that the problem only surfaces if the row
(or rather row view) being edited isn't the first in the DataView it's
being plucked from.

With that information, it should be somewhat easier to construct a
short but complete test case. We'll see :)

Hmm. I now have a complete test case. This runs on both the compact and
the desktop framework. Change the value of the combo box and hit "end
edit". The final value should be shown on the label as well as in the
combo box. Change the row number and recompile to see different effects
on the Compact Framework.

Results:

Either row on the desktop framework: value goes back to "Old value (x)"
Row 0 on the compact framework: value changes to the newly selected one
Row 1 on the compact framework: value goes back to "Old value 1"


A few more interesting results:
o Using SelectedItem instead of ValueProxy gives the same results
o Using SelectedIndex (with a different column type, obviously) flicks
back to the old value, but then "settles" on the new one, for all
rows and on both desktop and compact frameworks.
o Putting a bit of debug information in ValueProxy shows the following
occurring in the code shown below:

Desktop (either row, text changes in obvious way):
Before display:
Setting value to Old value 0
Setting value to Old value 0

Select "Hello" and then hit "End edit":
Fetching value: Hello
Setting value to Hello
Setting value to Old value 0
Fetching value: Old value 0
Setting value to Old value 0
Setting value to Old value 0


Compact framework - row 0:
Before display:
Setting value to Old value 0
Fetching value: Old value 0
Setting value to Old value 0
Setting value to Old value 0

Select "Hello" and then hit "End edit":
Fetching value: Hello
Setting value to Hello
Setting value to Old value 0
Setting value to Hello
Fetching value: Hello
Setting value to Hello
Setting value to Hello


Compact framework - row 1:
Before display:
Setting value to Old value 0
Fetching value: Old value 0
Setting value to Old value 0
Setting value to Old value 0
Fetching value: Old value 0
Setting value to Old value 0
Setting value to Old value 1

Select "Hello" and then hit "End edit":
Fetching value: Hello
Setting value to Hello
Setting value to Old value 1


Goodness knows what the difference between using SelectedItem and
SelectedIndex really is.



So, any ideas? Code is below.



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

public class BindingProblem : Form
{
DataTable table;
DataView view;

const int RowNumber = 1;

Label output;

public BindingProblem()
{
MinimizeBox = false;

table = new DataTable();
table.Columns.Add("Foo", typeof(string));
table.Rows.Add(new string[] {"Old value 0"});
table.Rows.Add(new string[] {"Old value 1"});
table.ColumnChanging +=new DataColumnChangeEventHandler
(ValidateColumn);
view = table.DefaultView;

ExtendedComboBox combo = new ExtendedComboBox();
combo.Items.Add("Hello");
combo.Items.Add("World");
combo.Items.Add("Old value 0");
combo.Items.Add("Old value 1");
combo.Size = new Size (200, 20);
combo.Location = new Point (10, 10);

Controls.Add(combo);

// Add a button to end the edit
Button button = new Button();
button.Text = "End edit";
button.Size = new Size (70, 20);
button.Location = new Point (10, 40);
button.Click +=new EventHandler(ButtonClicked);
Controls.Add(button);

// Something to show the result
output = new Label();
output.Size = new Size (240, 20);
output.Location = new Point (10, 70);
Controls.Add(output);

// Now do the data binding stuff
combo.DataBindings.Add("ValueProxy", view, "Foo");
CurrencyManager cm = (CurrencyManager) BindingContext
[view, ""];
cm.Position = RowNumber;

}

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

void ButtonClicked(object sender, EventArgs e)
{
CurrencyManager cm = (CurrencyManager) BindingContext
[view, ""];
cm.EndCurrentEdit();

output.Text = view[RowNumber]["Foo"].ToString();
}

void ValidateColumn(object sender, DataColumnChangeEventArgs e)
{
e.Row.SetColumnError("Foo", null);
}
}

public class ExtendedComboBox : ComboBox
{
public object ValueProxy
{
get
{
int index = SelectedIndex;
if (index==-1)
{
return "";
}
return Items[index];
}
set
{
int index = Items.IndexOf(value);
SelectedIndex = index;
}

}
}
 
Back
Top