Computer Backup
Computer Backup

Reputation: 187

Changes to ItemsControl's ItemsSource not updated in UI when tab is changed in TabControl

I am trying to create a wpf application with several tabs in a TabControl, where one tab is a log. The button on the first tab adds a line to the log in the second tab. When the button is pressed, it also displays a message box containing the length of the ItemsSource of the log. Issue:

Full Code:

MainWindow.xaml

<Window x:Class="WpfApp2.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:WpfApp2"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <TabControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,10,10">
            <TabItem Header="Microcode Process" Name="MicrocodeProcessTab">
                <Button Content="Single Step" Margin="0,0,10,0" Click="SingleStep" Name="SingleStepButton"/>
            </TabItem>
            <TabItem Header="Log">
                <ScrollViewer>
                    <ItemsControl Name="LogList">
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding}"/>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </ScrollViewer>
            </TabItem>
        </TabControl>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Windows;

namespace WpfApp2 { 
    public partial class MainWindow : Window {
        List<string> list = new List<string>();

        public MainWindow() {
            InitializeComponent();
            LogList.ItemsSource = list;
        }

        private void SingleStep(object sender, RoutedEventArgs e) {
            list.Add($"STEP");
            MessageBox.Show((LogList.ItemsSource as List<string>).Count.ToString());
            e.Handled = true;
        }
    }
}

Upvotes: 1

Views: 622

Answers (1)

Bizhan
Bizhan

Reputation: 17125

The WPF technology does not magically update target whenever a change is made to the source. It keeps UI updated through some notification mechanisms such as DependencyObject, INotifyPropertyChanged or INotifyCollectionChanged.

The data type used for the collection (List<>) does not have a notification mechanism. So the UI gets updated only once upon its creation and never again, that's why you see the log item added only the first time you go to the second tab.

(not recommended) You can do this to manually reset the ItemsSource every time a change is made to it. It will however always re-create all UI elements in the visual tree of the ItemsControl.

private void SingleStep(object sender, RoutedEventArgs e)
{
    list.Add("STEP");
    LogList.ItemsSource = null;
    LogList.ItemsSource = list;
}

(recommended) You can implement binding for your ItemsSource by using an ObservableCollection - which implements INotifyCollectionChanged - as follows:

ObservableCollection<string> _list = new ObservableCollection<string>();
public ObservableCollection<string> list { get { return _list; } }

Note that an ObservableCollection automatically notifies the target of any changes made within the collection.

Upvotes: 1

Related Questions