次の方法で共有


Data Binding Part 3- Implementing the INotifyPropertyChanged Interface

In Part 1 of this series I covered the basic theoretical idea behind data binding, and in Part 2, we got a little more concrete with a hands on example in code.  We created a Movie class, then bound the three properties of that object (title, rating, year), to our UI in both Windows and Windows Phone projects using a Universal Windows App.  If you haven’t taken a look at the previous two posts, I would recommend doing so because we will be continuing where we left off in Part 2.

So, where exactly did we leave off in Part 2?  Like I said, we created our movie class and bound an instance of it to our UI.  What I didn't cover was demonstrating the power of data binding in how changing the “data source” will propagate changes to the UI, visa versa, and both.  Remember those four different types of data binding (OneWay, TwoWay, OneWayToSource, and OneTime) waaay back in Part 1?  Now we can take a look at how all of that works.

To propagate changes to and from our Movie object instance, the Movie class will need to implement the INotifyPropertyChanged Interface.  This interface is “used to notify clients, typically binding clients, that a property value has changed”, which is exactly what we are looking for.  We want our UI to reflect any changes made to the data source, the Movie object.  That said, we can keep the majority of the code that we already created in our Movie class but only making four small changes; declare that we are implementing the interface, modify the “set” methods for each of the properties, implement the NotifyPropertyChanged method, and declare a PropertyChangedEventHandler called PropertyChanged.  Here is the how to.

1. Simply add “:INotifyPropertyChaned” after the name of our class.  Will look like this.

class Movie:INotifyPropertyChanged {

2. Secondly, the new “set” methods for each of our properties should make a call to the NotifyPropertyChanged method passing it’s name as a parameter.  For example, in the set method for our Title property, it will look like this.

set

{

title=value;

NotifyPropertyChanged(“Title”);

}

3. Third, our new PropertyChangedEventHandler will look like so.

protected virtual void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
             {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
         }

4. simply declare PropertyChanged like so

public event PropertyChangedEventHandler PropertyChanged;

All in all, my movie.cs class now looks like so.

class Movie:INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private string title;
        private string rating;
        private string year;
        public string Title
        {
            get{return title;}
             set
            {
                title=value;
                 NotifyPropertyChanged("Title");
            }
        }
        public string Rating
        {
            get { return rating; }
            set
            {
                rating = value;
                NotifyPropertyChanged("Rating");
             }
        }
        public string Year
        {
             get { return year; }
            set
            {
                 year = value;
                NotifyPropertyChanged("Year");
            }
        }

