cchampion
cchampion

Reputation: 7921

Why does the data binding not work when i set the data context in my button click handler?

Edit: i have simplified this problem a lot since i first wrote it up. See the code examples at the bottom. I will clean the post up tomorrow.

I want to set the DataContext AFTER the user as filled out a form because that is when I know the type of object I need to create (based on what the user selected). The problem with doing it this way is that the BindingGroup.IsDirty flag is false because all edits are done by this point and I suppose that is why my call to BindingGroup.UpdateSources is having no effect.

Essentially this is what I'm doing:

  1. Have user fill out form. The DataContext has not been set yet, but bindings are in place.
  2. In the buttonSave_Click handler, create and set the correct DataContext object (based on provided user input) then call BindingGroup.UpdateSources and close the dialog.

I realize there are other ways to go about this. Probably even better ones, but I am really just experimenting with DataBinding and trying to learn it better. That is why I am trying out many possible designs with this.

Ultimately I will probably settle on a design where I ask the questions that I need answered in order to know what object to create for the data context then set it and let them fill out the remainder of the form from there. But for now I want to get it working the other way (if it is even possible) just for learning purposes.

EDIT 1:

I have determined that the IsDirty flag must not be my problem because immediately before the DataContext is set, IsDirty is true. Then immediately after DataContext is set it becomes false so I suppose it automatically updated sources then, however, I do not see the changes reflected in my data object so it obviously failed for some reason.

I know my Bindings are correct because if I set the DataContext in the Windows ctor it updates the data. Move those two lines of code to the buttonSave_Click handler and it no longer updates data.

I feel like there is something that I am missing that is going to me make me feel reaaallly stupid when I figure it out :\

UPDATE 1:

I have determined that the BindingExpression.Worker.CanUpdate is false. By looking at the source code of BindingExpression I can see that that would cause it to fail. Now to figure out why that is false....

UPDATE 2:

With all the reading i have been doing i am starting to suspect a timing issue. read this I will be able to try it when i get home

UPDATE 3:

Still working on this. Here is a bit of code. I have simplified to get minimal code.

Here is the data objects.

public abstract class Dog
    {
        public Dog(SexType sex)
        {
            NickName = string.Empty;
        }

        public string NickName { get; set; }
    }

 public class Stud : Dog
    {
        public Stud()
            : base(SexType.Male)
        {
        }
    }

This code works:

//code behind
public partial class AddDogWindow : Window
{
    public Dog NewDog { get; set; }
    public AddDogWindow()
    {
        InitializeComponent();
        //set the DataContext here and the data object will be updated as expected...
        NewDog = new Stud();
        DataContext = NewDog;
    }

    private void buttonSave_Click(object sender, RoutedEventArgs e)
    {
        DialogResult = true;
    }
}

And the XAML

<Window x:Class="PuppyMan.AddDogWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PuppyMan"
        mc:Ignorable="d"
        Title="AddStudDogWindow" Height="300" Width="300">
    <StackPanel>
        <TextBox HorizontalAlignment="Stretch" Text="{Binding NickName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
        <Button x:Name="buttonSave" Click="buttonSave_Click">Save</Button>
    </StackPanel>
</Window>

THIS is the code that BREAKS.

  public partial class AddDogWindow : Window
    {
        public Dog NewDog { get; set; }
        public AddDogWindow()
        {
            InitializeComponent();
        }

        private void buttonSave_Click(object sender, RoutedEventArgs e)
        {
            //Notice all I have changed is moving the set DataContext out of the ctor and into this handler. 
            //Now the data object no longer gets updated. 
            NewDog = new Stud();
            DataContext = NewDog;
            DialogResult = true;
        }
    }

The idea is that I let them fully fill out the dog info and then I only create the Stud or Dame dog object based on the final decision of whether the dog is male or female. A kind of lazy data binding I suppose. I know this isn't the best design for this situation but I want to learn how to do it this way for learning purposes. Or learn why this method won't work, either is fine, I just want to learn this data binding stuff and well!

FINAL UPDATE

This post seems to be essentially the same as my problem. I am using OneWayToSource and it always sets my NickName property to the default value (in this case ""). The problem is NOT that the NickName getter is called after the setter, but that it gets set to "". The UI keeps the original value until I type in the TextBox again and INotifyPropertyChange fires then everything syncs up again.

Seems an awful lot like a bug to me that OneWayToSource would push a "" instead of the current value of the target. But I very well may be miss understanding.

Upvotes: 0

Views: 1546

Answers (2)

cchampion
cchampion

Reputation: 7921

My problem turned out to be the same issue as this post. I am using OneWayToSource and when I change the DataContext my property is set to a default value which is currently "" instead of the value of the target as I was expecting. I have found no work around. I will design such that I know what object to create in advance so that the DataContext will be set in the constructor.

Upvotes: 0

AnjumSKhan
AnjumSKhan

Reputation: 9827

Design your form such that you know beforehand which object you will get. This can probably be done for example by checking if uses fills a particular field, or if he selects some combobox value in your form. In appropriate event of that control you can set your datacontext. And by the way Triggers are most suited here. Set trigger for your object-deciding-form-field. More can be done if you post something more, or upload some source code at dropbox.com.

//////////

You have set Bindings beforehand, and then you are changing the DataContext. Changing the DataContext will not make your TextBox value to reach the NickName property.

Secondly, you have set UpdateSourceTrigger to PropertyChanged. So, after changing your DataContext you have to make this property appear changed.

One way to do this is to :

DataContext = NewDog;

/* this will trigger property changed */
txtDogNickName.Text = txtDogNickName.Text;

string newNickName = NewDog.NickName; // updated value appear here 

But this is not the proper way to do. Change your DataContext sepearately, and then edit values in your form. Changes will propagate correctly. Don't do everything in same handler. For example, you might show this form in some Button1's click handler and set DataContext, then use another Button2's click handler to get form values.

Upvotes: 1

Related Questions