locked
Entity Field PropertyChanged RRS feed

  • Question

  • Hi everyone,

    I can't seem to figure out how to get the PropertyChanged Event for an Entity Field. Let me explain:

    If we had a property "Name" in the ViewModel:

     string _name;
                public string Name{
                    get {
                        return _name;
                    }
                    set {
                        _name= value;
                        PropertyChanged(this, new PropertyChangedEventArgs("Name"));                    
                    }
                }
    And in the View we had a text box like this: Text="{Binding Name, Mode=TwoWay}" then it would be easy to catch
    the setter of the property in the ViewModel. But what if we had in the ViewModel a property wich is an Entity, let's say
    Person and we want to cath the Person.Name changed event? In the the view (XAML) now we would have the text box's
    text property bound like this: Text="{Binding Person.Name, Mode=TwoWay}".

    How can I catch in the View Model that the text box value has changed? or, in other words, that the Person.Name was
    changed?

    Thanks in advance
    Saturday, January 7, 2012 12:33 PM

Answers

  • Oh wow! you made that sample to try to help me?

    ...

    It's pretty simple actually, but you're welcome.

    So to use MVVM (which I like a lot), I use a pattern where neither the view nor the viewmodel know about each other.  In fact, I usually put each in its own assembly.  But again for simplicity, I haven't done separate assemblies here.

    I like to have the Page know about the View and the Viewmodel and let the Page create them and hook them up. 

    After some minor refactoring, I have this for the Page (NO code behind):

    <?xml version='1.0' encoding='utf-8' ?>
    <UserControl 
       
       x:Class="SilverlightApplication72.MainPage"
       
       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:views="clr-namespace:MyNamespace.Views"
       xmlns:viewmodels="clr-namespace:MyNamespace.Viewmodels"
       
       mc:Ignorable="d"
       d:DesignHeight="300" d:DesignWidth="400"
       >
       <UserControl.Resources>
          <viewmodels:MyViewModel x:Key="vm1"/>
       </UserControl.Resources>
    
       <Grid x:Name="LayoutRoot" Background="White" Margin="10">
          <views:MyView DataContext="{StaticResource vm1}"/>
       </Grid>
    </UserControl>
    

    And this for the View (NO code behind):

    <UserControl 
       
       x:Class="MyNamespace.Views.MyView"
       
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
       Width="400" Height="300"
       >
       <StackPanel>
          <TextBox Text="{Binding Person.Name, Mode=TwoWay}" HorizontalAlignment="Left"/>
          <TextBox Text="{Binding Person.Age,  Mode=TwoWay}" HorizontalAlignment="Left"/>
       </StackPanel>
       
    </UserControl>
    

    And this for the Viewmodel:

    namespace MyNamespace.Viewmodels;
    
    interface
    
       uses
          System.ComponentModel,
          System.Text;
    
    
       type
          Person = public class( INotifyPropertyChanged )
             private
                fAge: integer;
                fName: String;
                method Person_PropertyChanged(sender: Object; e: PropertyChangedEventArgs);
                method SetAge(value: integer);
                method SetName(value: String);
             public
                constructor;
                property Name : String read fName write SetName;
                property Age : integer read fAge write SetAge;
                event PropertyChanged : System.ComponentModel.PropertyChangedEventHandler;
             end;
    
          MyViewModel = public class
             private
                method Person_PropertyChanged(sender: Object; e: System.ComponentModel.PropertyChangedEventArgs);
             protected
             public
                constructor;
                property Person : Person; notify;
             end;
    
    implementation
    
       uses
          System.Windows;
    
       constructor MyViewModel;
          begin
          Person := new Person;
          Person.PropertyChanged += Person_PropertyChanged;
          end;
    
       constructor Person;
          begin
          PropertyChanged += Person_PropertyChanged;
          end;
    
       method Person.Person_PropertyChanged(sender: Object; e: PropertyChangedEventArgs);
          begin
          MessageBox.Show( e.PropertyName + ' changed in Person.' );
          end;
    
       method Person.SetName(value: String);
          begin
          fName := value;
          if assigned( PropertyChanged ) then PropertyChanged( self, new PropertyChangedEventArgs( 'Name' ) );
          end;
    
       method Person.SetAge(value: integer);
          begin
          fAge := value;
          if assigned( PropertyChanged ) then PropertyChanged( self, new PropertyChangedEventArgs( 'Age' ) );
          end;
    
    
       method MyViewModel.Person_PropertyChanged(sender: Object; e: System.ComponentModel.PropertyChangedEventArgs);
          begin
          MessageBox.Show( e.PropertyName + ' changed in Viewmodel Person.' );
          end;
    
    end.
    

    You should be able to just type the same sort of thing into C# and it will help you.  The only part that is mostly different is that I have that "notify" directive in Delphi Prism (which I like a lot).  But the Person class shows how to do that in the clunky C# way.

    C# also mixes up the declaration/interface section with the implementation section.  Delphi Prism just splits those up so that it is easier to read and better organized (amongst other things).  But you can mush those together in your C#.

    As was mentioned, watch the videos under the Learn tab above.  That is how I learned to do stuff.  And I had to translate from C# to Delphi Prism and do some things manually because I didn't have as many templates to get started.

    Try typing stuff yourself and see what the IDE says.  I think you should have to do SOMETHING ;-)

    Sunday, January 8, 2012 6:02 AM

