Click here to Skip to main content
15,886,362 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi guys,

This is actually my first question here. I tend to find all the answers browsing the net, but haven't found the answer to that question yet...

*I've created this code especially for the question, look below please!*

I've been trying to figure out if there is any way to do something as follows:
1) I have a view model base with IPropertyChanged
2) I have a model for example a student model with 2 properties - ID and Name.
3) I have a simple MainWindow view, which is set on MainWindowViewModel datacontext.
4) The view shows a datagrid bounded to "Students" from MainWindowViewModel which is an OC of students - ObservableCollection<student>(with random data I inserted).
5) I've configured the datagrid to have SelectedItem = {Binding SelectedStudent}
6) I have set 2 text boxes to the SelectedStudent.ID and SelectedStudent.Name, and the values show up nicely whenever I select a student.
7) I also see the changes in the textbox's text in the UI, but not in code(!!!)
* I've set UpdateSourceTrigger=OnPropertyChanged, and binding Mode=TwoWay

The problem starts whenever I want to try to EDIT the properties values by changing the current value shown in the text boxes.
Maybe I don't understand what I'm doing while I bind this way, but I put a debug breakpoint on the "get/set" of my properties - ID and Name, and nothing seems to invoke the "Set" method(On the SelectedStudent - maybe that's part of my problem?).

This works if i work hard and create 2 new properties like "Id", and "Name" under the view model and bind them to the textboxes instead... I could change their value and the "set" would invoke i could see it on the breakpoint in the properties themselves - the problem starts only when I try to change my OBJECT's child properties...

Do I need to set the stackpanel "local" datacontext(for example) to something else???

Just to let you know, I'm working with PRISM, but I want to make this as simple as possible(if you know the answer in PRISM I would love to know!).


Thanks a lot to everyone who tries to help!

-----------------
CODE(Everything is here!):
-----
ViewModelBase:
C#
public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged([CallerMemberName]string caller = null)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(caller));
        }
    }
}

----------------
MainWindowViewModel:
C#
public class MainWindowViewModel : ViewModelBase
    {
        private ObservableCollection<Student> _students;
        private Student _selectedStudent = null;

        public Student SelectedStudent
        {
            get { return _selectedStudent; }
            set 
            {
                _selectedStudent = value;
                OnPropertyChanged();
            }
        }

        public ObservableCollection<Student> Students
        {
            get { return _students; }
            set
            {
                _students = value;
                OnPropertyChanged();
            }
        }

        public MainWindowViewModel()
        {
            Students = new ObservableCollection<Student>();
            Students.Add(new Student { Id = "0", Name = "Jackson" });
            Students.Add(new Student { Id = "1", Name = "Jenny" });
        }
    }

-----------------
Student:
C#
public class Student : ViewModelBase
{
    private string _id;
    private string _name;

    public string Id
    {
        get { return _id; }
        set { _id = value; OnPropertyChanged(); }
    }

    public string Name
    {
        get { return _name; }
        set { _name = value; OnPropertyChanged(); }
    }

}

-------------------
MainWindow.xaml:
XML
<Window.DataContext>
    <local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <StackPanel Grid.Row="0">
        <TextBlock Text="Name: "/>
        <TextBox Text="{Binding SelectedStudent.Name, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
        <TextBlock Text="Id: "/>
        <TextBox Text="{Binding SelectedStudent.Id, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
    </StackPanel>

    <DataGrid Grid.Row="1" x:Name="dgStudents" ItemsSource="{Binding Students, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" SelectedItem="{Binding SelectedStudent, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" AutoGenerateColumns="True"/>
</Grid>


What I have tried:

Everything I possibly could, many things I saw on the net... Tried going over binding guides but all they showed are mostly things I know.. I haven't found this specific editing case that I'm trying to do - pretty surprising...
Posted
Updated 15-Nov-16 7:51am

Well, they do work as expected.

If you write something in the SelectedTextBox and the text isn't updated, hitting Tab when you are completed, it does update the values in your collection. But for me it works as you want it to, without any changes to the code. If you do have problems you might try this out:

You can force the bindings to updated on TextChanged, i.a. :
XML
<StackPanel Grid.Row="0">
    <TextBlock Text="Name: "/>
    <TextBox  x:Name="txtSelectedName" TextChanged="txtSelectedName_TextChanged" Text="{Binding SelectedStudent.Name, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

With the code behind:
C#
private void txtSelectedName_TextChanged(object sender, TextChangedEventArgs e)
{
    BindingOperations.GetBindingExpression(txtSelectedName, TextBox.TextProperty).UpdateSource();
}

This should take care of your problem.
 
Share this answer
 
v2
Comments
Member 11949544 4-Aug-16 9:16am    
Hi !

I Haven't tried your second solution yet, I am trying to understand why pressing TAB button a few times did work, is it because the focus is not on the textbox? What if I want it to update immediately, on every change in the textbox, is there another way or just use your advice with "TextChanged" event?

By the way just a quick question - does this solution with writing the txtSelectedName_TextChanged in the code behind violate MVVM pattern? I am not sure. If it does, how can I do this the best way with my own event in my view model?

Thanks a lot! great answer :).

EDIT:
----
I did try to put the event in the code behind of MainWindow.xaml.cs but it didn't work too, only after pressing tab tab it did... So no difference from the first thing you mentioned. Did it work right on the spot for you?
Kenneth Haugland 4-Aug-16 15:52pm    
Well, it didn't work right away. I had to build it the second time then all issues were gone. It doesn't per say ruin the MVVM pattern, as the update binding is generic. It doesn't know what the TextBox is bounded to. But I agree, it should not be necessary. BTW: What version of VS are you using?
Member 12929951 10-Jan-18 2:16am    
Hello, Thanks a lot! Using `UpdateSourceTrigger=PropertyChanged` did the magic.
I tried this without the caller name and actually passed the string and it worked for me, another work around if you do not want to use the code behind because I rarely like to do such things is to bind it to a command using the System.Windows.Interactivity class and implementing a Event trigger; however, I will look for something that is being left out. I used an model named student with id and name and was able to bind to it. The only other thing that I can think of is did you actually instantiate Student? In your constructor
C#
SelectedStudent = new Student();
?
Otherwise this works for me.
 
Share this answer
 
v3
Comments
Richard Deeming 15-Nov-16 14:46pm    
The [CallerMemberName] attribute on the caller parameter causes the compiler to automatically insert the name of the calling member - in this case, the property name.

It was introduced for precisely this purpose.

CallerMemberNameAttribute | MSDN[^]
J. Calhoun 15-Nov-16 15:15pm    
I was having trouble with that, but when I actually used the string it worked for me for whatever reason. But thank you for the reference as I will look further into it and have updated my solution to reflect the only thing I see that is missing that maybe could affect the binding.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900