Search code examples
c#wpfdata-visualizationdatatemplatetimeline

Adding a label to an ItemsControl


Visual presentation is not my strong side when it comes to programming. I'm developing a small program for my company, ment to aid in a schedule overview (can also be described as a timeline).

The background works work as expected, so I don't include this code here as it's not a question of why it's not displaying this data or so. My questions are more on how to present it visually better.

The timeline shows up as a gray bar and is then populated with buttons on the timeline where an event is taking place.

I would like your advice on how I can:

  1. Add a label to the left of the gray bar.
  2. Add a visual timeline (i.e. timestamps) as a gradient above the gray bar, so it visually presents where in the timeline is 3 o'clock and where is 6 o'clock.

or, i would appriciate some advice on where I can read more about it, a hint in the right direction.

Thank you!

<Window x:Class="Test.BookingOverview"
        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:Test"
        mc:Ignorable="d"
        Title="Bokningsöversikt" Height="450" Width="800">
    <Window.Resources>
        <local:EventLengthConverter x:Key="mEventLengthConverter"/>
    </Window.Resources>
    <Grid>
<ItemsControl ItemsSource="{Binding Path=TimeLines}" Margin="10,90,10,27" >
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>

                            <ItemsControl x:Name="TimeLine" ItemsSource="{Binding Path=Events}">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <Grid x:Name="EventContainer" Height="20" Margin="5" Background="Gainsboro">

                                        </Grid>
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>

                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <Button Grid.Column="1" Background="Yellow" VerticalAlignment="Stretch" HorizontalAlignment="Left">
                                            <Button.Margin>
                                                <MultiBinding Converter="{StaticResource mEventLengthConverter}">
                                                    <Binding ElementName="TimeLine" Path="DataContext.Duration"/>
                                                    <Binding Path="Start"/>
                                                    <Binding ElementName="EventContainer" Path="ActualWidth"/>
                                                </MultiBinding>
                                            </Button.Margin>
                                            <Button.Width>
                                                <MultiBinding Converter="{StaticResource mEventLengthConverter}">
                                                    <Binding ElementName="TimeLine" Path="DataContext.Duration"/>
                                                    <Binding Path="Duration"/>
                                                    <Binding ElementName="EventContainer" Path="ActualWidth"/>
                                                </MultiBinding>
                                            </Button.Width>
                                            <Button.Content>
                                                <TextBlock Text="{Binding customer}"></TextBlock>
                                            </Button.Content>
                                            <Button.ContextMenu>
                                                <ContextMenu Name="Test">
                                                    <MenuItem Header="Testar contextmenu"></MenuItem>
                                                    <Separator></Separator>
                                                    <MenuItem Header="Testar igen"></MenuItem>
                                                </ContextMenu>
                                            </Button.ContextMenu>
                                            <Button.ToolTip>
                                                <ToolTip Content="Testar"></ToolTip>
                                            </Button.ToolTip>

                                        </Button>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
    </Grid>

Aepot got me in the right direction, but currently it's creating rows of labeling and then the acctual timelines. Most likely due to something I din't understand by using ItemsControls.

Changes made to the code according to the suggestion of Aepot:

<StackPanel Orientation="Vertical" Margin="90,123,0,0">
            <TextBlock Text="Bokningsöversikt"></TextBlock>
            
            <ItemsControl ItemsSource="{Binding Path=TimeLines}" >
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <ItemsControl x:Name="TimeLine" ItemsSource="{Binding Path=Events}">
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <Grid x:Name="EventContainer" Grid.Column="2" Height="20" Margin="5" Background="Gainsboro"/>
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>

                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <TextBlock FontSize="10" Margin="0,0,0,0" Padding="10,0,0,0" Grid.Column="1" Background="Transparent" VerticalAlignment="Center" HorizontalAlignment="Left">
                                        <TextBlock.Text>
                                            <Binding ElementName="EventContainer" Path="DataContext.RegNr"/>
                                        </TextBlock.Text>
                                    </TextBlock>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>

            <ItemsControl ItemsSource="{Binding Path=TimeLines}" >
                <ItemsControl.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="{Binding RegNr}"/>
                        <Separator></Separator>
                        <MenuItem Header="Test"></MenuItem>
                    </ContextMenu>
                </ItemsControl.ContextMenu>

                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <ItemsControl x:Name="TimeLine" ItemsSource="{Binding Path=Events}">
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <Grid x:Name="EventContainer" Grid.Column="2" Height="20" Margin="5" Background="Gainsboro"/>
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>

                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <Button Grid.Column="2" Background="Yellow" VerticalAlignment="Stretch" HorizontalAlignment="Left">
                                        <Button.Margin>
                                            <MultiBinding Converter="{StaticResource mEventLengthConverter}">
                                                <Binding ElementName="TimeLine" Path="DataContext.Duration"/>
                                                <Binding Path="Start"/>
                                                <Binding ElementName="EventContainer" Path="ActualWidth"/>
                                            </MultiBinding>
                                        </Button.Margin>
                                        <Button.Width>
                                            <MultiBinding Converter="{StaticResource mEventLengthConverter}">
                                                <Binding ElementName="TimeLine" Path="DataContext.Duration"/>
                                                <Binding Path="Duration"/>
                                                <Binding ElementName="EventContainer" Path="ActualWidth"/>
                                            </MultiBinding>
                                        </Button.Width>
                                        <Button.Content>
                                            <TextBlock Text="{Binding customer}"></TextBlock>
                                        </Button.Content>
                                        <Button.ContextMenu>
                                            <ContextMenu Name="Test">
                                                <MenuItem Header="Testar contextmenu"></MenuItem>
                                                <Separator></Separator>
                                                <MenuItem Header="Testar igen"></MenuItem>
                                            </ContextMenu>
                                        </Button.ContextMenu>
                                        <Button.ToolTip>
                                            <ToolTip Content="Testar"></ToolTip>
                                        </Button.ToolTip>
                                    </Button>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>

Solution

  • Note: use TextBlock instead of Label in case of displaying text because it's better optimized for that.

    Try wrapping the controls with StackPanel.

    <DockPanel>
        <TextBlock Text="My timeline"/>
        <StackPanel Orientation="Vertical">
            <ItemsControl .../><!-- with TextBlocks for labeling timeline -->
            <ItemsControl .../><!-- existing one -->
        </StackPanel>
    </DockPanel>
    

    You can also use StackPanel as Control to use in ItemsPanelTemplate.

    Or this way, more friendly if you're familiar with HTML <table>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBlock Grid.RowSpan="2" Text="My timeline"/>
        <ItemsControl Grid.Column="1" .../><!-- with TextBlocks for labeling timeline -->
        <ItemsControl Grid.Column="1" Grid.Row="1" .../><!-- existing one -->
    </Grid>