Ignacio Soler Garcia
Ignacio Soler Garcia

Reputation: 21855

How to size the WPF Grid panel this way?

I have this pretty simple window with a Grid containing two columns, a TextBlock and a TextBox.

enter image description here

What I need it to set the column 0 to automatically size to its content and to have column 1 (content) to be 4 times the size of the column 0.

How can I do that. I will create a Grid descendant if this is the solution because I really need this feature.

Edit: more explanations. The content of the column 0 won't change at runtime so the size of the column 0 or column 1 must not change on runtime. The grid will be the child of a window configured with SizeToContent="WidthAndHeight" so no extra space must exist.

Answer to Dmitry: I tried what you say with the following code and it is not working at all:

<Window x:Class="UnderstandSizing.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" 
SizeToContent="WidthAndHeight" >
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width=".25*" />
        <ColumnDefinition Width=".75*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <TextBlock Grid.Column="0" Text="THIS IS THE LABEL:" />
    <TextBox Grid.Column="1" Text="content" />
</Grid>
</Window>

Last Edit: Why the hell do I (or anyone) need this?

One of the nice things of WPF is its hability to work without fixed sizes right? If you are impaired and you have a bigger font size everything will look fine. If you translate your UI to another language that needs x2 size everything will look fine. If you have different PPI settings everything will look fine.

But what I don't want to see is screens changing its size at runtime because users are not used to this. That's why I want to set the size of the entry fields to a multiple of a know field. If I let the label cell to re size to what it needs and then I set the content cell to a multiplier of the label I will get the benefits of autosizing with the behaviour that users expect of having fixed size screens (unless they change it resizing it).

Upvotes: 3

Views: 5952

Answers (4)

Louis Kottmann
Louis Kottmann

Reputation: 16648

You can use bindings on grid columns:

<Grid.ColumnDefinitions>
   <ColmunDefinition Width="Auto" x:Name="firstCol"/>
   <ColumnDefinition Width="{Binding ActualWidth, ElementName=firstCol, Converter={StaticResource MultiplyConverter}, ConverterParameter=4}" />
</Grid.ColumnDefinitions>

Then the converter:

public class MultiplyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double originalValue;
        double multiplier;
        if (value != null && parameter != null && 
            double.TryParse(value.ToString(), out originalValue) &&
            double.TryParse(parameter.ToString(), out multiplier)) //Can be lots of things: sentinel object, NaN (not a number)...
        {
            return originalValue * multiplier;
        }
        else return Binding.DoNothing;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

You could write an attached property for grid columns also.

Upvotes: 8

S2S2
S2S2

Reputation: 8502

As I understand you are trying to layout the label (textblock) and the corresponding entry field for it. As an indicator, you should first see the Beth Massi's Windows Client video on simple data entry form which is on developing a data entry form but laying out is also demonstrated very well.

Particularly observe how she lays out the controls on the WPF Window using the designer, properties window and XAML, and then I think you should not have a need for the first column to be of Auto size and second column of 4* first column.

EDIT: As per update in question, I am posting a possible solution as below:

  1. XAML file code (Notice the SizeToContent usage in Window and that the binding is to ActualWidth property for Textbox control):

    <Window x:Class="GridTest.MainWindow"
        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:local="clr-namespace:GridTest"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        d:DesignHeight="350"
        d:DesignWidth="525"
        SizeToContent="WidthAndHeight"
        mc:Ignorable="d">
    <Grid>
        <Grid.Resources>
            <local:FourWidthConverter x:Key="FourWidthConv" />
        </Grid.Resources>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <TextBlock Name="tb"
                   Grid.Column="0"
                   Text="Auto Text, change at design time to see changes in Width" />
        <TextBox Name="tx"
                 Grid.Column="1"
                 Width="{Binding ElementName=tb,
                                 Path=ActualWidth,
                                 Converter={StaticResource FourWidthConv}}"
                 Text="4 * col 1 width displaying Text in SizetoContentWindow" />
    </Grid>
    </Window>
    
  2. .Xaml.cs file code (Notice the converter here):

    using System.Windows;
    using System.Windows.Data;
    
    namespace GridTest
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
        }
    
        public class FourWidthConverter : IValueConverter
        {
            public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                return 4 * (double)value;
            }
    
            public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new System.NotImplementedException();
            }
        }
    }
    

Upvotes: 0

user572559
user572559

Reputation:

Taken from: http://www.tanguay.info/web/index.php?pg=codeExamples&id=36

the idea is to use:

Update 2 - full XAML posted (mind the behavior of your grid's children):

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width=".25*" />
                <ColumnDefinition Width=".75*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Grid Background="Yellow" Grid.Column="0">
                <TextBlock  Text="THIS IS THE LABEL:" />
            </Grid>
            <Grid Background="Blue" Grid.Column="1">
                <TextBox Background="Transparent" Text="content" />
            </Grid>

        </Grid>

    </Grid>
</Window>

I've just checked it and it worked.

Update: Simply put - there's no out of the box way for doing this in WPF. Depending on your circumstanes you'll have to come up with some sort of a trick to get your grid working.

Another problem is - in general WPF layouts are designed to be protective, i.e. if your grid's child cannot be shrinked down - normally your layout logic should accomodate for it, typically by suspending some layout rules.

Upvotes: 0

Martin Hennings
Martin Hennings

Reputation: 16866

Edit:

If the sizes are known at compile time, wouldn't it be easier to set the widths manually?


You could use a BindingConverter for that, I'd go with separate items in a horizontal StackPanel (look that the StackPanel's width is big enough for your contents):

Here's the cleaned code.

MainWindow.xaml:

<!-- define xmlns:local="clr-namespace:YourAppNamespace" -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
    <StackPanel.Resources>
        <local:WidthConverter x:Key="wc" />
    </StackPanel.Resources>
    <Grid Background="Gray" x:Name="col1">
        <TextBlock Text="blub"/>
    </Grid>
    <Grid Background="Orange" Width="{Binding ElementName=col1, Path=ActualWidth, Converter={StaticResource ResourceKey=wc}}">
        <Label Content="bla"></Label>
    </Grid>
</StackPanel>

MainWindow.xaml.cs:

public class WidthConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (!(value is double))
        {
            return Binding.DoNothing;
        }
        return ((double)value) * 4.0;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Upvotes: 0

Related Questions