ItemsControl 中的元素不会更新,但在 ListBox 中会更新
Elements inside ItemsControl don't update, but inside ListBox they do
(正在编辑整个 post,因为你们认为正在发生一些代码魔术)
简介
我有一个 Observable 集合,它应该在列表中可视化
为此,我想使用 ItemsControl,但列表元素不会更新(它们为空或显示我定义的后备值)
当使用 ListBox 时,它按预期工作,但是我得到了我不想要的列表项选择。
TL;DR;问题
ItemsControl 没有显示正确的值,但 ListBox 可以。
重现代码
为了重现问题,我新建了一个 WPF 项目,尽可能简单地展示问题,这是 我的最小示例的完整代码
我有一个自定义用户控件,主要显示一些文本,在本例中是一个地址,(当前)仅在构造函数中设置:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Controls;
namespace WpfApp1
{
public partial class MyUserControl : UserControl, INotifyPropertyChanged
{
private readonly string _address;
public MyUserControl()
{
InitializeComponent();
}
public MyUserControl(string address)
{
InitializeComponent();
_address = address;
OnPropertyChanged(nameof(Address));
}
public string Address => _address;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
这是用户控件的 XAML 代码:
<UserControl x:Class="WpfApp1.MyUserControl"
<!-- snip namespaces -->
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="20" d:DesignWidth="300">
<Border BorderBrush="Black" BorderThickness="0.6">
<Border.Style> <!-- snip --> </Border.Style>
<StackPanel Orientation="Horizontal">
<Label Grid.Column="0" Grid.Row="0" Padding="2" Content="Address:"/>
<Label Grid.Column="1" Grid.Row="0" Padding="2" Grid.ColumnSpan="2" Content="{Binding Path=Address, FallbackValue=00000000000000000000000000000}"/>
</StackPanel>
</Border>
</UserControl>
主要 window 的 XAML 现在使用相同的绑定呈现两个列表,一个是 ItemsControl,一个是 ListBox
<Window x:Class="WpfApp1.MainWindow"
<!-- snip namespaces -->
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel x:Name="FeeKeyListView" VerticalAlignment="Top" HorizontalAlignment="Stretch" Margin="5">
<ItemsControl ItemsSource="{Binding MyList, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinHeight="200">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:MyUserControl/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ListBox ItemsSource="{Binding MyList, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinHeight="200">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<local:MyUserControl/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
最后但同样重要的是 MainWindow class,其中包含 Observable 列表:
using System.Collections.ObjectModel;
using System.Windows;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public ObservableCollection<MyUserControl> MyList { get; set; } = new ObservableCollection<MyUserControl>();
public MainWindow()
{
InitializeComponent();
MyList.Clear();
MyList.Add(new MyUserControl("my address1"));
}
}
}
结果 window 显示,值在 ListBox 中正确绑定,但在 ItemsControl 中未正确绑定
期望的行为 是 ItemsControl 显示 'my address1' 就像 ListBox 那样。
IsItemItsOwnContainerOverride
method, in ItemsControl
的不同实现导致不同的行为
protected virtual bool IsItemItsOwnContainerOverride(object item)
{
return (item is UIElement);
}
并在 ListBox
protected override bool IsItemItsOwnContainerOverride(object item)
{
return (item is ListBoxItem);
}
当来自 Items
或 ItemsSource
集合的项目的方法 returns 为真时 - 它在 ItemsControl 中为您的用户控件执行 - 不会生成项目容器,因此不会生成 ItemTemplate已应用。
但是,您通常不会将 UIElement 集合分配给 ItemsSource 属性,而是分配视图模型项的集合 - ItemTemplate 中的元素将其属性绑定到该集合。
(正在编辑整个 post,因为你们认为正在发生一些代码魔术)
简介
我有一个 Observable 集合,它应该在列表中可视化
为此,我想使用 ItemsControl,但列表元素不会更新(它们为空或显示我定义的后备值)
当使用 ListBox 时,它按预期工作,但是我得到了我不想要的列表项选择。
TL;DR;问题
ItemsControl 没有显示正确的值,但 ListBox 可以。
重现代码
为了重现问题,我新建了一个 WPF 项目,尽可能简单地展示问题,这是 我的最小示例的完整代码
我有一个自定义用户控件,主要显示一些文本,在本例中是一个地址,(当前)仅在构造函数中设置:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Controls;
namespace WpfApp1
{
public partial class MyUserControl : UserControl, INotifyPropertyChanged
{
private readonly string _address;
public MyUserControl()
{
InitializeComponent();
}
public MyUserControl(string address)
{
InitializeComponent();
_address = address;
OnPropertyChanged(nameof(Address));
}
public string Address => _address;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
这是用户控件的 XAML 代码:
<UserControl x:Class="WpfApp1.MyUserControl"
<!-- snip namespaces -->
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="20" d:DesignWidth="300">
<Border BorderBrush="Black" BorderThickness="0.6">
<Border.Style> <!-- snip --> </Border.Style>
<StackPanel Orientation="Horizontal">
<Label Grid.Column="0" Grid.Row="0" Padding="2" Content="Address:"/>
<Label Grid.Column="1" Grid.Row="0" Padding="2" Grid.ColumnSpan="2" Content="{Binding Path=Address, FallbackValue=00000000000000000000000000000}"/>
</StackPanel>
</Border>
</UserControl>
主要 window 的 XAML 现在使用相同的绑定呈现两个列表,一个是 ItemsControl,一个是 ListBox
<Window x:Class="WpfApp1.MainWindow"
<!-- snip namespaces -->
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel x:Name="FeeKeyListView" VerticalAlignment="Top" HorizontalAlignment="Stretch" Margin="5">
<ItemsControl ItemsSource="{Binding MyList, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinHeight="200">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:MyUserControl/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ListBox ItemsSource="{Binding MyList, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinHeight="200">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<local:MyUserControl/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
最后但同样重要的是 MainWindow class,其中包含 Observable 列表:
using System.Collections.ObjectModel;
using System.Windows;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public ObservableCollection<MyUserControl> MyList { get; set; } = new ObservableCollection<MyUserControl>();
public MainWindow()
{
InitializeComponent();
MyList.Clear();
MyList.Add(new MyUserControl("my address1"));
}
}
}
结果 window 显示,值在 ListBox 中正确绑定,但在 ItemsControl 中未正确绑定
期望的行为 是 ItemsControl 显示 'my address1' 就像 ListBox 那样。
IsItemItsOwnContainerOverride
method, in ItemsControl
protected virtual bool IsItemItsOwnContainerOverride(object item)
{
return (item is UIElement);
}
并在 ListBox
protected override bool IsItemItsOwnContainerOverride(object item)
{
return (item is ListBoxItem);
}
当来自 Items
或 ItemsSource
集合的项目的方法 returns 为真时 - 它在 ItemsControl 中为您的用户控件执行 - 不会生成项目容器,因此不会生成 ItemTemplate已应用。
但是,您通常不会将 UIElement 集合分配给 ItemsSource 属性,而是分配视图模型项的集合 - ItemTemplate 中的元素将其属性绑定到该集合。