Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Limit maximum width of "Auto" column in WPF data grid

I have a standard WPF grid with 2 columns defined as:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" MinWidth="200" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    ...
</Grid>

What I want to achieve is that the second column is automatically sized based on its content and the first column gets all remaining space. However I also want the first column to never have less than 200 pixels. The problem is that with this configuration my auto column will be cut off instead of being resized.

What I really want is WPF to first assign 200 pixels to the first columns, then take all remaining space and ask the content of the second column to size itself with a maximum size restriction of the remaining width. If the content needs less space, it should assign the remaining space to the first column (making it bigger than the 200 pixel).

Use case: My second column is an image with a fixed aspect ratio, so that for a given height there is an optimal width. I also have a first column with some controls that should always be visible. If there is more space available than needed I would like to assign the space to the first column instead of the second.

Is there a way to achieve this with default WPF controls or do I need to write my own Grid for this?

like image 902
aKzenT Avatar asked Jan 22 '13 15:01

aKzenT


People also ask

Can user resize columns WPF?

Column and Row Resizing | ComponentOne DataGrid for WPF and Silverlight. By default end users can resize columns and rows in the grid at run time.

What is SharedSizeGroup WPF?

You can use the SharedSizeGroup attribute to share column sizes across different grids. You can't normally share sizes of star-sized columns across multiple grids, however, since star sizing works only in the context of the current grid.

What is the difference between Grid and DataGrid in WPF?

A Grid is a control for laying out other controls on the form (or page). A DataGrid is a control for displaying tabular data as read from a database for example.


1 Answers

It sounds like what you want is basically to have 2 different modes for your second column. When there's enough space for the entire image to display at it's current height it should be Auto, otherwise it should just get the remaining space left after taking 200 for the first column. To do this kind of mode-switching you can use a converter and bind the Width for your column.

Here you have 2 basic inputs: the total size available to the Grid, and the desired width of the image. Since you need them both you'll need an IMultiValueConverter. Here's a basic implementation to calculate the switch described above:

public class AutoColumnConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        double imageWidth = values.OfType<double>().FirstOrDefault();
        double gridWidth = values.OfType<double>().ElementAtOrDefault(1);
        int minWidth = System.Convert.ToInt32(parameter);

        double availableSpace = gridWidth - minWidth;
        if (imageWidth > availableSpace)
            return new GridLength(availableSpace, GridUnitType.Pixel);

        return new GridLength(0, GridUnitType.Auto);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

To use this you need to wrap your Grid in another element that you can bind to instead of the Grid itself, which will essentially lie about the space available to it and try to stretch out further with the two defined columns. Here I'm using Source.Width to find the image's desired width. You may want to adjust if you care more about the aspect ratio or otherwise want to account for height too.

<Border>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" MinWidth="200" />
            <ColumnDefinition>
                <ColumnDefinition.Width>
                    <MultiBinding ConverterParameter="200">
                        <MultiBinding.Converter>
                            <local:AutoColumnConverter/>
                        </MultiBinding.Converter>
                        <Binding ElementName="Img" Path="Source.Width"/>
                        <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type Border}}" Path="ActualWidth"/>
                    </MultiBinding>
                </ColumnDefinition.Width>
            </ColumnDefinition>
        </Grid.ColumnDefinitions>

        <Image x:Name="Img" Grid.Column="1" Stretch="Uniform" StretchDirection="DownOnly"
                Source="..."/>
    </Grid>
</Border>
like image 73
John Bowen Avatar answered Nov 13 '22 12:11

John Bowen