Text wrapping expands the column to fit the text - wpf

I've got a grid defined simply:
<Grid Margin="0,5,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"></ColumnDefinition>
<ColumnDefinition Width="50"></ColumnDefinition>
<ColumnDefinition Width="48"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
Then I'm trying to bind some content like this:
<TextBlock TextWrapping="Wrap" Grid.Column="3" Text="{Binding Text}">
Set up like this, the text won't wrap. It simply expands the column to fit the text. If I set the Width to a fixed amount on the last column, wrapping works as expected. The problem there is that if the user widens the window, the column stays at a fixed size. How can I get the column to size dynamically with the width of the grid, but still wrap the text within it?

A width of "*" will split any remaining space evenly between the columns using "*". If you have a single column with Width="*", that column will get all remaining space. If you have 2 columns with Width="*", each will get 1/2 of the remaining space.
Here's a good article on grid sizing that includes star sizing.

There's one frustrating case I discovered that can break even with Width="*" and thats when you have IsSharedSizeScope= true.
<Border BorderBrush="Red" BorderThickness="1">
<StackPanel Grid.IsSharedSizeScope="True">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="G1"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="G2" />
<ColumnDefinition Width="*" SharedSizeGroup="G3" />
</Grid.ColumnDefinitions>
<TextBlock Text="Col0" Grid.Column="0" Margin="0,0,5,0"/>
<TextBlock Text="Col1" Grid.Column="1" Margin="0,0,5,0"/>
<TextBlock Text="A B C D E F G H I J K L M N O P Q R S T U V W X Y Z" TextWrapping="Wrap" Grid.Column="2"/>
</Grid>
</StackPanel>
</Border>
This won't wrap, but if you change Grid.IsSharedScopeSize to false then it does.
Haven't found a solution yet, but this can be another reason it won't work.

Try this:
<Grid Margin="0,5,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"></ColumnDefinition>
<ColumnDefinition Width="50"></ColumnDefinition>
<ColumnDefinition Width="48"></ColumnDefinition>
<ColumnDefinition Name="ParentColumn" Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock TextWrapping="Wrap" Grid.Column="3" Text="{Binding Text}"
MaxWidth="{Binding ActualWidth, ElementName=ParentColumn}">

Set its width to "*"

You should use Auto only when you want to column/row to size depending on content in said column/row. If you want to "asign rest of space" use "*".
In your case, TextBlock needs to know how much space it has before acutal measure, so it can tell where to wrap the text.

Related

How to layout controls and make it resizable

I would like to place the controls as listed below in WPF. Please given an example to do the same.
<Grid x:Name="ContentRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" .../>
<TextBox Grid.Column="1" .../>
<Button Grid.Column="2" .../>
<Button Grid.Column="3" .../>
</Grid>
Assuming that ContentRoot is placed directly in your Window, it'll inherit it's Width and Height from the Window. Then it'll assign 200px to the 0th column, and 100px each to the 2nd and 3rd columns. And any leftover space will be assigned to the 1st column.
Of course, you can change 200, 100, and 100 to whatever you want.
If needed, add MinWidth="<value>" (replace <value> with a number) in the 1st ColumnDefnition to specify a minimum width that column must have.

Autosizing a grid column to take up remaining space in parent

In WPF, I am having a heck of a time trying to get a grid to size properly.
I have the following layout for my grid:
<ItemsControl HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" />
<Label Grid.Column="1"/>
<TextBox Grid.Column="2"/>
<Button Grid.Column="3"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The problem is that Width="Auto" seems to be sizing that column to the width of the content, and not filling out the extra space in the parent container. This leaves the rest of the columns all unaligned, and ugly blank space at the end of each row.
I'm probably missing something simple, but I can't seem to find a method to fit the column appropriately.
Or is there a better control for the job?
* means fill or share. If you had two with * then they would share the width evenly.
<ColumnDefinition Width="*"/>
Seems I found a solution after a bit more tinkering around.
The problem was: <ColumnDefinition Width="Auto"/>
This was causing the column to fit to the content. I changed it to: <ColumnDefinition />
This causes the column to fit to the empty space left in the parent container, regardless of content size.

Clipping from left rather than right when there isn't enough room for a child element?

I have a grid with three columns of width *, Auto, *.
When I collapse the grid width using an expander, I want the central column to remain fixed size. I want the right hand column to begin clipping from the right hand edge. This is the default behaviour, and already works fine. However, I would like the first column to clip from the left-hand side. That is to say, I'd like the left-hand edge of the column to become hidden first. The left-hand column, is currently clipping from the right-hand edge.
Hope I've explained that well enough! Thanks
You probably need to set HorizontalAlignment="Right" and ="Left" appropriately.
Here's an example, which may not match your requirements exactly, but clips from left and right as you require:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Left hand column" HorizontalAlignment="Right"/>
<Rectangle Grid.Column="1" Fill="Blue" Width="100"/>
<TextBlock Grid.Column="2" Text="Right hand column" HorizontalAlignment="Left"/>
<GridSplitter Grid.Column="3" Width="4" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext"/>
<Rectangle Grid.Column="4" Fill="Red"/>
</Grid>

