首页 > 解决方案 > 即使更改了 INotifyProperty,WPF XAML 控件绑定也不会更新

问题描述

我在 DayView.xaml 中有一个 ContentControl,其内容绑定到 DayViewModel.cs 中的 CurrentSongViewModel 属性。CurrentSongViewModel 在 ContentControl 中是 DataTemplated 以根据视图模型(DefaultSongViewModel 或 EditSongViewModel)显示其各自的视图,并且两个 DataTemplates 都被确认可以工作。当我单击 DefaultSongView.xaml 上的“编辑”按钮时,EditSongCommand 将执行并将 CurrentSongViewModel 设置为新的 EditSongViewModel。CurrentSongViewModel 设置器调用 OnPropertyChanged(),但 ContentControl 内容没有更新!我在 OnPropertyChanged() 调用上设置了断点,它正在调用它。不知道为什么不更新。。。

DayView.xaml

<UserControl x:Class="Calandar.Views.DayView"
         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:local="clr-namespace:Calandar.Views"
         xmlns:viewmodels="clr-namespace:Calandar.ViewModels"
         d:DataContext="{d:DesignInstance Type=viewmodels:DayViewModel}"
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800">

<Grid Background="LightSteelBlue">
    <Grid.RowDefinitions>
        <RowDefinition Height="50"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <TextBlock Text="Day" Grid.Row="0" FontSize="35" FontFamily="Yu Gothic UI Semibold" HorizontalAlignment="Center" VerticalAlignment="Center"/>

    <Grid Grid.Row="1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="400"/>
            <ColumnDefinition Width="400"/>
        </Grid.ColumnDefinitions>
        
        <StackPanel Grid.Column="0" >
            <Border Style="{StaticResource PurpleBorder}">

                <!-- The binding that isnt working -->
                <ContentControl Content="{Binding CurrentSongViewModel}">
                    <ContentControl.Resources>
                        <DataTemplate DataType="{x:Type viewmodels:DefaultSongViewModel}">
                            <local:DefaultSongView/>
                        </DataTemplate>
                        <DataTemplate DataType="{x:Type viewmodels:EditSongViewModel}">
                            <local:EditSongView/>
                        </DataTemplate>
                    </ContentControl.Resources>
                </ContentControl>
            </Border>
        </StackPanel>
        
    </Grid>
    
</Grid>
</UserControl>

DefaultSongView.xaml

<UserControl x:Class="Calandar.Views.DefaultSongView"
         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:local="clr-namespace:Calandar.Views"
         xmlns:viewmodels="clr-namespace:Calandar.ViewModels"
         d:DataContext="{d:DesignInstance Type=viewmodels:DayViewModel}"
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800">

<Grid>
    <Grid.DataContext>
        <viewmodels:DayViewModel/>
    </Grid.DataContext>
    <StackPanel>
        <DockPanel >
            <Button  Content="Edit" Command="{Binding EditSongCommand}"
                     Style="{StaticResource CollectionModifierButton}" DockPanel.Dock="Right"/>
            <TextBlock Text="Songs" Style="{StaticResource BoxTitleText}" DockPanel.Dock="Top"/>
        </DockPanel>
        <ListBox ItemsSource="{Binding SongList}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Path=.}" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>
</Grid>
</UserControl>

DayViewModel.cs

public class DayViewModel : ViewModelBase
{
    // content view models

    private ViewModelBase _currentSongViewModel;

    public ViewModelBase CurrentSongViewModel
    {
        get { return _currentSongViewModel; }
        set 
        { 
            _currentSongViewModel = value; 
            OnPropertyChanged(nameof(CurrentSongViewModel)); 
        }
    }

    // Song list
    public ObservableCollection<string> SongList { get; set; }

    // Commands
    public ICommand EditSongCommand => new EditSongsCommand(this);

    // Constructor
    public DayViewModel()
    {
        _currentSongViewModel = new DefaultSongViewModel();

        SongList = new ObservableCollection<string>();

        foreach (string line in File.ReadLines(@"C:\Users\person\source\repos\Calandar\DataFiles\Songs.txt"))
        {
            SongList.Add(line);
        }

    }
}

EditSongCommand.cs

public class EditSongsCommand : CommandBase
{
    DayViewModel _vm;

    public override bool CanExecute(object parameter) => true;

    public override void Execute(object parameter)
    {
        _vm.CurrentSongViewModel = new EditSongViewModel();
    }

    public EditSongsCommand(DayViewModel vm)
    {
        _vm = vm;
    }
}

ViewModelBase.cs

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string PropertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
    }
}

标签: c#wpfxaml

解决方案


以下标记将 设置DataContext为 的另一个实例DayViewModel

<Grid.DataContext>
    <viewmodels:DayViewModel/>
</Grid.DataContext>

您应该使用以下命令将其从DefaultSongView.xaml父视图模型的命令中删除并绑定到RelativeSource

<Button 
    Content="Edit"
    Command="{Binding DataContext.EditSongCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}"

推荐阅读