geminiCoder
geminiCoder

Reputation: 2906

View not updating despite calling PropertyChanged

I have a simple app with a slider and a label. when I move the slider I can see the setter(Points) being called and updating the value, but when calling PropertyChanged the label doesn't update.

What I am expecting it to do is to call the PointsString getter when PropertyChanged is called. Using the debugger I have confirmed that ProperyChanged is being called and that the value of points is being updated. Why is the label bound to PointsString not being updated when the points setter is called?

View.xaml:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:vm="clr-namespace:MyFirstApp.ViewModels"
             x:Class="MyFirstApp.Views.DetailPage">
    <ContentPage.Content>
       <TableView Intent="Form">
           <TableRoot>
               <TableSection Title="Name">
                   <EntryCell Label="Name:" />
                   </TableSection>
                   <TableSection Title="Rating">
                    <ViewCell>
                        <StackLayout Orientation="Horizontal" >
                            <Label Text="Points:"  />
                            <Label Text="{Binding PointsString}">
                                <Label.BindingContext>
                                    <vm:DetailsPageViewModel />
                                </Label.BindingContext>
                            </Label>

                        </StackLayout>
                    </ViewCell> 
                   <ViewCell>
                       <Grid>
                            <Slider Maximum="10" Value="{Binding Points , Mode=TwoWay}">
                                <Slider.BindingContext>
                                    <vm:DetailsPageViewModel/>
                                </Slider.BindingContext>
                            </Slider>
                        </Grid>
                   </ViewCell>
               </TableSection>
           </TableRoot>
       </TableView>
    </ContentPage.Content>
</ContentPage>

ModelView:

using System;
using System.ComponentModel;

    class DetailsPageViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private double points;

        public DetailsPageViewModel()
        {
            points = 4.0;
        }

        public DetailsPageViewModel(double Points) :  this()
        {
            points = Points;
        }

        public double Points
        {
            get { return points; }
            set
            {
                Console.WriteLine(value);
                points = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("PointsString"));
                }

            }
        }

        public string PointsString
        {
            // Lazy load from points value
            get { return points.ToString(); }
        }
    }
}

Upvotes: 0

Views: 154

Answers (2)

geminiCoder
geminiCoder

Reputation: 2906

Thanks to the Answer by Bijington above -- I was instantiating two instances of the VM. The answer was to set the Binding context of the page and remove the specific BindingContext's

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:vm="clr-namespace:MyFirstApp.ViewModels"
             x:Class="MyFirstApp.Views.DetailPage"
             >
    <ContentPage.BindingContext>
        <vm:DetailsPageViewModel/>
    </ContentPage.BindingContext>
    <ContentPage.Content>
       <TableView Intent="Form">
           <TableRoot>
               <TableSection Title="Name">
                   <EntryCell Label="Name:" />
                   </TableSection>
                   <TableSection Title="Rating">
                    <ViewCell>
                        <StackLayout Orientation="Horizontal" >
                            <Label Text="Points:"  />
                            <Label Text="{Binding PointsString}" />
                        </StackLayout>
                    </ViewCell> 
                   <ViewCell>
                       <Grid>
                            <Slider Maximum="10" Value="{Binding Points , Mode=TwoWay}" />
                        </Grid>
                   </ViewCell>
               </TableSection>
           </TableRoot>
       </TableView>
    </ContentPage.Content>
</ContentPage>

Upvotes: 0

Bijington
Bijington

Reputation: 3751

Looking at your XAML I suspect that you are creating new view models and not binding to the single view model.

To give some context each time you declare this <vm:DetailsPageViewModel /> you are in fact creating a new instance of your view model.

You can try using a Relative binding.

Essentially you need to change this:

<Label Text="{Binding PointsString}">
    <Label.BindingContext>
        <vm:DetailsPageViewModel />
    </Label.BindingContext>
</Label>

to something like this:

<Label Text="{Binding Source={RelativeSource AncestorType={x:Type vm:DetailsPageViewModel}}, Path=PointsString}" />

To prove my point you could place a breakpoint in the constructor to your DetailsPageViewModel and I expect it to hit multiple times.

Upvotes: 1

Related Questions