Within a Grid, how to set a textblock's width

I have a Grid, there are two columns, here is the definition.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180"></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Name="nText" Text="{Binding Name}" Grid.Column="0" />
<TextBlock Name="vText" Text="{Binding Value}" Grid.Column="1" HorizontalAlignment="Left" TextWrapping="Wrap"/>
</Grid>
My question is, for the vText, when the Text is too long, the text can not be wrapped, no matter the columndefinition width is auto or *. But if I set a definited value to the column 2 's width or to the vText 's width, the text will be wrapped.
How can I set the textblock's width so that the text content can be wrapped?
I have tried to bind to columnDefinition's width/ActualWidth, but also failed.
Great thanks.
To elaborate on BalamBalam's answer, changing
<ColumnDefinition Width="auto"></ColumnDefinition>
to
<ColumnDefinition Width="*"></ColumnDefinition>
will do what you want because "auto" mode expects to infer its size based on the child elements. In this case, your TextBlock doesn't have a Width defined, so there's no way for the ColumnDefinition to infer the width.
Because neither the ColumnDefinition nor the TextBlock have a width defined, the TextBlock's width will default to infinite, and the word wrap algorithm will never clip the text.
By contrast, "*" means to fill remaining space, which will either be defined by the grid (if you set a width on it), or one of its parents. Worst case, the "*" will be able to find a value at the top level, because a window always has a width/height set.
Hopefully this sheds a bit more light on how the layout engine does its magic with sizes!
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Name="nText" Text="nText"/>
<TextBlock Grid.Column="1" Grid.Row="0" Name="vText" TextWrapping="Wrap">
vText vTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvText
vTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvText
vTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvTextvText
</TextBlock>
</Grid>

WPF: getting AccessText in ScrollViewer to wrap

I have a Grid containing a ScrollViewer containing AccessText. I want the AccessText to take up the full width of the ScrollViewer, which should take up the full width of the Grid, without any overflow. Currently, the contents of my AccessText are cut off on the right side of the screen instead of wrapping. I have tried setting AccessText.TextWrapping to Wrap, WrapWithOverflow, and I've also tried removing the property entirely. I switched to using a Grid instead of a StackPanel because I thought that might affect how the contents are sized, but that hasn't helped. Here's what I have:
<Grid MaxHeight="40">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Whee a label:" Grid.Column="0"/>
<ScrollViewer Grid.Column="1" VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
<AccessText Text="{Binding MyLongTextField}"/>
</ScrollViewer>
</Grid>
When you set ColumnDefinition Width to Auto, the ScrollViewer within it won't be limited by the "visible Width" of the Column, so it will still take up as much horizontal space as it needs. With the xaml you posted, I think Width="*" will suit your needs. For the ScrollViewer, it seems like you don't want it to be able to Scroll horizontaly but only verticaly? In that case, set HorizontalScrollBarVisibility="Disabled". Otherwise you'll get a Horizontal ScrollBar.
<Grid MaxHeight="40">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Whee a label:" Grid.Column="0"/>
<ScrollViewer Grid.Column="1" VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<AccessText TextWrapping="Wrap" Text="{Binding MyLongTextField}"/>
</ScrollViewer>
</Grid>
If you simply want the AccessText to wrap indefinitely, modify your second ColumnDefinition from Auto to * and move the AccessText outside of the ScrollViewer as seen below...
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Whee a label:" Grid.Column="0"/>
<AccessText Grid.Column="1" TextWrapping="Wrap" Text="{Binding MyLongTextField}"/>
</Grid>
The reason the text would not wrap is because the second ColumnDefinition was set to Auto; which essentially does not force a bounds around the AccessText.
If you want to keep the ScrollViewer try this...
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Whee a label:" Grid.Column="0"/>
<ScrollViewer Grid.Column="1" VerticalScrollBarVisibility="Auto">
<AccessText TextWrapping="Wrap" Text="{Binding MyLongTextField}"/>
</ScrollViewer>
</Grid>
This ended up giving me what I wanted:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Whee a label:" Grid.Column="0"/>
<ScrollViewer Grid.Column="1" MaxHeight="40"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<AccessText Text="{Binding CRData.Error}" TextWrapping="Wrap"/>
</ScrollViewer>
</Grid>
Thanks to Meleak and Aaron for the suggestion of using * for the column width instead of Auto, and to Meleak for suggesting Disabled for the horizontal scrollbar instead of Auto.

Resources