        protected virtual void NotifyPropertyChanged(string propertyName)
        {
             if (PropertyChanged != null)
            {
                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

    }

If we save now and run our project again, we will see exactly what we ended with in Part 2.  So what exactly did we change?  Well, to show you, we are going to add a button to our UI, and in the click handler for the button, we will dynamically change the data source.  When we do, our UI should be updated demonstrating that the data binding has truly taken place.  So, go ahead and add a button in our stack panel.  Mine will just say “Click Me” and then I will add an event handler.

** A little trick in case you didn't know.  If you add the button in XAML and then start to set the “Click” property, Visual Studio will give you the option to press tab and have it generate the handler for you.  Trust me, this is extremely handy! 

I bumped up the text size and set the horizontal alignment also, so my button looks like this.

<Button Content="Click Me" Click="Button_Click" FontSize="54" HorizontalAlignment="Center"/>

Now in the event handler we just want to change the instance of the Movie class object that we are bound to.  We can do this by using the set method for any of the three properties.  My original movie displays information about X-Men, so in the update, I will feature another movie I watched recently, Draft Day (awesome movie if you haven’t seen it.)  Coincidentally, Draft Day and X-Men have the same rating and year property, so we only need to change the title property.  Therefore, my handler looks like this.

private void Button_Click(object sender, RoutedEventArgs e)
       {
           movie.Title = "Draft Day";
       }

If you run your app now and click your button, the UI should update to reflect the new movie title!  Remember, this is all occurring without us having to manually update our UI every time there is a change.  Might not seem as impressive in this simple example, but you can imagine how amazing this is with data that is constantly changing!

Now that we have that working, let’s take a look at TwoWay binding, which is especially useful if you are collecting information from a user with Textboxes.  With TwoWay binding, if the user updates information in a text box, the underlying data source will also be updated.  In other words, if we let the user have some input, they could change the different properties of our Movie object from the UI, all with minimal effort on our part.

To demo this, I’m going to abandon the StackPanel that we were using and use a grid with two columns and four rows.  There will be four rows, one for each of the object properties and one for our button.  On the left there will be the three Textblocks we already created, and the right three Textboxes that are bound to the same properties but using TwoWay binding as it’s mode.  I won’t cover the details of how to set that up, but you can copy and paste this code if you are having trouble.  Keep in mind, I am using custom styles, so you might have to alter that part.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            < RowDefinition Height="*"/>
             <RowDefinition Height="*"/>
            < RowDefinition Height="*"/>
            < RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        < Grid.ColumnDefinitions>
             <ColumnDefinition Width="*"/>
            < ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        < TextBlock Text="{Binding Title}" Style="{StaticResource TextStyle}" Grid.Column="0"  Grid.Row="0"/>
        < TextBlock Text="{Binding Rating}" Style="{StaticResource TextStyle}" Grid.Column="0" Grid.Row="1"/>
        < TextBlock Text="{Binding Year}" Style="{StaticResource TextStyle}" Grid.Column="0" Grid.Row="2"/>
        <Button Content="Click Me" Click="Button_Click" FontSize="54" HorizontalAlignment="Center" Grid.Column="0" Grid.Row="3"/>
        < TextBox Text="{Binding Title, Mode=TwoWay}" Style="{StaticResource TextBoxStyle}" Grid.Column="1"  Grid.Row="0"/>
        < TextBox Text="{Binding Rating, Mode=TwoWay}" Style="{StaticResource TextBoxStyle}" Grid.Column="1" Grid.Row="1"/>
        < TextBox Text="{Binding Year, Mode=TwoWay}" Style="{StaticResource TextBoxStyle}" Grid.Column="1" Grid.Row="2"/>
    </Grid>

If you run your app now, you might be a little confused as to what is going on.  You should see the same information on the left as on the right as well as your “Click Me” button.  Since both the Textboxes and the Textblocks are bound to the same data source, if we click our button, the changes should propagate to both like so.

Screenshot (212)  -->  -->  -->  Screenshot (211)

This still demonstrates OneWay binding. Now, let’s test the TwoWay binding by typing in one of the Textboxes on the right and then tabbing away from it.  I’m going to type in “Divergent” as the title in the Textbox, R as the rating, and then tab away.    This is what happens.

Screenshot (211)  -->  -->  -->  Screenshot (213)

The left side is automatically populated from the changes made on the right.  It is sort of tricky so let’s walk through what is happening.  When we change the text in the Textboxes, since they are using TwoWay binding, the data source (our Movie object instance) is being updated in the background.  Then, since our Textblocks are bound to that same data source, they are updated as well.  It’s a cool chain reaction example that I walked through myself when I was working with TwoWay binding.  Pretty cool huh?

We have experimented with OneWay and TwoWay binding, so what’s next.  Well, you might be thinking that in a real world application, I might want to display a list of movies instead of simply one movie.  That would be helpful if we wanted to, for example, list the movies that are currently in theater.  For this, we will use what is called an ObservableCollection.  Stay tuned for Part 4!

Feel free to follow me on twitter for upcoming posts! @jquickwit  As always comment with any questions, comments, or concerns below!

Comments

  • Anonymous
    December 27, 2014
    Great job! You finally nailed it for me... for past several hours, I was utterly confused with data binding! Finally I see light! I am so grateful to you!

  • Anonymous
    December 31, 2014
    Glad I could help!  I'm always looking for more content to put up so if you have any suggestions feel free to let me know!

  • Anonymous
    January 05, 2015
    TKS VERRY MUCH. A SAMPLE SIMPLE BUT GOOD !

  • Anonymous
    January 05, 2015
    YOU CAN CREATE A SAMPLE NEW , BUT BINDING DATA FROM LIST OR GRIDVIEW. TKS VERRY MUCH

  • Anonymous
    January 06, 2015
    You're very welcome.  Thanks for following along!

  • Anonymous
    June 15, 2015
    I am perplexed.  I am new to visual studio, however, I set up a mock SQL data binding with Linq populating a form with textboxes and checkboxes form a sql table, which woks, as expected.  Unfortunately, I want to use one of the data binding field values to show a hidden panel referencing another table populating a dataGrid. Issue:  Data binding does not take priority of completing all of the fields since the field changed value Procedure takes priority.  the immediate attention goes to the textbox value change and does not complete my secondary panel view since the data populating seems to be secondary.  How can I use a data binding field value to generate a subroutine AFTER all fields are populated? Thanks, Sudz