All replies

  • I'm confused.  You talk about a property called "Name", but your code shows a property called "TextBoxValue".  So if you bind "Name" to your Viewmodel, there won't BE a correct binding.  So changes in the TextBox (or whatever control contains the Text binding) won't be reflected in your Viewmodel property.

    I think, although I don't use Entity Framework where I assume your "Entity" comes from, you should be able to add a PropertyChanged listener to Person (assuming Person implements INotifyPropertyChanged).  Then check to see when the "Name" is the property that was changed.

    Saturday, January 7, 2012 1:17 PM
  • Hi, thanks for the answer. I was first writing the code as an example and to make it clearer I changed TextBoxValue with Name, but seems it was not updated. It is corrected now in my other post.

    Anyway that is not the actual code, is just an example, so misspelling is not the problem. 

    I think, although I don't use Entity Framework where I assume your "Entity" comes from, you should be able to add a PropertyChanged listener to Person (assuming Person implements INotifyPropertyChanged).  Then check to see when the "Name" is the property that was changed.

    Yes, I'm able to raise the PropertyChanged event from person, but the problem is that Person is not bound to any control, but the fields that Person contains are. That's the problem, that though I change a text box bound to a Person field (like name, or age, etc..) the execution thread never reaches the setter of the Person property. 

    A possible workaround would be to create the Name property and whenever it changes in the setter make something like:

    Person.Name = value;

    but I do not think this would be very elegant... there might be a way to cath the changed event of a Property field...

    Did I explain it better? Sorry it is kinda messy and is hard to explain...

    Saturday, January 7, 2012 3:05 PM
  • I'm not quite sure what you want, but this does what I think you want.  Here is the xaml:

    <?xml version='1.0' encoding='utf-8' ?>
    <UserControl 
       
       x:Class="SilverlightApplication72.MainPage"
       
       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"
       mc:Ignorable="d"
       d:DesignHeight="300" d:DesignWidth="400"
       >
    
       <Grid x:Name="LayoutRoot" Background="White" Margin="10">
          <StackPanel>
             <TextBox Text="{Binding Person.Name, Mode=TwoWay}" HorizontalAlignment="Left"/>
             <TextBox Text="{Binding Person.Age,  Mode=TwoWay}" HorizontalAlignment="Left"/>
          </StackPanel>
       </Grid>
    </UserControl>
    

    Here is some code (in Delphi Prism and only has code behind for simplicity):

    namespace SilverlightApplication72;
    
    interface
    
       uses
          System,
          System.ComponentModel,
          System.Windows,
          System.Windows.Controls;
    
       type
          Person = public class( INotifyPropertyChanged )
            private
               fAge: integer;
               fName: String;
               method SetAge(value: integer);
               method SetName(value: String);
            public
               property Name : String read fName write SetName;
               property Age : integer read fAge write SetAge;
               event PropertyChanged : System.ComponentModel.PropertyChangedEventHandler;
            end;
    
          MainPage = public partial class(System.Windows.Controls.UserControl)
             private
                method p_PropertyChanged(sender: Object; e: PropertyChangedEventArgs);
                method MainPage_Loaded(sender: Object; e: RoutedEventArgs);
             public
                constructor;
                property Person : Person; notify;
             end;
    
    
    implementation
    
       constructor MainPage; 
          begin
          InitializeComponent();
          Loaded += MainPage_Loaded;
          end;
    
       method MainPage.MainPage_Loaded(sender: Object; e: RoutedEventArgs);
          begin
          Person := new Person;
          Person.PropertyChanged += p_PropertyChanged;
          DataContext := self;
          end;
    
    
       method MainPage.p_PropertyChanged(sender: Object; e: PropertyChangedEventArgs);
          begin
          MessageBox.Show( e.PropertyName + ' changed.' );
          end;
    
       method Person.SetName(value: String);
          begin
          fName := value;
          if assigned( PropertyChanged ) then PropertyChanged( self, new PropertyChangedEventArgs( 'Name' ) );
          end;
    
       method Person.SetAge(value: integer);
          begin
          fAge := value;
          if assigned( PropertyChanged ) then PropertyChanged( self, new PropertyChangedEventArgs( 'Age' ) );
          end;
    
    end.
    

    Although I built-out the Person property change notification, I didn't bother to do it for the Person instance that is in the Page.  Delphi Prism has a Notify directive that does all of that for me. 

    But the point is that even though the binding is to Person.Name, change notification still happens.

    Saturday, January 7, 2012 3:34 PM
  • And here is both the Page AND Person listening for changes to the property (in case you want Person to be notified of its own changes for some reason):

    namespace SilverlightApplication72;
    
    interface
    
       uses
          System,
          System.ComponentModel,
          System.Windows,
          System.Windows.Controls;
    
       type
          Person = public class( INotifyPropertyChanged )
             private
                fAge: integer;
                fName: String;
                method Person_PropertyChanged(sender: Object; e: PropertyChangedEventArgs);
                method SetAge(value: integer);
                method SetName(value: String);
             public
                constructor;
                property Name : String read fName write SetName;
                property Age : integer read fAge write SetAge;
                event PropertyChanged : System.ComponentModel.PropertyChangedEventHandler;
             end;
    
          MainPage = public partial class(System.Windows.Controls.UserControl)
             private
                method p_PropertyChanged(sender: Object; e: PropertyChangedEventArgs);
                method MainPage_Loaded(sender: Object; e: RoutedEventArgs);
             public
                constructor;
                property Person : Person; notify;
             end;
    
    
    implementation
    
       constructor MainPage; 
          begin
          InitializeComponent();
          Loaded += MainPage_Loaded;
          end;
    
       method MainPage.MainPage_Loaded(sender: Object; e: RoutedEventArgs);
          begin
          Person := new Person;
          Person.PropertyChanged += p_PropertyChanged;
          DataContext := self;
          end;
    
    
       method MainPage.p_PropertyChanged(sender: Object; e: PropertyChangedEventArgs);
          begin
          MessageBox.Show( e.PropertyName + ' changed.' );
          end;
    
       method Person.SetName(value: String);
          begin
          fName := value;
          if assigned( PropertyChanged ) then PropertyChanged( self, new PropertyChangedEventArgs( 'Name' ) );
          end;
    
       method Person.SetAge(value: integer);
          begin
          fAge := value;
          if assigned( PropertyChanged ) then PropertyChanged( self, new PropertyChangedEventArgs( 'Age' ) );
          end;
    
       constructor Person;
          begin
          PropertyChanged += Person_PropertyChanged;
          end;
    
       method Person.Person_PropertyChanged(sender: Object; e: PropertyChangedEventArgs);
          begin
          MessageBox.Show( e.PropertyName + ' changed in Person.' );
          end;
    
    end.

    Saturday, January 7, 2012 3:37 PM
  • Oh wow! you made that sample to try to help me? Thanks a lot! I got a little bit lost in the Delphi part... I mean, it is readable, but I'm not used to it. Is this applyable to MVVM? I got the impression that it is being handled in the View... any hint on how to pass all this to C#? It is with MVVM and C# that I'm having some problems on firing the PropertyChanged event (in the ViewModel) of the property when a field changes...

    Trully, thanks a lot for the time you are taking!

    Saturday, January 7, 2012 4:01 PM
  • Hi,

    Your question is actually very fundamental, you need to see and do some excercises, and you can find samples of exercise at http://msdn.microsoft.com/en-us/silverlight4trainingcourse.aspx

    Have fun

    Saturday, January 7, 2012 7:14 PM
  • Oh wow! you made that sample to try to help me?

    ...

    It's pretty simple actually, but you're welcome.

    So to use MVVM (which I like a lot), I use a pattern where neither the view nor the viewmodel know about each other.  In fact, I usually put each in its own assembly.  But again for simplicity, I haven't done separate assemblies here.

    I like to have the Page know about the View and the Viewmodel and let the Page create them and hook them up. 

    After some minor refactoring, I have this for the Page (NO code behind):

    <?xml version='1.0' encoding='utf-8' ?>
    <UserControl 
       
       x:Class="SilverlightApplication72.MainPage"
       
       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:views="clr-namespace:MyNamespace.Views"
       xmlns:viewmodels="clr-namespace:MyNamespace.Viewmodels"
       
       mc:Ignorable="d"
       d:DesignHeight="300" d:DesignWidth="400"
       >
       <UserControl.Resources>
          <viewmodels:MyViewModel x:Key="vm1"/>
       </UserControl.Resources>
    
       <Grid x:Name="LayoutRoot" Background="White" Margin="10">
          <views:MyView DataContext="{StaticResource vm1}"/>
       </Grid>
    </UserControl>
    

    And this for the View (NO code behind):

    <UserControl 
       
       x:Class="MyNamespace.Views.MyView"
       
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
       Width="400" Height="300"
       >
       <StackPanel>
          <TextBox Text="{Binding Person.Name, Mode=TwoWay}" HorizontalAlignment="Left"/>
          <TextBox Text="{Binding Person.Age,  Mode=TwoWay}" HorizontalAlignment="Left"/>
       </StackPanel>
       
    </UserControl>
    

    And this for the Viewmodel:

    namespace MyNamespace.Viewmodels;
    
    interface
    
       uses
          System.ComponentModel,
          System.Text;
    
    
       type
          Person = public class( INotifyPropertyChanged )
             private
                fAge: integer;
                fName: String;
                method Person_PropertyChanged(sender: Object; e: PropertyChangedEventArgs);
                method SetAge(value: integer);
                method SetName(value: String);
             public
                constructor;
                property Name : String read fName write SetName;
                property Age : integer read fAge write SetAge;
                event PropertyChanged : System.ComponentModel.PropertyChangedEventHandler;
             end;
    
          MyViewModel = public class
             private
                method Person_PropertyChanged(sender: Object; e: System.ComponentModel.PropertyChangedEventArgs);
             protected
             public
                constructor;
                property Person : Person; notify;
             end;
    
    implementation
    
       uses
          System.Windows;
    
       constructor MyViewModel;
          begin
          Person := new Person;
          Person.PropertyChanged += Person_PropertyChanged;
          end;
    
       constructor Person;
          begin
          PropertyChanged += Person_PropertyChanged;
          end;
    
       method Person.Person_PropertyChanged(sender: Object; e: PropertyChangedEventArgs);
          begin
          MessageBox.Show( e.PropertyName + ' changed in Person.' );
          end;
    
       method Person.SetName(value: String);
          begin
          fName := value;
          if assigned( PropertyChanged ) then PropertyChanged( self, new PropertyChangedEventArgs( 'Name' ) );
          end;
    
       method Person.SetAge(value: integer);
          begin
          fAge := value;
          if assigned( PropertyChanged ) then PropertyChanged( self, new PropertyChangedEventArgs( 'Age' ) );
          end;
    
    
       method MyViewModel.Person_PropertyChanged(sender: Object; e: System.ComponentModel.PropertyChangedEventArgs);
          begin
          MessageBox.Show( e.PropertyName + ' changed in Viewmodel Person.' );
          end;
    
    end.
    

    You should be able to just type the same sort of thing into C# and it will help you.  The only part that is mostly different is that I have that "notify" directive in Delphi Prism (which I like a lot).  But the Person class shows how to do that in the clunky C# way.

    C# also mixes up the declaration/interface section with the implementation section.  Delphi Prism just splits those up so that it is easier to read and better organized (amongst other things).  But you can mush those together in your C#.

    As was mentioned, watch the videos under the Learn tab above.  That is how I learned to do stuff.  And I had to translate from C# to Delphi Prism and do some things manually because I didn't have as many templates to get started.

    Try typing stuff yourself and see what the IDE says.  I think you should have to do SOMETHING ;-)

    Sunday, January 8, 2012 6:02 AM
  • Hi again!

    It's pretty simple actually, but you're welcome.

    Complexity doesnt matter for me, the spent time and your consideration is what I'm glad of :-)

    It might be a fundamental question but I'm not that familiar to MVVM as I'm to the Silverlight technology itself. I usually try to find samples, videos or tutorials before asking, so thanks for pointing me to a helping one.

    I will be playing around with all the info you guys provided me. 

    Thanks a lot!

    Monday, January 9, 2012 12:55 AM