I have a DataGrid in WPF with 3 columns. I would like these columns to take up all the space available in the grid. So for example:
Column 1 takes 40% of the grid's width
Column 2 takes 30% of the grid's width
Column 3 takes 30% of the grid's width
Such that even when resizing the window or grid the columns width resizes accordingly. Anyway I can achieve this.
Thanks
Regards
Gabriel.
I see you already found the answer you were looking for based on your comment. However, in case anybody else runs across this question trying to figure out how to get the column ratios (like your example of Column 1 = 40%, Column 2 = 30%, Column 3 = 30%), you can specify the ratios with * sizing for column widths as follows:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
</Grid>
Related
This very frustrating problem keeps happening (now in vs 2019 and before in vs 2017) and after finding zilch looking through the XAML options and searching for similar issues I was hoping someone can help with this:
Selecting elements in the designer (particularly grid elements) will sometimes alter the * grid definitions to crazy values that will LOOK the same visually but then when I look at them markup at some point the ratios have gotten HUGE or even it will add extra rows/cols and then automatically change the spans of what is actually there its nerveracking!
Any ideas what this awful setting is or what I'm clicking to make this happen?
For example it will change this:
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="7*" />
To this:
<ColumnDefinition Width="37*" />
<ColumnDefinition Width="30" />
<ColumnDefinition Width="513*" />
And it never affects the view in the designer just the markup. That's as much explanation I can give offhand, any help would be great!
The ratio of rows to columns in a Grid is very precise. For your first ColumnDefinition, means dividing the width of the whole Grid into ten parts,the first column is 20% of the width of Grid, second is 10%,third is 30%.This is desgined by your need.
When you do a little modifity in the designer, all column widths maybe distributed proportionally if you choose Star for the ColumnDefinition in the designer as the below picture shown:
I have 3 grids on the same row on a grid. Is there a way to stretch just the middle grid but not the other two upon resizing? All I could do right now is to stretch the last one.
I have tried to set the middle grid's horizontalAlignment to stretch but then it stretches all the way and even overlapped the last grid without the program running. Still don't quite understand why....
Not sure if i understood your layout/plan, but you should be able to do this with the Row/Column in the middle haivng star size.
e.g.
<Grid>
<Grid.ColumnDefintions>
<ColumnDefintion Width="Auto"/>
<ColumnDefintion Width="*"/>
<ColumnDefintion Width="Auto"/>
</Grid.ColumnDefintions>
<Grid Grid.Column="0">...</Grid>
<Grid Grid.Column="1">...</Grid> <!-- Middle will stretch -->
<Grid Grid.Column="2">...</Grid>
</Grid>
What is the meaning of * (asterisk) in the XAML below?
<ColumnDefinition Width="0.07*"/>
<Grid Height="100" HorizontalAlignment="Left"
Margin="102,134,0,0"
Name="grid1" VerticalAlignment="Top"
Width="354">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40*" />
<ColumnDefinition Width="314*" />
</Grid.ColumnDefinitions>
</Grid>
When you define a column in a WPF grid you can set the width to one of three possible values:
A fixed width,
Auto – column will become as wide as necessary to fit its children, or
* (star) take up any available remaining space
The * is prefixed by a number (default is 1 if no number is specified). The available space is divided among the starred columns in proportion to the prefix number.
If you have this definition
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.07*"/>
<ColumnDefinition Width="0.93*"/>
</Grid.ColumnDefinitions>
The first column will get 7% of the total space available and the second column would get 93%. On the other hand if you had this definition:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.07*"/>
<ColumnDefinition Width="0.14*"/>
</Grid.ColumnDefinitions>
The first column would get 1/3 and the second 2/3 of the available space.
In your specific case where the width of the grid is 354 and the proportions of the two columns are 40 and 314 you get the following column widths:
First column width = 40/(40 + 314)*354 = 40
Second coulmn width = 314/(40 + 314)*354 = 314
The star width is best used when the width of the grid isn't fixed. When the grid is resized the columns will then scale proportionally as specified by the star widths. In your case the width of the grid is fixed and you could just as easily have used fixed width columns.
If you want a layout where the second column is double the width of the first and the third column is triple the width of the first you need this definition:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
If the total width of the grid is 300 you get column widths 50, 100 and 150. If the total width of the grid is 600 you get column widths 100, 200 and 300. And so on.
Its 0.07 ratio to any other star-width column - i.e. if another ColomnDefinition has a Width of 0.14 then that column is double the width = its all about rations
It creates column sizes using ratios. If you had another definition like <ColumnDefinition Width="0.03*"/> the first column would take up 70% of space and the second one would take up 30%.
[..] a value that is expressed as a weighted proportion of available space.
I asked a question at http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/5c7f5cdf-4351-4969-990f-29ce9ec84b87/ , but still lack a good explanation for a strange behavior.
Running the following XAML shows that the TextBlock in column 0 is width greater than 100 even though the column is set to width 100. I think that the strangeness may have something to do with it being wrapped in a ScrollViewer, but I don't know why. If I set a MaxWidth on the columns, it works fine, but setting Width does not.
Why is the width of column 0 not being honored?
Why does the column sizing behave differently when you remove the scroll viewer?
I appreciate any explanation! This is a real puzzle to me.
<Window x:Class="WpfApplication2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Width="300">
<ScrollViewer HorizontalScrollBarVisibility="Auto" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock x:Name="textBlock" Text="{Binding ElementName=textBlock, Path=ActualWidth}" />
<TextBlock Text="column 1" Grid.Column="1" />
<TextBlock Grid.Row="1" Grid.ColumnSpan="3" Text="text here that is wider than the first two columns combined" />
</Grid>
</ScrollViewer>
</Window>
This is a very good question and tests the limits of our intuition. It reveals the implementation details of the Grid's layout logic.
The width of 100 is not being honored because:
There is nothing in the third column that causes the grid to give it width.
The long text in the second row is wider than can fit in the first two columns.
When the width of the Grid is not constrained or set by its parent its layout logic evidently stretches the first column instead of the last column.
By putting a MaxWidth on the first column, you are constraining the Grid's layout logic, so it moves on to the second column and stretches it. You'll note it will be wider than 100 in that scenario.
However, as soon as the Grid's width is set to a specific value or is constrained by its parent (e.g. when no ScrollViewer in the Window), the Grid's width has a specific value, and the third column gets a width set even though it is empty. Now the Grid's auto-size code is deactivated, and it no longer stretches any of your columns to try to squeeze in that text. You can see this by putting a specific width on the Grid, even though it is still in the ScrollViewer.
Edit: Now that I read the answer of the MSDN support in your original thread, I believe it is correct, meaning this is probably the result of the implementation of the attached property and not the grid itself. However, the principle is the same, and hopefully my explanation is clear enough to make sense of the subtlety here.
Short Answer:
Its because of the combination of:
1. Presence of ScrollViewer which allows grid (if it wishes) to take any desired size.
2. The grid not having explicit width.
3. A column (Column 2) whose width has not been specified, which sets it to 1*, leaving its final size dependant on size of grid and other columns.
4. TextBlock which has colspan over three columns.
If you:
1. Remove the scrollviewer, the grid is allowed to grow only till the client area of window (which comes to be about 278 in your example), and the long textblock has to fit within this width otherwise its trimmed.
2. Set explicit width of Grid, which again trims textblock to fit.
3. Set explicit width of Column 2, which provides a fixed width to grid (100+100+width_of_col2), which again trims textblock to fit.
4. Remove the colspan, the columns which do not contain it and have fixed width defined, will take that width.
Here's what's happening:
This is crude and not exact explanation of the measure and arrange passes, however, it should give a fair idea.
To start with col0 is happy with 100, col1 with 100 and col2 with 0. Based on this grid's size would be 100+100+0=200. When Grid requests its children (textblocks) to be measured, it sees that first two textblocks fit within the width of their columns. However, the third textblock needs 288. Since, grid isn't having any width defined and its within a scrollviewer, it can increase its size if one of its child needs it. The Grid has now to increase its size from 200 to 288 (i.e. by 88). This means each column to which that textblock spans (all three of them) will expand by 88/3~=29 pixels. This makes col0=100+29=129, col1=100+29=129, col2=0+29.
Try this:
Include a rectangle, put it in col2 and set width of rectangle to 20.
This is what's happening:
To start with col0 and col1 are happy with 100 each as their individual textblocks need less than that. col2 is happy with 20 as rectangle in it needs that. Based on this grid's width would be 100+100+20=220. However, because of the columnspanning textblock the Grid has to increase its size from 220 to 288 (i.e. by 68). This means each column to which that textblock spans (all three of them) will expand by 68/3~=23 pixels. This makes col0=100+23=123, col1=100+23=123, col2=20+23=43.
HTH.
Here is another example that shows the problem using a Canvas instead of a ScrollViewer:
<Canvas>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="textBlock1" Text="{Binding ElementName=textBlock1, Path=ActualWidth}"/>
<TextBlock Grid.Column="1" x:Name="textBlock2" Text="{Binding ElementName=textBlock2, Path=ActualWidth}"/>
<TextBlock Grid.Row="1" Grid.ColumnSpan="3" Width="300"/>
</Grid>
</Canvas>
This example shows that when given unlimited space, the first two columns are incorrectly expanded by 33%. I do not have working reference source to debug this right now because SP1 broke .NET4 reference source but frankly pinpointing this to the line in the source file is not going to help you so let's not go that route.
Instead, we'll agree that this is definitely a bug and we can prove that it's a bug by setting Grid.MaxWidth to progressively larger values and the widths of two columns remain both at 100 no matter how large it gets. But if you leave Grid.MaxWidth unset and place the Grid inside of a Canvas then the value during measure will be double.PositiveInfinity and this value with produce column widths of 133. As a result we can speculate that some how the special case of a size constraint of positive infinity is not handled correctly during the column sizing calculations.
Luckily, that same experiment provides a simple workaround: simply supply an absurdly large value for Grid.MaxWidth when the Grid is used inside another control that allows it unlimited space, such as a ScrollViewer or a Canvas. I recommend something like:
<Grid MaxWidth="1000000">
This approach avoids the bug by preventing the size constraint from having the probematic value of positive infinity, while practically achieving the same effect.
But this:
<Grid MaxWidth="{x:Static sys:Double.PositiveInfinity}">
will trigger the bug.
I reported this issue as a bug:
https://connect.microsoft.com/VisualStudio/feedback/details/665448/wpf-grids-columns-width-property-not-honored-when-columnspan-row-forces-grid-to-grow
Please vote it up at that link if you agree that it is a bug.
I'm having some trouble getting this to work in a WPF app I'm working on. Basically, what I'm after is something like the Task pane in an MMC:
The app has three columns in the main part of the display. I need a column on the right side which is resizable. I presume this means using a Grid with a GridSplitter but anything that works will do.
I want to be able to save the width of the right-side column when the app is closed and load it when the app is opened but this should be an initial size: the user should be able to resize it.
When I resize the window, I want the left- and right-side columns to stay the same size and the middle column to resize with the window width.
The left- and right-side columns need to have a minimum width. When I resize the right-side column I want the centre column to get smaller but not the left-side column.
I also want to be able to toggle the visibility of the right-side column with a toggle button which is outside the column and when it returns to visibility I want it to be the same width it was before.
I'm trying to do as much as possible in XAML and with binding.
And can I have it topped with cream, ice cream and chocolate chips, please? :-)
As I read your requirements, instead of thinking of a Grid, I think of a DockPanel.
<DockPanel>
<Grid Name="right"
DockPanel.Dock="Right" MinWidth="100" />
<Grid Name="Left"
DockPanel.Dock="Left" MinWidth="100" />
<Grid Name="middle" />
</DockPanel>
If you make a way to resize right, then middle will change as right is resized. If you resize the window, only middle will change. Storing and setting the Width of right is up to you, but shouldn't be hard.
As for allowing the user to resize right, that will a bit trickier, but I found this article that should help. This other article might help even more.
For the visibility of right, you can set its Visibility to Collapsed to hide it and restore it by setting it to Visible.
Note: The panels inside don't have to be Grids, but you will want to use some sort of Panel for each. Whatever you have inside your current Grid columns should work just fine.
I used a Grid with GridSplitters since this made it really easy to resize the middle column while maintaining the widths of the left and right columns.
XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MainWindow"
Title="Main Window"
Width="640" Height="480">
<Grid>
<Grid.ColumnDefinitions>
<!-- Left column -->
<ColumnDefinition Width="200" MinWidth="100"/>
<!-- Left GridSplitter column -->
<ColumnDefinition Width="5"/>
<!-- Center column. A width of * means the column will fill
any remaining space. -->
<ColumnDefinition Width="*"/>
<!-- Right GridSplitter column -->
<ColumnDefinition x:Name="RightSplitterColumn" Width="5"/>
<!-- Right column -->
<ColumnDefinition x:Name="RightColumn" Width="200"
MinWidth="100"/>
</Grid.ColumnDefinitions>
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
<GridSplitter Grid.Column="3" HorizontalAlignment="Stretch" />
<Button x:Name="ToggleButton" Grid.Column="2"
Content="Toggle Right Column" Width="150" Height="25"
Click="ToggleButton_Click" />
</Grid>
</Window>
Code-Behind
When hiding the right column, I just set the column width to 0 since grid columns don't have a visibility property.
public partial class MainWindow : Window
{
private double rightColumnWidth;
private double rightColumnMinWidth;
private bool rightColumnHidden;
public MainWindow()
{
this.InitializeComponent();
}
private void ToggleButton_Click(object sender, RoutedEventArgs e)
{
if (rightColumnHidden)
{
// Restore the widths.
RightColumn.MinWidth = rightColumnMinWidth;
RightColumn.Width = new GridLength(rightColumnWidth);
RightSplitterColumn.Width = new GridLength(5);
}
else
{
// Remember the user-set widths for the columns.
rightColumnWidth = RightColumn.Width.Value;
rightColumnMinWidth = RightColumn.MinWidth;
// Remember to set the minimum width to 0 before changing the actual
// width.
RightColumn.MinWidth = 0;
RightColumn.Width = new GridLength(0);
RightSplitterColumn.Width = new GridLength(0);
}
rightColumnHidden = !rightColumnHidden;
}
}
As for saving and restoring the column widths on startup, I would just store the width variables to a settings file and then apply them when your app is reopened.
Set the columndefinition Width to Auto and put a control inside that column and give Star for the other columns . Whenever you want to hide the column with content, set the control.Visibility=Collapsed and since column width is Auto, you wont see that column and the remaining columns will take the space.
3 years later you can find another approach on CodeProject.
http://www.codeproject.com/Articles/437237/WPF-Grid-Column-and-Row-Hiding
It adds a "Visible" property to custom Column definitions.