ViewModelBase 中的 PropertyChanged 始终为 null

发布于 2024-11-26 12:28:01 字数 5857 浏览 0 评论 0原文

我做了一个简单的例子来更好地理解 MVVM 模式。 这是示例解决方案的链接,因为它很难解释整个问题: http://www.2shared.com/file/jOOAnacd/MVVMTestMyCopy.html

Employee 模型(带有 Age 属性)和 EmployeeViewModel,其中包含 Employee 对象并更改其以下代码中的 Age 属性:

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

EmployeeViewModel 继承自具有标准 INotifyPropertyCHanged 代码的 ViewModelBase 类:

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

我正在尝试使用 ICommand 更改员工的年龄:

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

属性已更改,但绑定的 TextBLock 不会更改其值。 我检查并看到 NotifyPropertyChanged 被调用,但 PropertyChangednull。 我还确保我的应用程序中只有一个 PeopleViewModel。 那么,为什么 PropertyChangednull 呢?

编辑: 以下是 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

}

PeopleViewModel 包含 ObservalbleCollection 和 EmployeeViewModels 并设置为 数据上下文。 属性值已更改,但如果不重新加载对象,则不会显示更改。

以下是显示绑定的 PeopleViewer.xaml:

<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>

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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

满意归宿 2024-12-03 12:28:01

在您的项目中,您实际上并未在视图模型上实现 INotifyPropertyChanged。您有:

public class ViewModelBase

但这应该是:

public class ViewModelBase : INotifyPropertyChanged

因为您没有实现 INotifyPropertyChange,WPF 绑定系统将无法为您的 PropertyChanged 事件添加处理程序。

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.

桜花祭 2024-12-03 12:28:01

在 ViewModelBase 上实现 INotifyPropertyChanged 接口。
http://msdn.microsoft.com/en-us/library /system.componentmodel.inotifypropertychanged.aspx
您已经定义了 PropertyChanged 事件,但重要的是接口。

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.

メ斷腸人バ 2024-12-03 12:28:01

我使用 MVVM-Light 而不是 BaseViewModel 实现检查了您的应用程序,它的工作原理如下它应该。

我建议使用 MVVM-Light,因为它具有其他功能,例如消息传递、处置和可混合性。

您可以使用 NuGet 轻松下载并安装它。

如果您无论如何都想实现 INotifyPropertyChange,可以使用以下代码:

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

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);
        }
    } 
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文