I have a Grid with 2 columns separated by a GridSplitter using the following XAML code :
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" MinWidth="20" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" MinWidth="100" />
</Grid.ColumnDefinitions>
<Rectangle Fill="Blue" />
<GridSplitter Grid.Column="1" Background="LightGray" HorizontalAlignment="Stretch" />
<Rectangle Fill="Yellow" Grid.Column="2" />
</Grid>
Problem : The MinWidth of the Column on the right is ignored
- I definitely need the first column Width to be "100px" when page loads, so It cannot be * sized.
- I do not want to set a MaxWidth on the first column
*I know that has been adressed before but it always suggest to set column size to * or set a maxWidth on the first column... I don't want that.
Found a solution, but its UGLY! :p, anybody has a cleaner way to achieve what I want... CODELESS (if possible)?
private void Grid_SizeChanged(object sender, SizeChangedEventArgs e)
{
var g = (Grid)sender;
Double maxW = e.NewSize.Width - g.ColumnDefinitions[2].MinWidth - g.ColumnDefinitions[1].ActualWidth;
g.ColumnDefinitions[0].MaxWidth = maxW;
}
The basic problem is that a grid splitter works by adjusting the width of the left column only and assumes the right column will star-size to fit the remaining space.
That means the problem you are trying to solve is actually "how do I limit the max width of the left column so that the right column does not get too small?". This is basically what your code sample is doing.
If you want a more portable solution, that you can implement in XAML, create a Silverlight behavior that can be applied to a grid (as shown below). It will attach to the parent grid's SizeChanged
event and do pretty much exactly what your code snippet does, but being a behavior you can drag and drop them in Blend or attach them in XAML.
Here is a sample behavior I threw together for you as an example (based on your own code):
MinWidthSplitterBehavior.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace GridSplitterMinWidth
{
public class MinWidthSplitterBehavior : Behavior<Grid>
{
public Grid ParentGrid { get; set; }
protected override void OnAttached()
{
base.OnAttached();
ParentGrid = this.AssociatedObject as Grid;
ParentGrid.SizeChanged += parent_SizeChanged;
}
void parent_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (ParentGrid.ColumnDefinitions.Count == 3)
{
Double maxW = e.NewSize.Width - ParentGrid.ColumnDefinitions[2].MinWidth -
ParentGrid.ColumnDefinitions[1].ActualWidth;
ParentGrid.ColumnDefinitions[0].MaxWidth = maxW;
}
}
protected override void OnDetaching()
{
base.OnDetaching();
if (ParentGrid != null)
{
ParentGrid.SizeChanged -= parent_SizeChanged;
}
}
}
}
and use it like this:
<UserControl x:Class="GridSplitterMinWidthApp.MainPage"
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:GridSplitterMinWidth="clr-namespace:GridSplitterMinWidth"
xmlns:interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:Controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" MinWidth="20" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" MinWidth="100" />
</Grid.ColumnDefinitions>
<interactivity:Interaction.Behaviors>
<GridSplitterMinWidth:MinWidthSplitterBehavior/>
</interactivity:Interaction.Behaviors>
<Rectangle Fill="Blue" />
<Controls:GridSplitter Grid.Column="1" Background="LightGray" HorizontalAlignment="Stretch">
</Controls:GridSplitter>
<Rectangle Fill="Yellow" Grid.Column="2" />
</Grid>
</Grid>
</UserControl>
This seems to me to be a bug in the WPF GridSplitter. The root of the problem is using different types of values for the Width column.
This will not work:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" MinWidth="20" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" MinWidth="100" />
</Grid.ColumnDefinitions>
Because column [0] has a width of "100", an explicit number type, and column [1] has a width of "*". But this will work:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="20" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" MinWidth="100" />
</Grid.ColumnDefinitions>
When you do this, both the left and right "MinWidth" values are respected by the GridSplitter. To me, this means bug in WPF.
In my case, I need to set the left column to "Auto" with a minimum, and the right to "*" with a minimum, but there doesn't seem to be any way to do that without some form of codebehind. My guess is that the simplest solution will be to just * size everything and use a custom behavior to replace the "auto" functionality of the column (since that is a one-column, one-time fix), but I have yet to do that. Hopefully, the XAML will look something like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="20" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" MinWidth="100" />
</Grid.ColumnDefinitions>
<interactivity:Interaction.Behaviors>
<MyBehaviors:GridColumnStarSizedToAutoBehavior Column="0"/>
</interactivity:Interaction.Behaviors>
...
</Grid>
Update*: I implemented a behavior as noted in this post above. it was not as simple as I was hoping due to Grid limitations. I can not post the solution due to company limitations, but I can give some tips. The biggest problem I ran into is that you can't really get away from star sizing, even with behavior codebehind, because it makes the MinWidth stop working for the star-sized columns.
To compensate for this involved processing all the columns for the grid. You have to take the columns you want auto-sized, determine their children's desired size by getting UIElements by column and using Measure on them, and then changing their star-size-value to a percentage of total available star-size spacing. You also have to adjust non-auto-star-sized columns star-size value accordingly.
As an example, consider a grid with 3 columns: 1st star-sized with MinWidth we want to be auto, 2nd auto-sized without MinWidth, and third star-sized with MinWidth. If the control is 100 units wide, and the content of the first column wants 25 pixels, and the content of the third column wants 5 pixels, the code has to star-size the first and last column as "25*" and "70*" respectively (or any other numbers that get the right ratio).
This was non-trivial, but did work in the end. I did not account for column spanning.
Sorry I can't post the code.