首页 > 解决方案 > WPF 绑定不更新 XAML 但调用了 PropertyChanged

问题描述

我正在尝试将 XAML 中 WPF UI 元素的可见性绑定到我的视图模型 ( MainViewModel.DisplayPopup) 的属性,该属性是从另一个视图模型 ( ContactViewModel) 的属性更新的,该视图模型 ( ) 是此类 ( MainViewModel) 的属性。该类BaseViewModel扩展INotifyPropertyChanged并使用了一个 Nuget 包,该包在每个属性的设置器中自动调用 PropertyChanged 事件。

这是我的视图模型代码:

public class MainViewModel : BaseViewModel
{
    public ObservableCollection<ContactViewModel> TankItems {get; set; }
    public bool DisplayPopup
    {
        get => TankItems.Any(contact => contact.DisplayPopup);
    }
    public MainViewModel() : base()
    {
        TankItems = new ObservableCollection<ContactViewModel>();
        TankItems.Add(new ContactViewModel());
        TankItems.Add(new ContactViewModel());
    }
}

public class ContactViewModel : BaseViewModel
{
    private bool _isSelected = false;
    public bool DisplayPopup {get; set; } = false;
    public bool IsSelected {get => _isSelected; set { _isSelected = value; DisplayPopup = value;            
}

这是我的 XAML:

<ListBox Grid.Column="0" ItemsSource="{Binding TankItems}" SelectionChanged="List_SelectionChanged">
        <ListBox.Style>
            <Style TargetType="ListBox">
                <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
            </Style>
        </ListBox.Style>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="Test" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
 </ListBox>
 <Border BorderBrush="Orange" BorderThickness="3" Grid.Column="1">
        <TextBlock Text="{Binding DisplayPopup}" /> <!-- Stays as false -->
 </Border>

我期望发生的是当我单击其中一个ListBox项目时,DisplayPopup变为true,但它保持不变。但是,如果我记录我的值,((MainViewModel)DataContext).DisplayPopup)我会得到正确的值 -false在开始时,然后true在更改选择时。

为什么绑定值不更新?

更新

这是BaseViewModel使用Fody PropertyChanged的

[AddINotifyPropertyChangedInterfaceAttribute]
public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };

    public void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null) 
        {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }
}

输出

标签: c#.netwpfmvvmsynchronization

解决方案


绑定坦克项目中的选定状态

您的代码中有多个问题。让我们从ContactViewModel.

public bool IsSelected {get => _isSelected; set { _isSelected = value; DisplayPopup = value;   

DisplayPopup属性是多余的,因为它具有与 相同的状态IsSelected,请将其删除。

<ListBox.Style>
   <Style TargetType="ListBox">
      <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
   </Style>
</ListBox.Style>

此 XAML 是错误的。您必须绑定IsSelecteda 的属性ListBoxItem,而不是ListBox. 另外,您绑定到 上的IsSelected属性MainViewModel,因为这是DataContext这里。该属性不存在,因此绑定将不起作用。而是使用ItemContainerStyle.

<ListBox.ItemContainerStyle>SelectedTankItem 
   <Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
      <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
   </Style>
</ListBox.ItemContainerStyle>

更新显示弹出属性

如果选择了任何项目,您想要设置DisplayPopup为。上面的 item 容器样式会设置your 的属性,但不会自动触发in的属性更改。因此,“Text”绑定永远不会更新它的值。MainViewModeltrueIsSelectedContactViewModelDisplayPopupMainViewModel

要解决此问题,请将DisplayPopup属性MainViewModel设置为简单的 get-set 属性。你不需要计算它。创建第二个属性来SelectedItem绑定ListBoxin MainViewModel。This property will get set, when the selection changes.

public bool DisplayPopup { get; set; } 
public ContactViewModel SelectedTankItem { get; set; }

此外,创建一个名为OnSelectedTankItemChanged您设置DisplayPopup属性的方法,具体取决于SelectedTankItem. 当发生变化时,Fody 框架会自动调用此方法SelectedTankItem

public void OnSelectedTankItemChanged()
{
   DisplayPopup = SelectedTankItem != null;
}

然后将SelectedItem你的绑定ListBoxSelectedTankItem.

<ListBox Grid.Column="0" ItemsSource="{Binding TankItems}" SelectedItem="{Binding SelectedTankItem}">
   <!-- ...other code. -->
</ListBox>

您可以通过删除属性更改代码来简化基本视图模型。您不需要它,因为该属性将导致 Fody 为您实现它。

[AddINotifyPropertyChangedInterfaceAttribute]
public class BaseViewModel : INotifyPropertyChanged
{
}

推荐阅读