简体   繁体   中英

PropertyChanged is always null in ViewModelBase

I made a simple example to better understan the MVVM pattern. Here is a link to the sample solution, because its difficult to explain the whole problem: http://www.2shared.com/file/jOOAnacd/MVVMTestMyCopy.html

There is Employee model (with Age property) and EmployeeViewModel , which contains Employee object and changes its Age property in the following code:

public int Age
{
    get { return _employee.Age; }
    set
    {
        if (value == _employee.Age)
            return;
        _employee.Age = value;
        NotifyPropertyChanged("Age");
    }
}

EmployeeViewModel is inherited from ViewModelBase class with standard INotifyPropertyCHanged code:

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

I'm trying to change employee's age using ICommand:

public void Increase()
{
    this.SelectedEmployee.Age++;
    NotifyPropertyChanged("Age");
}

The property is changed, but the binded TextBLock does not change its value. I checked and saw that NotifyPropertyChanged is called, but PropertyChanged is null . I also ensured that I have only one PeopleViewModel in my app. So, why is the PropertyChanged is null ?

EDIT: Here is full code for ViewModelBase :

public class ViewModelBase
{

    public String DisplayName { get; set; }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

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


    #endregion

}

There is PeopleViewModel which contains ObservalbleCollectio n with EmployeeViewModels and set as DataContext . The values of properties are changed, but the changes are not shown without reloading objects.

Here is the PeopleViewer.xaml that shows the binding:

<UserControl x:Class="MVVMTestMyCopy.View.PeopleViewer"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:vm="clr-namespace:MVVMTestMyCopy.ViewModel"
             mc:Ignorable="d" 
             d:DesignHeight="316" d:DesignWidth="410">
    <UserControl.Resources>
        <vm:PeopleViewModel x:Key="viewModel"/>
    </UserControl.Resources>
    <Grid DataContext="{Binding Source={StaticResource viewModel}}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <ListBox  ItemsSource="{Binding People}"
                  Grid.Column="0"
                  Margin="5,5,4,5"
                  SelectedItem="{Binding SelectedEmployee, Mode=TwoWay}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Margin="2"
                                   Text="{Binding FirstName}" />
                        <TextBlock Margin="2"
                                   Text="{Binding LastName}" />
                        <TextBlock Margin="0 2"
                                   Text="[" />
                        <TextBlock Margin="2"
                                   Text="{Binding Age}" />
                        <TextBlock Margin="0 2"
                                   Text="]" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Grid Grid.Column="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="0.75*" />
                <RowDefinition Height="0.25*" />
            </Grid.RowDefinitions>
            <Grid x:Name="EmployeeDetails"
                  Grid.Row="0"
                  DataContext="{Binding SelectedEmployee}"
                  Margin="5">
                <Grid.RowDefinitions>
                    <RowDefinition Height="1*" />
                    <RowDefinition Height="1*" />
                    <RowDefinition Height="1*" />
                </Grid.RowDefinitions>
                <TextBlock Text="{Binding FirstName}"
                           VerticalAlignment="Center"
                           HorizontalAlignment="Center"
                           Grid.Column="0"/>
                <TextBlock Text="{Binding LastName}"
                           VerticalAlignment="Center"
                           HorizontalAlignment="Center"
                           Grid.Row="1" />
                <TextBlock Text="{Binding Age}"
                           VerticalAlignment="Center"
                           HorizontalAlignment="Center"
                           Grid.Row="2" />

            </Grid>
            <StackPanel Orientation="Vertical"
                        HorizontalAlignment="Center"
                        Grid.Row="1">
                <Button x:Name="button"
                        Content="-"
                        Width="32"
                        Height="32"
                        Command="{Binding DecreaseCommand}">
                </Button>
                <Button x:Name="button1"
                        Content="+"
                        Width="32"
                        Height="32"
                        Command="{Binding IncreaseCommand}">
                </Button>
            </StackPanel>
        </Grid>
    </Grid>
</UserControl>

In your project, you don't actually implement INotifyPropertyChanged on your view-model. You have:

public class ViewModelBase

But this should be:

public class ViewModelBase : INotifyPropertyChanged

Because you don't implement INotifyPropertyChange, the WPF binding system will not be able to add a handler for your PropertyChanged event.

Implement the INotifyPropertyChanged interface on your ViewModelBase. http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx You have defined a PropertyChanged event but it is the interface that is important.

I checked you application using MVVM-Light instead of your BaseViewModel implementation and it worked as it should.

I suggest using MVVM-Light because of other features like Messaging, Disposing and Blendability.

You can easily download and install it using NuGet .

If you want to implement INotifyPropertyChange anyway, here is code that will do it:

public class MainViewModel: INotifyPropertyChanged
{        
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            var e = new PropertyChangedEventArgs(propertyName);
            handler(this, e);
        }
    } 
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM