#1,043 – Using a DockPanel as the Items Panel for a ListBox

You can replace the default StackPanel used as the items panel for a ListBox with any other panel element.  Below is an example of displaying some news stories in a DockPanel.

Assume that we have a NewsStory class as follow:

    public class NewsStory
    {
        public string Story { get; set; }
        public Brush Color { get; set; }
        public Dock Dock { get; set; }
        public double Rotate { get; set; }

        public NewsStory(string story, Color color, Dock dock, double rotate)
        {
            Story = story;
            Color = new SolidColorBrush(color);
            Dock = dock;
            Rotate = rotate;
        }

        public override string ToString()
        {
            return Story;
        }
    }

We can then create a collection of NewsStory items that we’ll bind to.  Notice that we create a spiral pattern by setting consecutive Dock properties to Bottom/Left/Top/Right.  We also use the Angle property to rotate every other element.

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

            Stories = new ObservableCollection<NewsStory>
            {
                new NewsStory("Diaper Market Bottoms Out", Colors.AliceBlue, Dock.Bottom, 0.0),
                new NewsStory("Antique Stripper to Display Wares", Colors.AntiqueWhite, Dock.Left, -90.0),
                new NewsStory("Cancer Society Honors Marlboro Man", Colors.Aqua, Dock.Top, 0.0),
                new NewsStory("War Dims Hope for Peace", Colors.Aquamarine, Dock.Right, -90.0)
                // more entries go here..
            };
            RaisePropertyChanged("Stories");

        }

        public ObservableCollection<NewsStory> Stories { get; protected set; }

        // INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        private void RaisePropertyChanged(string propName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }

In XAML, we set the ItemContainerStyle to do the docking and specify a DockPanel as the ItemsPanel.

        <ListBox ItemsSource="{Binding Stories}">
            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="DockPanel.Dock" Value="{Binding Dock}"/>
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
                </Style>
            </ListBox.ItemContainerStyle>
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <DockPanel IsItemsHost="True"/>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Label Content="{Binding Story}"
                           Background="{Binding Color}">
                        <Label.LayoutTransform>
                            <RotateTransform Angle="{Binding Rotate}"/>
                        </Label.LayoutTransform>
                    </Label>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

Here’s what the end result looks like:

1044-001

Advertisement

#577 – You Can Change DockPanel.Dock Property at Runtime

The attached property DockPanel.Dock dictates which side of the remaining space in a DockPanel a child control will dock to.  You typically set DockPanel.Dock in XAML at design time, for each child of the DockPanel.

You can also change any DockPanel.Dock value at runtime.  In the example below, clicking on the button selects new random values for Dock.

		<DockPanel>
		    <Label Name="lbl1" Content="1st label" DockPanel.Dock="Top" Background="Bisque"/>
		    <Label Name="lbl2" Content="2nd" DockPanel.Dock="Left" Background="BurlyWood"/>
		    <Label Name="lbl3" Content="3rd" DockPanel.Dock="Top" Background="Cornsilk"/>
		    <Label Name="lbl4" Content="4th" DockPanel.Dock="Right" Background="DarkKhaki"/>
		    <Label Name="lbl5" Content="5th" DockPanel.Dock="Bottom" Background="DarkSeaGreen"/>
		    <Label Name="lbl6" Content="6th" DockPanel.Dock="Left" Background="Gainsboro"/>
		    <Label Content="7th, fills remaining space" Background="Khaki"/>
		</DockPanel>

 

        private Random randGen = new Random();

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            DockPanel.SetDock(lbl1, RandomDock());
            DockPanel.SetDock(lbl2, RandomDock());
            DockPanel.SetDock(lbl3, RandomDock());
            DockPanel.SetDock(lbl4, RandomDock());
            DockPanel.SetDock(lbl5, RandomDock());
            DockPanel.SetDock(lbl6, RandomDock());
        }

        private Dock RandomDock()
        {
            return (Dock)randGen.Next(3);
        }




#447 – You Can Use Layout Transforms Within a DockPanel

You can use a LayoutTransform on individual child elements in a DockPanel container to scale or rotate the elements.

In the example below, we have four Label controls, each docked to one side of a DockPanel.  The labels on the left, top and right sides use a LayoutTransform to make them face outwards.

