[英]WPF Binding not updating XAML but PropertyChanged called
I am trying to bind the visibility of a WPF UI element in my XAML to a property of my view model ( MainViewModel.DisplayPopup
), which is updated from the property of another view model ( ContactViewModel
) which is a property of this class ( MainViewModel
). I am trying to bind the visibility of a WPF UI element in my XAML to a property of my view model (
MainViewModel.DisplayPopup
), which is updated from the property of another view model ( ContactViewModel
) which is a property of this class ( MainViewModel
)。 The BaseViewModel
class extends INotifyPropertyChanged
and uses a Nuget package that automatically calls the PropertyChanged event inside of each property's setter. BaseViewModel
class 扩展了INotifyPropertyChanged
并使用 Nuget package自动调用每个属性设置器内部的 PropertyChanged 事件。
This is my view model code:这是我的看法 model 代码:
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;
}
This is my XAML:这是我的 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>
What I expect to happen is when I click one of the ListBox
items, DisplayPopup
becomes true
, but it stays the same.我期望发生的是当我单击
ListBox
项目之一时, DisplayPopup
变为true
,但它保持不变。 HOWEVER, if I log the value of ((MainViewModel)DataContext).DisplayPopup)
I get the correct value - false
in the beginning then true
when the selection is changed.但是,如果我记录
((MainViewModel)DataContext).DisplayPopup)
的值,我会得到正确的值 - 开始时为false
,然后在更改选择时为true
。
Why is the binding value not updating?为什么绑定值不更新?
Update更新
Here is the BaseViewModel
, which uses Fody PropertyChanged这是
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));
}
}
}
There are multiple issues in your code.您的代码中有多个问题。 Let's start with the
ContactViewModel
.让我们从
ContactViewModel
开始。
public bool IsSelected {get => _isSelected; set { _isSelected = value; DisplayPopup = value;
The DisplayPopup
property is redundant, as it is has the same state as IsSelected
, remove it. DisplayPopup
属性是多余的,因为它具有与 IsSelected 相同的IsSelected
,请将其删除。
<ListBox.Style>
<Style TargetType="ListBox">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</ListBox.Style>
This XAML is wrong.这个 XAML 是错误的。 You have to bind the
IsSelected
property of a ListBoxItem
, not the ListBox
.您必须绑定
ListBoxItem
的IsSelected
属性,而不是ListBox
。 Plus you bind to an IsSelected
property on MainViewModel
, as this is the DataContext
here.另外,您绑定到
MainViewModel
上的IsSelected
属性,因为这是此处的DataContext
。 This property does not exist, so the binding will not work.该属性不存在,因此绑定将不起作用。 Instead use an
ItemContainerStyle
.而是使用
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>
You want to set DisplayPopup
on MainViewModel
to true
, if any item is selected.如果选择了任何项目,您希望将
MainViewModel
上的DisplayPopup
设置为true
。 The item container style above will set the IsSelected
property of your ContactViewModel
, but it does not automatically trigger a property change of DisplayPopup
in MainViewModel
.上面的项目容器样式将设置您的
ContactViewModel
的IsSelected
属性,但它不会自动触发MainViewModel
中DisplayPopup
的属性更改。 Hence, the "Text" binding will never update its value.因此,“Text”绑定永远不会更新它的值。
To solve this, make the DisplayPopup
property in MainViewModel
a simple get-set property.要解决此问题,请将
MainViewModel
中的DisplayPopup
属性设为简单的 get-set 属性。 You do not need to compute it.你不需要计算它。 Create a second property to bind the
SelectedItem
of the ListBox
in MainViewModel
.创建第二个属性以绑定
MainViewModel
中ListBox
的SelectedItem
。 This property will get set, when the selection changes. This property will get set, when the selection changes.
public bool DisplayPopup { get; set; }
public ContactViewModel SelectedTankItem { get; set; }
Additionally, create a method called OnSelectedTankItemChanged
where you set the DisplayPopup
property depending on SelectedTankItem
.此外,创建一个名为
OnSelectedTankItemChanged
的方法,您可以在其中根据SelectedTankItem
设置DisplayPopup
属性。 This method will automatically be called by the Fody framework, when SelectedTankItem
changes.当
SelectedTankItem
更改时,Fody 框架将自动调用此方法。
public void OnSelectedTankItemChanged()
{
DisplayPopup = SelectedTankItem != null;
}
Then bind the SelectedItem
on your ListBox
to SelectedTankItem
.然后将
ListBox
上的SelectedTankItem
SelectedItem
<ListBox Grid.Column="0" ItemsSource="{Binding TankItems}" SelectedItem="{Binding SelectedTankItem}">
<!-- ...other code. -->
</ListBox>
You can simplify your base view model by removing the property changed code.您可以通过删除属性更改代码来简化基本视图 model。 You do not need it, since the attribute will cause Fody to implement it for you.
您不需要它,因为该属性将导致 Fody 为您实现它。
[AddINotifyPropertyChangedInterfaceAttribute]
public class BaseViewModel : INotifyPropertyChanged
{
}
The reason why the binding to DisplayPopup
does not update is that the property is a computed property.与
DisplayPopup
的绑定不更新的原因是该属性是计算属性。 A computed property lacks a setter and therefore never raises INotifyPropertyChanged.PropertyChanged
.计算属性缺少设置器,因此永远不会引发
INotifyPropertyChanged.PropertyChanged
。 The data binding listens to this event.数据绑定侦听此事件。 As the result
DisplayPopup.Get
is only called once (the moment the binding is initialized).结果
DisplayPopup.Get
只被调用一次(绑定初始化的那一刻)。
To solve this you can either let the MainViewModel
listen to PropertyChanged
events of the ContactViewModel
items or as it seems that you are interested in selected items simply bind the ListBox.SelectedItem
and change MainViewModel.DisplayPopup
on changes.要解决此问题,您可以让
MainViewModel
监听ContactViewModel
项目的PropertyChanged
事件,或者您似乎对选定项目感兴趣,只需绑定ListBox.SelectedItem
并在更改时更改MainViewModel.DisplayPopup
。
For simplicity I recommend the second solution.为简单起见,我推荐第二种解决方案。
Note that in order to make the ListBox.IsSelected
binding work you must set the ListBox.ItemContainerStyle
and target ListBoxItem
instead of ListBox
:请注意,为了使
ListBox.IsSelected
绑定起作用,您必须设置ListBox.ItemContainerStyle
和目标ListBoxItem
而不是ListBox
:
MainViewModel.cs主视图模型.cs
public class MainViewModel : BaseViewModel
{
public ObservableCollection<ContactViewModel> TankItems { get; set; }
private ContactViewModel selectedTankItem;
public ContactViewModel SelectedTankItem
{
get => this.selectedTankItem;
set
{
this.selectedTankItem = value;
OnPropertyChanged(nameof(this.SelectedTankItem));
this.DisplayPopup = this.SelectedTankItem != null;
}
// Raises INotifyPropertyChanged.PropertyChanged
public bool DisplayPopup { get; set; }
public MainViewModel() : base()
{
TankItems = new ObservableCollection<ContactViewModel>()
{
new ContactViewModel(),
new ContactViewModel()
};
}
}
MainWindow.xaml主窗口.xaml
<ListBox ItemsSource="{Binding TankItems}"
SelectedItem="{Binding SelectedTankItem}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected"
Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type ContactViewModel}">
<StackPanel>
<TextBlock Text="Test" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Border>
<TextBlock Text="{Binding DisplayPopup}" />
</Border>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.