[英]Why is this DataTemplate inside a ListView not updating its binding automatically?
我具有以下类层次结构:
namespace WpfBindingProblem
{
public class Top
{
public IList<Mid> MidList { get; } = new ObservableCollection<Mid>();
}
public class Mid
{
public IList<Bot> BotList { get; } = new ObservableCollection<Bot>();
}
public class Bot
{
}
}
我有这个XAML窗口:
<Window x:Class="WpfBindingProblem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfBindingProblem"
mc:Ignorable="d"
Title="MainWindow" Height="217.267" Width="333.686">
<Window.DataContext>
<local:Top/>
</Window.DataContext>
<Window.Resources>
<local:TriggersToString x:Key="TriggersToString"/>
</Window.Resources>
<Grid>
<ListView Margin="10" ItemsSource="{Binding MidList}" x:Name="ThatList">
<ListView.Resources>
<DataTemplate DataType="{x:Type local:Mid}">
<TextBlock Text="{Binding BotList, Converter={StaticResource TriggersToString}}" />
</DataTemplate>
</ListView.Resources>
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Add mid" Click="AddMid"/>
<MenuItem Header="Add bot to selected mid" Click="AddBot" />
</ContextMenu>
</ListView.ContextMenu>
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
使用这些处理程序:
namespace WpfBindingProblem
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void AddMid(object sender, RoutedEventArgs e)
{
if(DataContext is Top p)
{
p.MidList.Add(new Mid());
}
}
private void AddBot(object sender, RoutedEventArgs e)
{
if(ThatList.SelectedItem is Mid c)
{
c.BotList.Add(new Bot());
}
}
}
}
这个转换器(作为任何任意转换器的替代品):
namespace WpfBindingProblem
{
[ValueConversion(typeof(IList<Bot>), typeof(string))]
public class TriggersToString : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(value is IList<Bot> list)
{
return list.Count.ToString();
}
throw new InvalidOperationException();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
}
在我们运行此示例时出现的窗口中,我可以右键单击并选择“添加中间”,以便将Mid
的实例添加到Top
数据上下文中,并且列表视图将相应地更新,显示数字0(按照转换逻辑)
但是,当我单击“将机器人添加到选定的中段”时, Bot
实例添加到选定的Mid
(我可以使用断点进行验证),但是列表视图不会相应更新(我希望0更改为1,但对于Mid
特定实例,不会再次调用该转换器。
为什么此更改不会触发GUI的更新?
我知道我可以通过一些技巧来解决此问题(例如,将数据上下文设置为null
并返回,或者可以通过使用依赖项属性来调用显式更新),但是我有两个原因想要避免这种情况:
ObservableCollection
和INotifyPropertyChanged
接口,正是这样,我才不需要执行手动更新-所以我觉得在这种情况下应该进行自动更新,除非我已经错过了一些东西。 您可以使用多重绑定:
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TriggersToString}">
<Binding Path="BotList" />
<Binding Path="BotList.Count" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
和多值转换器:
public class TriggersToString : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) =>
(values[0] as IList<Bot>)?.Count.ToString(); // first binding
...
}
这样,无论何时更新任何绑定,都将调用转换器。
为什么此更改不会触发GUI的更新?
因为绑定的源属性( BotList
)没有更新。 仅在更新数据绑定属性时才调用转换器。
您可以使用@Sinatr建议的MultiBinding
,也可以
直接绑定到集合的Count
属性:
<TextBlock Text="{Binding BotList.Count}" />
在Mid
类中实现INotifyPropertyChanged
接口,并在每次添加项目时引发BotList
属性的PropertyChanged
事件。 处理CollectionChanged
。
您还可以将转换逻辑移动到视图模型,绑定到该模型的一个属性,并在每次刷新绑定时都为其引发PropertyChanged
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.