Search code examples
wpfexpander

How I can print listbox item count along with Expander header?


I want to print listbox item count alog with header of expander like header1 (count) . How can I achieve this in WPF


Solution

  • You haven't provided any code for what you have done, such as if you are using a ViewModel or doing code-behind. There are multiple ways to go about it.

    My ViewModel Way

    In this example, I've made a ViewModel containing an ObservableCollection of string to populate the ListBox. The Expander header is bound to a property that is populated using a combination of the HeaderText and ItemCount.

       public class ViewModel : INotifyPropertyChanged
       {
    
          public event PropertyChangedEventHandler PropertyChanged;
    
          private string _headerText = string.Empty;
          private string _headerTextFull = string.Empty;
          private ObservableCollection<string> _listItems = new ObservableCollection<string>();
          private int _itemCount = 0;
    
          public ViewModel() { }
    
          public string HeaderText
          {
             get { return _headerText; }
             set
             {
                _headerText = value;
                NotifyPropertyChanged("HeaderText");
                UpdateHeader();
             }
          }
    
          public string HeaderTextFull
          {
             get { return _headerTextFull; }
             set
             {
                _headerTextFull = value;
                NotifyPropertyChanged("HeaderTextFull");
             }
          }
    
          public ObservableCollection<string> ListItems
          {
             get { return _listItems; }
             set
             {
                _listItems = value;
                NotifyPropertyChanged("ListItems");
                ItemCount = (_listItems != null ? _listItems.Count : 0);
             }
          }
    
          public int ItemCount
          {
             get { return _itemCount; }
             set
             {
                _itemCount = value;
                NotifyPropertyChanged("ItemCount");
                UpdateHeader();
             }
          }
    
          private void UpdateHeader()
          {
             HeaderTextFull = String.Format("{0} ({1})", _headerText, _itemCount);
          }
    
          public void NotifyPropertyChanged(string propertyName)
          {
             if (PropertyChanged != null)
             {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
             }
          }
       }
    

    The XAML for the Window:

    <Window x:Class="SO37192142.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
        <Grid>
          <Grid.RowDefinitions>
             <RowDefinition Height="*" />
             <RowDefinition Height="Auto" />
          </Grid.RowDefinitions>
          <Expander Grid.Row="0" Name="expander1" Header="{Binding Path=HeaderTextFull, FallbackValue='Items'}" IsExpanded="True">
             <Grid>
                <ListBox Name="listBox1" Width="Auto" Height="Auto" ItemsSource="{Binding Path=ListItems}" />
             </Grid>
          </Expander>
          <Button Grid.Row="1" Content="Add an Item" Click="Button_Click" />
       </Grid>
    </Window>
    

    The code-behind:

       public partial class Window1 : Window
       {
          ViewModel myModel = new ViewModel();
          public Window1()
          {
             InitializeComponent();
    
             myModel.ListItems.CollectionChanged += new NotifyCollectionChangedEventHandler(ListItems_CollectionChanged);
    
             myModel.HeaderText = "Items";
             myModel.ListItems.Add("Item 1");
             myModel.ListItems.Add("Item 2");
             this.DataContext = myModel;
          }
    
          void ListItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
          {
             myModel.ItemCount = myModel.ListItems.Count;
          }
    
          void Button_Click(object sender, RoutedEventArgs e)
          {
             myModel.ListItems.Add("Another item");
          }
    
       }
    

    When it starts, the Expander header will say "Items (2)". Every time the button is clicked, the header will update to show the new count.

    Slight Variation of Above

    Here's an example that provides the above example, but also adds a second list to demonstrate a different way. Notice the Expander.Header section.

    <Window x:Class="SO37192142.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
        <Grid>
          <Grid.RowDefinitions>
             <RowDefinition Height="*" />
             <RowDefinition Height="Auto" />
          </Grid.RowDefinitions>
          <Grid.ColumnDefinitions>
             <ColumnDefinition Width="*" />
             <ColumnDefinition Width="*" />
          </Grid.ColumnDefinitions>
    
             <Expander Grid.Row="0" Grid.Column="0" Name="expander1" Header="{Binding Path=HeaderTextFull, FallbackValue='Items'}" IsExpanded="True">
             <Grid>
                <ListBox Name="listBox1" Width="Auto" Height="Auto" ItemsSource="{Binding Path=ListItems}" />
             </Grid>
          </Expander>
    
           <!-- SECOND EXPANDER THAT DOESN'T RELY ON A VIEWMODEL -->
          <Expander Grid.Row="0" Grid.Column="1" Name="expander2" IsExpanded="True">
             <Expander.Header>
                <StackPanel Orientation="Horizontal">
                   <TextBlock Text="{Binding ElementName=listBox2, Path=Items.Count, UpdateSourceTrigger=PropertyChanged, StringFormat={}Items ({0})}" />
                </StackPanel>
             </Expander.Header>
             <Grid>
                <ListBox Name="listBox2" Width="Auto" Height="Auto" ItemsSource="{Binding Path=ListItems}" />
             </Grid>
          </Expander>
          <Button Grid.Row="1" Grid.ColumnSpan="2" Content="Add an Item" Click="Button_Click" />
       </Grid>
    </Window>
    

    Only Code Behind

    If, for some reason, you are just using code-behind, you can do it this way:

       public partial class Window1 : Window
       {
          public Window1()
          {
             InitializeComponent();
    
             ((INotifyCollectionChanged)listBox1.Items).CollectionChanged += new NotifyCollectionChangedEventHandler(ListBox_CollectionChanged);
    
          }
    
          void ListBox_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
          {
             expander1.Header = "Items (" + listBox1.Items.Count + ")";
          }
    
       }