开发者

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 ObservalbleCollection 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);
        }
    } 
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