Search code examples
c#wpfmvvmbindingstatusbar

Rebuild statusbar on the fly with databinding


Im building a plugin system where each plugin has its own statusbar, containing different regions. So Plugin #1 could have a statusbar with 2 x 200 pixels regions, while Plugin #2 can have just a single 400 pixel region.

Im building this in WPF/MVVM and in my viewmodel for the application, the StatusBar for the plugin gets loaded when i switch between plugins.

I have a observablecollection of statusbars that i load to:

ObservableCollection<StatusBar> StatusBars 
{ 
    get;
    set;
}

And then i have a function just retrieves the collection of statusbars for that plugin.

The class that gets loaded looks something like this:

public class StatusBar : ObservableObject
{
    public string Name { get; set; }
    public int Width { get; set; }
    public StatusBarAreaType Type { get; set; }

    string _message;

    public string Message
    {
        get
        {
            return _message;
        }

        set
        {
            if (_message != value)
            {
                _message = value;
                OnPropertyChanged();
            }
        }
    }
}

My problem is that i cant get the XAML to support me loading the collection and showing each StatusBar as its own region. A simplified version of how i tried going about this looks something like this:

<StatusBar VerticalAlignment="Bottom" Height="25" ItemsSource="{Binding StatusBars}" ItemTemplate="{DynamicResource StatusBarTemplate}">
    <StatusBar.Resources>
        <DataTemplate x:Key="StatusBarTemplate">
            <TextBlock Text="{Binding Message}"></TextBlock>
        </DataTemplate>
    </StatusBar.Resources>
</StatusBar>

(I have tried StatusBarItem stuff, ItemsControl templates, using a grid inside instead etc etc)

How do i achieve my goal of having a StatusBar consisting of different regions based on the StatusBar class?


Solution

  • I was actually able to resolve this now, here is my XAML:

    <UserControl x:Class="MyCompany.View.StatusBarControl"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                 xmlns:viewModel="clr-namespace:MyCompany.ViewModel"
                 mc:Ignorable="d"
                 d:DesignHeight="300" d:DesignWidth="300"
                 DataContext="{Binding StatusBar,Source={StaticResource Locator}}">
        <UserControl.Resources>
            <DataTemplate x:Key="SimpleTextDataTemplate">
                <StatusBarItem Width="{Binding Width}" Margin="0,5,0,0">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Message}"></TextBlock>
                    </StackPanel>
                </StatusBarItem>
            </DataTemplate>
    
            <DataTemplate x:Key="ErrorDataTemplate">
                <StatusBarItem Width="{Binding Width}" Margin="0,5,0,0">
                    <TextBlock Text="{Binding Message}"></TextBlock>
                </StatusBarItem>
            </DataTemplate>
    
            <DataTemplate x:Key="ProgressBarDataTemplate">
                <StatusBarItem Width="{Binding Width}" Margin="0,10,0,0">
                    <ProgressBar Width="{Binding Width}" Height="{Binding ElementName=theStatusBar,Path=Height}" Value="{Binding Message}"></ProgressBar>
                </StatusBarItem>
            </DataTemplate>
        </UserControl.Resources>
        <Grid>
            <StatusBar x:Name="theStatusBar" VerticalAlignment="Bottom" Background="#d0d2d6" Height="30"
                       ItemsSource="{Binding StatusBars}">
                <StatusBar.ItemTemplateSelector>
                    <viewModel:StatusBarRegionSelector SimpleTextTemplate="{StaticResource SimpleTextDataTemplate}"
                                                       ErrorTemplate="{StaticResource ErrorDataTemplate}"
                                                       ProgressBarTemplate="{StaticResource ProgressBarDataTemplate}" />
                </StatusBar.ItemTemplateSelector>
            </StatusBar>
        </Grid>
    </UserControl>
    

    And the code behind:

    public class StatusBarRegionSelector : DataTemplateSelector
        {
            public DataTemplate SimpleTextTemplate { get; set; }
            public DataTemplate ErrorTemplate { get; set; }
            public DataTemplate ProgressBarTemplate { get; set; }
    
            public override DataTemplate SelectTemplate(object item, DependencyObject container)
            {
                var statusBar = (StatusBar) item;
    
                if (statusBar != null)
                    switch (statusBar.Type)
                    {
                        case StatusBarAreaType.SimpleText:
                            return SimpleTextTemplate;
                            break;
                        case StatusBarAreaType.Error:
                            return ErrorTemplate;
                            break;
                        case StatusBarAreaType.ProgressBar:
                            return ProgressBarTemplate;
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
    
                return SimpleTextTemplate;
            }
        }
    

    Only issue left now is to add a seperator between each item, just adding a seperator right now does not work for some reason, it wont show up no matter where i place it.