wpfmvvmdynamicdatatemplatestatusbaritem

WPF Adding StatusBarItem using mvvm and Datatemplate


I'm trying to add StatusBarItem dynamically by using mvvm pattern but i don't know what to do.

If i write the code that i want to create in XAML file, it looks like this:

<StatusBar Grid.Row="2" DockPanel.Dock="Bottom">
    <StatusBarItem HorizontalAlignment="Left">
        <StatusBarItem Content="There's a sentence in it."/>
    </StatusBarItem>
                
    <StatusBarItem HorizontalAlignment="Right">
        <StackPanel Orientation="Horizontal">
            <StatusBarItem Content="Status 1" Background="Yellow" Margin="0 0 10 0"/>
            <StatusBarItem Content="Status 2" Background="Red" Margin="0 0 10 0"/>
        </StackPanel>
    </StatusBarItem>
</StatusBar>

But I want to dynamically add 'StatusBarItem' on the right side. Because i can see the status of the programs that are communicating with WPF program, and it can be multiple. (On the left, there's only a sentence. And StatusBarItem which shows the status of the program communicating will be added from the right.)

So i write the code like this.

StatusBarVieModel class

public class StatusBarViewModel : ViewModelBase
{
    private string _alignment;
    public string Alignment
    {
        get
        {
            return _alignment;
        }
        set
        {
            _alignment = value;
            OnPropertyChanged(nameof(Alignment));
        }
    }

    private ObservableCollection<BarItemViewModel> _barItems;
    public ObservableCollection<BarItemViewModel> BarItems
    {
        get
        {
            return _barItems;
        }
        set
        {
            _barItems = value;
            OnPropertyChanged(nameof(BarItems));
        }
    }
}

BarItemViewModel class

public class BarItemViewModel : ViewModelBase
{
    private string _itemName;
    public string ItemName
    {
        get
        {
            return _itemName;
        }
        set
        {
            _itemName = value;
            OnPropertyChanged(nameof(ItemName));
        }
    }

    private string _backgroundColor;
    public string BackgroundColor
    {
        get
        {
            return _backgroundColor;
        }
        set
        {
            _backgroundColor = value;
            OnPropertyChanged(nameof(BackgroundColor));
        }
    }

    private string _foregroundColor;
    public string ForegroundColor
    {
        get
        {
            return _foregroundColor;
        }
        set
        {
            _foregroundColor = value;
            OnPropertyChanged(nameof(ForegroundColor));
        }
    }

MainWindowView.xaml

...
<StatusBar Grid.Row="2" DockPanel.Dock="Bottom" ItemsSource="{Binding StatusBars}" >
    <StatusBar.ItemTemplate>
        <DataTemplate DataType="{x:Type vms:BarItemViewModel}">
            <StatusBarItem HorizontalAlignment="{Binding Alignment}">
                <StackPanel Orientation="Horizontal">
                    <StatusBarItem Content="{Binding BarItems.ItemName}"/>
                </StackPanel>
            </StatusBarItem>
        </DataTemplate>
    </StatusBar.ItemTemplate>
</StatusBar>

MainWindowViewModel

public class MainWindowViewModel : ViewModelBase
{
    public MainWindowViewModel() 
    {
        CreateBarItems();
    }

    private ObservableCollection<StatusBarViewModel> _statusBars;
    public ObservableCollection<StatusBarViewModel> StatusBars
    {
        get
        {
            return this._statusBars;
        }

        set
        {
            this._statusBars = value;
            OnPropertyChanged("StatusBars");
        }
    }

    private void CreateBarItems()
    {
        StatusBars = new ObservableCollection<StatusBarViewModel>
        {
            new StatusBarViewModel
            {
                Alignment = "Left",
                BarItems = new ObservableCollection<BarItemViewModel>
                {
                    new BarItemViewModel{ ItemName = "test1", BackgroundColor = "Black", ForegroundColor = "White"},
                }
            },
            new StatusBarViewModel
            {
                Alignment = "Right",
                BarItems = new ObservableCollection<BarItemViewModel>
                {
                    new BarItemViewModel{ ItemName = "test2", BackgroundColor = "#52d976", ForegroundColor = "Black"},
                    new BarItemViewModel{ ItemName = "test3", BackgroundColor = "Red", ForegroundColor = "Black"},
                }
            },
        };
    }
}

But if i run this code, it gives me binding error with BarItems.ItemName... How can I add a StatusBarItem dynamically as I want?


Solution

  • you need to use an ItemsControl to create multiple elements from BarItems collection:

    <StatusBar Grid.Row="2" DockPanel.Dock="Bottom" ItemsSource="{Binding StatusBars}" >
        <StatusBar.ItemTemplate>
            <DataTemplate DataType="{x:Type vms:StatusBarViewModel}">
                <StatusBarItem HorizontalAlignment="{Binding Alignment}">
    
                 <ItemsControl ItemsSource="{Binding BarItems}">
                   <ItemsControl.ItemsPanel>
                       <ItemsPanelTemplate>
                          <StackPanel Orientation="Horizontal"/>
                       </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate DataType="{x:Type vms:BarItemViewModel}">
                             <StatusBarItem Content="{Binding ItemName}"/>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                 </ItemsControl>
    
                </StatusBarItem>
            </DataTemplate>
        </StatusBar.ItemTemplate>
    </StatusBar>
    

    also note that you had a wrong DataType on the first DataTemplate