Reputation: 766
(Editing the whole post, since you guys think there is some code magic going on)
I've got an Observable Collection which should be visualized in a list
For that purpose I want to use an ItemsControl, but the list elements do not update (they are empty or show fallback values I defined)
when using a ListBox instead it works as expected, but then I've got the list item selection which I don't want.
ItemsControl does not show the correct values, but a ListBox does.
To reproduce the problem, I made a new WPF project, as simple as possible to show the problem, this is the complete code of my minimal example
I have a custom UserControl mostly showing some text, in this case an address, which (currently) is only set in the constructor:
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));
}
}
}
this is the XAML code for the user control:
<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>
The XAML of the main window, now renders two lists one ItemsControl and one ListBox using the same binding and whatsoever
<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>
last but not least the MainWindow class, where the Observable list is contained:
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"));
}
}
}
The resulting window shows, that the values are correctly bound in the ListBox but not in the ItemsControl
The desired behavior would be, that the ItemsControl shows 'my address1' like the ListBox does.
Upvotes: 0
Views: 127
Reputation: 128070
The different behavior results from the different implementations of the IsItemItsOwnContainerOverride
method, in ItemsControl
protected virtual bool IsItemItsOwnContainerOverride(object item)
{
return (item is UIElement);
}
and in ListBox
protected override bool IsItemItsOwnContainerOverride(object item)
{
return (item is ListBoxItem);
}
When the method returns true for an item from the Items
or ItemsSource
collection - which it does in ItemsControl for your UserControls - no item container is generated and hence no ItemTemplate is applied.
However, you would usually not assign a collection of UIElements to the ItemsSource property, but instead a collection of view model items - to which the elements in the ItemTemplate bind their properties.
Upvotes: 1