Andy T
Andy T

Reputation: 1971

WPF - DataGrid Column with Width="*", but MinWidth to Fit Contents

What would be the best/right way to have a set of DataGrid columns have proportional width (Width="\*"), but to have their minimum width be at least the width of their content? At the moment, if I use Width="*", then the columns stay exactly proportional, but content gets cropped if the columns get too thin. If I use Width="Auto", then the columns size themselves perfectly to their content, but this makes them all different sizes.

What I want is really a combination of the two, like Width="\*", MinWidth="Auto" so that when there's extra width the columns will all space out to equal widths, but when the grid is made smaller, the content never gets cropped.

Sadly, MinWidth="Auto" doesn't exist, so I guess I need to bind the column's MinWidth property, but it's hard to figure out exactly what I would bind it to.

How do I tell WPF "MinWidth=" the width of the column's widest piece of content?

Upvotes: 38

Views: 58617

Answers (5)

Marek S.
Marek S.

Reputation: 11

You can create a dependency property (called e.g. HorizontalPropFillOfBlankSpace) for Grid control which will ensure what you need (columns with Width="*", but MinWidth to fit contents). Then you can apply it on any grid you want:

<Grid namespace:GridHelper.HorizontalPropFillOfBlankSpace="True">
   <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="Auto" />
   </Grid.ColumnDefinitions>
   ...

You can see an example of the implementation of this dependency property below. Only columns with Width="Auto" are automatically resized to fill gap space. It can be customized by you what you need.

public class GridHelper
{
   /// <summary>
   /// Columns are resized to proportionally fill horizontal blank space.
   /// It is applied only on columns with the Width property set to "Auto".
   /// Minimum width of columns is defined by their content.
   /// </summary>
   public static readonly DependencyProperty HorizontalPropFillOfBlankSpaceProperty =
      DependencyProperty.RegisterAttached("HorizontalPropFillOfBlankSpace", typeof(bool), typeof(GridHelper), new UIPropertyMetadata(false, OnHorizontalPropFillChanged));

   public static bool GetHorizontalPropFillOfBlankSpace(Grid grid)
      => (bool)grid.GetValue(HorizontalPropFillOfBlankSpaceProperty);

   public static void SetHorizontalPropFillOfBlankSpace(Grid grid, bool value)
      => grid.SetValue(HorizontalPropFillOfBlankSpaceProperty, value);

   private static void OnHorizontalPropFillChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
   {
      if (!(d is Grid grid))
         return;

      if ((bool)e.NewValue)
      {
         grid.Loaded += Grid_Loaded;
      }
      else
      {
         grid.Loaded -= Grid_Loaded;
      }
   }

   private static void Grid_Loaded(object sender, RoutedEventArgs e)
   {
      if (!(sender is Grid grid))
         return;

      foreach (var cd in grid.ColumnDefinitions)
      {
         if (cd.Width.IsAuto && cd.ActualWidth != 0d)
         {
            if (cd.MinWidth == 0d)
               cd.MinWidth = cd.ActualWidth;
            cd.Width = new GridLength(1d, GridUnitType.Star);
         }
      }
   }
}

Upvotes: 0

Ollikat
Ollikat

Reputation: 241

I also had problems to size Grid columns correctly inside the GridViewColumn. There were several things that I tried but then I found the UniformGrid. It was the ultimate solution for me. It just works. I haven't knew it before...seems that it doesn't exist in VS toolbox by default (?) and thus didn't know it even exists.

You can find more about UniformGrid from here.

Upvotes: 1

danielcooperxyz
danielcooperxyz

Reputation: 999

Give the column a name in the XAML:

<Grid>
      <Grid.ColumnDefinitions>
             <ColumnDefinition Width="*" Name="Col1"/>
             <ColumnDefinition Width="*"/>
      </Grid.ColumnDefinitions>
</Grid>

Then set the MinWidth property in the code as shown below:

public MainWindow()
{
    InitializeComponent();

    Col1.MinWidth = 340; //enter desired minimum width here
}

Upvotes: -2

Tomas Grosup
Tomas Grosup

Reputation: 6514

I know its a bit late, but I found your question and programmed a pure-XAML solution.

 <ColumnDefinition Width="42*" MinWidth="{Binding Path=ActualWidth, ElementName=projectInfoHeader }"/> 

Where the ElementName points to the control taking up most of the space. Of course thats only possible to do with elements, that do have a limited width. If you do it for example for a GroupBox, than you can resize only to larger width and never resize to smaller one.

If you have several candidates for the value of MinWidth, you need to write yourself a IMultiValueConverter, which takes an object[], parses it to floats, and returns the maximum (its just 1 linq query if you use it only yourselves and don't need to handle bad usage of the converter)

This way also supports dynamic changing of the MinWidth.

Upvotes: 24

Anon
Anon

Reputation: 131

Set Width = "Auto" in XAML.

Then in the code:

MinWidth = ActualWidth
Width = new GridLength(1, GridUnitType.Star)

Upvotes: 13

Related Questions