Fields 'resurrecting' themselves when databound...

Posts   
 
    
JSobell
User
Posts: 145
Joined: 07-Jan-2006
# Posted on: 20-Mar-2008 07:04:26   

I've been struggling with this all afternoon, and it may be nothng to do with LLBLGen, but I'm exhausting my list of otherplaces to look...

I have a form full of databound components. They link to an Entity via an object bindingSource. The bindingSource's datamember is set to an entity, and various form components are bound to various fields.

Everything worked fine until I decided to add some programmatic setting of data. When the user enters an 'EnteredBy' value (bound to EnteredBy field) I lookup their email, and attempt to populate that into the 'Email' field. But, as soon as I attempt to write a value into the 'Email' field, the entity calls "SetValue((int)MovementFieldIndex.EnteredBy, value, true)" which proceeds to call each GetValue in the object, and appears to reset the control that was last edited back to its previous value. What is the 'correct' process to follow when committing data to the object (i.e. before .Save() is called) on a databound form?

I would love to post an example, and will try to if nobody has any suggestions, but the current form uses a myriad of third-party components, and I thought I'd put some feelers out there before developing a whole stand-alone example.

Cheers, Jason

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 20-Mar-2008 07:15:36   

How are you setting the Email field? (could you post just that code snippet) because I think that lookup for the email and show up shouldn't call the SetValue on EnteredBy field.

What is the 'correct' process to follow when committing data to the object (i.e. before .Save() is called) on a databound form?

That depends upon what you want to accomplish. For example, you could use ValidateEntityBeforeSave, AuditOnEntitySave, PreProcessValue, and a some entity events. What are you trying to accomplish before save method?

David Elizondo | LLBLGen Support Team
JSobell
User
Posts: 145
Joined: 07-Jan-2006
# Posted on: 20-Mar-2008 07:31:58   

daelmo wrote:

How are you setting the Email field? (could you post just that code snippet) because I think that lookup for the email and show up shouldn't call the SetValue on EnteredBy field.

I tried setting the Text property of thebound control, and writing directly to the Entity. I've finally got something working by setting both the Email and EnteredBy properties on the Entity. If I set either one independently then the other get's 'stonked on' by the underlying object. It's obviously something weird about data binding, as this never happens when using Entities directly.

As pseudo-code, here is the effect I experienced:


Entity current = new MyEntity(123);
(txtEnteredBy and txtEmail are bound to current)
current contains 'Albert' and 'a@a.com'.
existing data shows 'Albert' in txtEntered, and 'a@a.com' in txtEmail.
User enters 'Bob' into txtEnteredby, then clicks '[Save]'
Leave event fires on txtEntered, and runs the following:

 string was = txtEnteredby.Text;
 current.Email = txtEnteredby.Text + "@dodo.com";
 string isnow = txtEnteredby.Text;

'was' contains 'Bob', as expected,
but for some reason 'isnow' has reverted back to 'Albert' again!.

Stepping the code shows the call to SetValue, then a series of calls to GetValue, but the reason is hidden in the compiled LLBLGen code, so I don't know what triggered the fetches.

Cheers, Jason

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 20-Mar-2008 09:51:46   

string was = txtEnteredby.Text; current.Email = txtEnteredby.Text + "@dodo.com"; string isnow = txtEnteredby.Text;

I think the problem is when to use this code, and whether to use TextBox values or entity field values. I have a feeling that you are interfering with the databinding edit-cycle. And that's the above code is called before EndEdit() was called.

JSobell
User
Posts: 145
Joined: 07-Jan-2006
# Posted on: 21-Mar-2008 13:09:16   

Yes, and for those wanting to experiment with it, there's some simple code below that shows it happening with any databound object. Just drop two text boxes, a button, and a BindingSource, then bind Name to the first box, Email to the other.

I always hated data-bound GUIs, and having to code in all these EndCurrentEdit statements is very messy.

In this case you must do both of the statements below marked with "****". It appears to be a common requirement, updating one field based on the value in another, but it turns out to be a little more complex than expected simple_smile

Cheers, Jason


    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        PretendEntity pe = new PretendEntity() { Name = "Fred", eMail = "Fred@initial.com" };

        private void Form1_Load(object sender, EventArgs e)
        {
            bindingSource1.DataSource = pe;
        }

        private void textBox1_Leave(object sender, EventArgs e)
        {
            textBox1.BindingContext[bindingSource1].EndCurrentEdit(); //*************
            textBox2.Text = textBox1.Text + "@where.com";
        }

        private void button1_Click(object sender, EventArgs e)
        {
            BindingContext[bindingSource1].EndCurrentEdit(); //*************
            MessageBox.Show(string.Format("Name={0}, Email={1}", pe.Name, pe.eMail));
        }
    }

    public class PretendEntity
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string eMail { get; set; }
    }

simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 21-Mar-2008 14:13:11   

Hi Jason

Your PretendEntity doesn't have OnChanged events or implement INotifyPropertyChanged - Binding sometimes works differently when these are not present. Though I don't think it matters in this particular case, watch out when debugging other binding issues.

I think the problem is that the default binding created using the designer updates the DataSource on Validation but if you change it to DataSourceUpdateMode.OnPropertyChanged, I think you will achieve what you want.

If you create your bindings by hand in a Load event or whatever, it should look something like this:

    this.textBox1.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.bindingSource1, "Name", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));

If you prefer to use the designer, click on (Advanced) under (DataBindings) and change the DataSourceUpdate mode there.

Make this change for both bindings and it works as expected.

Cheers Simon

PS I've found that whenever you use OnPropertyChanged, you MUST have the preceeding boolean set to True!

JSobell
User
Posts: 145
Joined: 07-Jan-2006
# Posted on: 22-Mar-2008 14:12:01   

Thanks Simon, I'll check this out next week. I must admit that I'm no expert at .NET's data binding, and this is all excellent information. This sort of issue tends to drag out to days of work (often ending with a completely unnecessary bodge workaround), so this information is very much appreciated.

Many thanks, Jason