<DockPanel LastChildFill="False">
    <Label Content="Facing Up" DockPanel.Dock="Top" Background="Aquamarine"
           HorizontalContentAlignment="Center">
        <Label.LayoutTransform>
            <RotateTransform Angle="180"/>
        </Label.LayoutTransform>
    </Label>

    <Label Content="Facing Down" DockPanel.Dock="Bottom" Background="LightSteelBlue"
           HorizontalContentAlignment="Center">
    </Label>

    <Label Content="Facing Left" DockPanel.Dock="Left" Background="DarkSeaGreen"
           HorizontalContentAlignment="Center">
        <Label.LayoutTransform>
            <RotateTransform Angle="90"/>
        </Label.LayoutTransform>
    </Label>

    <Label Content="Facing Right" DockPanel.Dock="Right" Background="SkyBlue"
           HorizontalContentAlignment="Center">
        <Label.LayoutTransform>
            <RotateTransform Angle="-90"/>
        </Label.LayoutTransform>
    </Label>
</DockPanel>

#446 – Default Docking for DockPanel Children

You normally specify a value for the DockPanel.Dock attached property for all children of a DockPanel, indicating which side the element should be docked to.  However, if you don’t specify a value for the Dock property, the child element will be docked to the Left, by default.

    <DockPanel LastChildFill="False">
        <ListBox ItemsSource="{Binding MovieList}"/>
        <Label Content="1 - Label at Top" DockPanel.Dock="Top" Background="Bisque"/>
        <Label Content="2 - Label at Left" DockPanel.Dock="Left" Background="BurlyWood"/>
        <Label Content="3 - Last Label" DockPanel.Dock="Top" Background="Cornsilk"/>
    </DockPanel>


Because the DockPanel container docks elements in the order that they appear in the XAML, if we change the order of the elements, the ListBox will still default to a Dock value of Left, but will dock within the remaining space.

    <DockPanel LastChildFill="False">
        <Label Content="1 - Label at Top" DockPanel.Dock="Top" Background="Bisque"/>
        <ListBox ItemsSource="{Binding MovieList}"/>
        <Label Content="2 - Label at Left" DockPanel.Dock="Left" Background="BurlyWood"/>
        <Label Content="3 - Last Label" DockPanel.Dock="Top" Background="Cornsilk"/>
    </DockPanel>

#445 – DockPanel Can Be Used Like a StackPanel

The DockPanel layout container is most often used to dock other container controls along each edge of a window and perhaps include a control that fills the remaining space.  This works well as the outermost container for a main application window.

But because you can stack multiple controls consecutively on the same side, you can use a DockPanel like a StackPanel, but stacking controls in any direction.

Docking everything to the left:

Docking everything to the right:

To the top:

Or the bottom:

You can also use various combinations, stacking a series of controls to one side and then another group of controls to another side.

    <DockPanel LastChildFill="False">
        <Button Content="1 - Button" DockPanel.Dock="Left"/>
        <Button Content="2 - Button" DockPanel.Dock="Left" />
        <Button Content="3 - Button" DockPanel.Dock="Left" />

        <Button Content="4 - Button" DockPanel.Dock="Bottom" />
        <Button Content="5 - Button" DockPanel.Dock="Bottom" />
        <Button Content="6 - Button" DockPanel.Dock="Bottom" />

        <Button Content="7 - Button" DockPanel.Dock="Right" />
        <Button Content="8 - Button" DockPanel.Dock="Right" />
        <Button Content="9 - Button" DockPanel.Dock="Right" />
    </DockPanel>

#444 – Children of DockPanel Don’t Always Have to be Stretched

Normally, when you dock a child element in a DockPanel to one of the sides, it is stretched to fit the entire length or height of that side.

    <DockPanel>
        <Button Content="1st (Top)" DockPanel.Dock="Top" />
        <Button Content="2nd (Left)" DockPanel.Dock="Left" />
        <Button Content="3rd (Top)" DockPanel.Dock="Top" />
        <Label Content="4th (Right)" DockPanel.Dock="Right" Background="DarkKhaki"/>
        <Label Content="5th, fills remaining space" Background="Khaki"/>
    </DockPanel>

