I want a logical and simple way of producing a layout with one control set to fill and the rest to dock. I could use:
<DockPanel LastChildFill="True">
<Button Content="3" DockPanel.Dock="Bottom" />
<Button Content="2" DockPanel.Dock="Bottom" />
<Button Content="1" />
</DockPanel>
But its not very intuitive to use. I could also do it like this:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button Content="1" Grid.Row="0" />
<Button Content="2" Grid.Row="1" />
<Button Content="3" Grid.Row="2" />
</Grid>
But its also quite alot of xaml. What I really want is something like this:
<StackPanel Fill="None|First|Last">
<Button Content="1" />
<Button Content="2" />
<Button Content="3" />
</StackPanel>
How could this be achieved while not having to reverse the items as with DockPanel and not using a fixed number of rows and attached properties as with Grid?
You can always write your own panel with different docking rules. You could use the standard DockPanel implementation (available in the framework source - it doesn't look very complicated) and create something similar with rules you prefer. You might even be able to create a class which derives from DockPanel and overrides ArrangeOverride.
But personally I would just use the dock panel, which does exactly what you want except that you don't like its rules about which member gets to be the fill.
IME grid has a horrible maintenance problem if you insert/delete rows, in that you find yourself endlessly adjusting row numbers - DockPanel is much easier in that regard.
Update:
Here you go, I've denied you the pleasure of doing this yourself - this is just cut-down/reversed version of the framework source:
public class BottomDockingPanel : DockPanel
{
protected override Size ArrangeOverride(Size arrangeSize)
{
UIElementCollection children = InternalChildren;
int totalChildrenCount = children.Count;
double accumulatedBottom = 0;
for (int i = totalChildrenCount-1; i >=0 ; --i)
{
UIElement child = children[i];
if (child == null) { continue; }
Size childDesiredSize = child.DesiredSize;
Rect rcChild = new Rect(
0,
0,
Math.Max(0.0, arrangeSize.Width - (0 + (double)0)),
Math.Max(0.0, arrangeSize.Height - (0 + accumulatedBottom)));
if (i > 0)
{
accumulatedBottom += childDesiredSize.Height;
rcChild.Y = Math.Max(0.0, arrangeSize.Height - accumulatedBottom);
rcChild.Height = childDesiredSize.Height;
}
child.Arrange(rcChild);
}
return (arrangeSize);
}
}