You can, however, use HorizontalAlignment (when docked to Top or Bottom) or VerticalAlignment (when docked to Left or Right) to have the control autosize to fit its content.

    <DockPanel>
        <Button Content="1st (Top/Center)" DockPanel.Dock="Top" HorizontalAlignment="Center"/>
        <Button Content="2nd (Left/Bottom)" DockPanel.Dock="Left" VerticalAlignment="Bottom"/>
        <Button Content="3rd (Top/Right)" DockPanel.Dock="Top" HorizontalAlignment="Right"/>
        <Label Content="4th (Right/Top)" DockPanel.Dock="Right" Background="DarkKhaki" VerticalAlignment="Top"/>
        <Label Content="5th, fills remaining space, Centered" Background="Khaki" HorizontalAlignment="Center"/>
    </DockPanel>

#403 – The Order of DockPanel Children Matters

The layout and appearance of child elements in a DockPanel control is based not only on the Dock property of each child element, but on the order that they are listed in.  This is true because layout of a child is determined based on the available space left after all previous children have already been positioned.

In the example below, we add two Label controls at the top and bottom of the window and then add a StackPanel with buttons after that.

    <DockPanel LastChildFill="False">
        <Label Content="Some intro could go here, across the top" DockPanel.Dock="Top" Background="AliceBlue"/>
        <Label Content="Some pithy quote could go here, on the bottom" DockPanel.Dock="Bottom" Background="LightSteelBlue"/>

        <StackPanel DockPanel.Dock="Left" Background="Cornsilk">
            <Button Content="Open" Margin="5"/>
            <Button Content="Close" Margin="5"/>
            <Button Content="Save" Margin="5"/>
            <Button Content="Export" Margin="5"/>
        </StackPanel>
    </DockPanel>


If we move the StackPanel to be the first child of the DockPanel, it takes up the entire left edge of the window.

#402 – Final Child of DockPanel Fills Remaining Space by Default

By default, the last child in the list of child elements of a DockPanel will automatically grow to fill the available space, regardless of the value of the DockPanel.Dock property.

    <DockPanel>
        <Label Content="1st label (Top)" DockPanel.Dock="Top" Background="Bisque"/>
        <Label Content="2nd (Left)" DockPanel.Dock="Left" Background="BurlyWood"/>
        <Label Content="3rd fills remaining space" DockPanel.Dock="Top" Background="Cornsilk"/>
    </DockPanel>

This happens because the DockPanel has a property called LastChildFill that defaults to true.  If we set it to false, we can then dock the last child element–and it will not fill all of the available space.

    <DockPanel LastChildFill="False">
        <Label Content="1st label (Top)" DockPanel.Dock="Top" Background="Bisque"/>
        <Label Content="2nd (Left)" DockPanel.Dock="Left" Background="BurlyWood"/>
        <Label Content="3rd fills remaining space" DockPanel.Dock="Top" Background="Cornsilk"/>
    </DockPanel>

#401 – DockPanel Element

The DockPanel element serves as a container for a collection of child elements, positioning its children by docking them to one of the sides of the container.  For each of the child elements, you specify a value for the DockPanel.Dock property, which can have one of the following values: Left, Top, Right or Bottom.

The location of each child is calculated one at a time, based on their order.  Each child is docked to one of the sides and stretched to fill the available space.  At each step, the docking/sizing happens within the remaining open space in the DockPanel.

    <DockPanel>
        <Label Content="1st label (Top)" DockPanel.Dock="Top" Background="Bisque"/>
        <Label Content="2nd (Left)" DockPanel.Dock="Left" Background="BurlyWood"/>
        <Label Content="3rd (Top)" DockPanel.Dock="Top" Background="Cornsilk"/>
        <Label Content="4th (Right)" DockPanel.Dock="Right" Background="DarkKhaki"/>
        <Label Content="5th (Bottom)" DockPanel.Dock="Bottom" Background="DarkSeaGreen"/>
        <Label Content="6th (Left)" DockPanel.Dock="Left" Background="Gainsboro"/>
        <Label Content="7th, fills remaining space" Background="Khaki"/>
    </DockPanel>

Resizing: