Textblock.TextTrimming not working inside a grid - wpf

I have a 3 column grid for my layout with each of it width set to Width="*". For the middle (2nd) grid, I have another 3 column grid each containing it own textblock, and again the column grids width are set to Width="*".
When the Window is resized, the grids are resized as expected, however the 3rd textblock isn't getting trimmed if the text goes outside the boundary of the grid. I have textbox set with TextTrimming="WordEllipsis" and TextWrapping="Wrap", and the properties are not being enforced for some reason.
Here is some of the code I have:
Layout grid:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="150" MaxWidth="300" Width="1*" />
<ColumnDefinition Width="Auto"/>
<ColumnDefinition MinWidth="150" MaxWidth="500" Width="1*" />
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
</Grid>
2nd column code:
<Grid Grid.Column="2" VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="5" Width="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=FeedItems.Count}" Foreground="White" FontSize="18" Width="Auto" FontWeight="SemiBold" />
<TextBlock Text=" items from " Foreground="White" FontSize="18" Width="Auto" Grid.Column="1" />
<TextBlock Text="{Binding Path=Name}" Foreground="White" FontSize="18" Grid.Column="2" TextTrimming="CharacterEllipsis" HorizontalAlignment="Left" Width="Auto" TextWrapping="NoWrap" ClipToBounds="True" />
</Grid>

In order for this to work, you would need the last column in the second grid to have a * size, otherwise it will tell the TextBlock that it has as much space as it wants, even if it doesn't. Auto sized columns won't restrict the content to the bounds of a grid. However, you would probably get better results if you did this with a single TextBlock, and multiple Runs:
<TextBlock FontSize="18" TextTrimming="CharacterEllipsis">
<Run Text="{Binding Path=FeedItems.Count}" FontWeight="SemiBold" />
<Run Text=" items from " />
<Run Text="{Binding Path=Name}" />
</TextBlock>
Note that you can only bind Run.Text as of .NET 4.0. If you're using an older version of the framework, you'll have to create your own BindableRun, which is pretty simple as seen here.

Related

Lock element on the right

I have two textboxes in my app, with a variable lenght. One is on the left, the other one on the right. By default their width is pretty little, 30 px. But as they contain a lot of numbers, the textbox on the left shifts the right one and enlarges the window (same thing when the right textbw contains too many numbers). To avoid this, I would like to stabilize the right textbox on the right, even if its width rises.
I've tried to play with the columns size, but not.
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="170" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="1" MinWidth="30" Margin="0,0,0,0" Name="maxGmapWest" Text="{Binding Path=Options.MaxGmapWest, ElementName=Window, Mode=OneWay, Converter={StaticResource StringToDoubleConverter}}" PreviewTextInput="Tab1_PreviewTextInput" Width="auto" />
<TextBox Grid.Column="3" MinWidth="30" Margin="230,0,0,0" Name="maxGmapEast" Text="{Binding Path=Options.MaxGmapEast, ElementName=Window, Mode=OneWay, Converter={StaticResource StringToDoubleConverter}}" PreviewTextInput="Tab1_PreviewTextInput" Width="auto" HorizontalAlignment="Left" />
</Grid>
Try something like that markup
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0" Width="auto" MinWidth="50" VerticalScrollBarVisibility="Auto" Margin="0,0,0,0" HorizontalAlignment="Left" MaxHeight="40" >
<TextBox Name="maxGmapWest" TextWrapping="Wrap" />
</ScrollViewer>
<ScrollViewer Grid.Column="1" HorizontalAlignment="Right" VerticalScrollBarVisibility="Auto" MinWidth="50" Margin="0,0,20,0" MaxHeight="40">
<TextBox Name="maxGmapEast" TextWrapping="Wrap" />
</ScrollViewer>
</Grid>
maxwidth for textbox=width of column, fixed max height and using ScrollViewer for large text

Horizontal scroll bar prevents text box wrapping

I am trying to make TextBox to wrap text and grow with the Window. That works if I don't set ScrollViewer's HorizontalScrollBarVisibility property. But if I do, TextBox will grow infinitely. The problem is solved by setting MaxWidth property to the TextBox, but in that case TextBox does not grow beyond the MaxWidth value. My idea was then to bind MaxWidth to ColumnDefinition's ActualWidth, but that also doesn't work because ColumnDefinition's ActualWidth property is not a DependencyProperty. Here is my code:
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Name:" Margin="5" />
<TextBox MinWidth="200" Grid.Row="0" Grid.Column="1" TextWrapping="Wrap" AcceptsReturn="True" Margin="5"/>
</Grid>
</ScrollViewer>
Any ideas will be appreciated.
Try this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Name:" Margin="5" />
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Grid.Column="1">
<TextBox MinWidth="200" TextWrapping="Wrap" AcceptsReturn="True" Margin="5" MaxWidth="{Binding Path=ActualWidth, RelativeSource={RelativeSource AncestorType={x:Type ScrollViewer}}}" />
</ScrollViewer>
</Grid>

StackPanel expanding outside size of parent grid

I'm pretty new with WPF, so apologies if I'm missing something obvious. I have this template that is bound to items in an obs. collection. I'm trying to get it so that the 2nd column, the "test test..." part has a variable width that fills all the available space in the parent grid.
What I'm finding though, is that my code automatically shows all the text for that "test test..." text box as opposed to just binding to the available space in the grid, and instead creates the scroll bar that you see below.
I instead, want that "test test" to be cut off so that everything else fits so that no scroll bar appears (that when, if the user resizes the screen then that "test test..." textbox will automatically resize to fit the new space). Is there a way to do that?
My code for that template is as follows:
<DataTemplate x:Key="MainTemplate">
<Grid Margin="4" ClipToBounds="True">
<Grid.Resources>
<local:BooleanToHiddenVisibility x:Key="boolToVis"/>
</Grid.Resources>
<StackPanel Orientation="Vertical" ClipToBounds="True">
<Grid Width="Auto" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="60" />
<ColumnDefinition Width="40" />
<ColumnDefinition Width="60" />
<ColumnDefinition Width="40" />
<ColumnDefinition Width="60" />
<ColumnDefinition Width="40" />
<ColumnDefinition Width="40" />
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0" Name="checkViewTextBox"/>
<TextBlock Grid.Column="1" Text="{Binding OriginalQuote}" FontWeight="Bold" TextTrimming="WordEllipsis" FontStyle="Italic" ClipToBounds="True"/>
<TextBlock Margin="10,0,0,0" Grid.Column="2" Text="plane :" FontWeight="SemiBold" Width="60"/>
<TextBlock Margin="5,0,0,0" Grid.Column="3" Text="{Binding Mid}" Width="40"/>
<TextBlock Margin="10,0,0,0" Grid.Column="4" Text="data2 :" FontWeight="SemiBold" Width="60"/>
<TextBlock Margin="5,0,0,0" Grid.Column="5" Text="{Binding MidTwo}" Width="40"/>
<TextBlock Margin="10,0,0,0" Grid.Column="6" Text="data3:" FontWeight="SemiBold" Width="60"/>
<TextBlock Margin="5,0,0,0" Grid.Column="7" Text="{Binding MidThree}" Width="40"/>
<Button Margin="10,0,0,0" Content="History" Grid.Column="8" Click="History_Click" Width="40"/>
</Grid>
<StackPanel Orientation="Horizontal" Visibility="{Binding Path=IsChecked, ElementName=checkViewTextBox, Converter={StaticResource boolToVis}}">
<StackPanel.Resources>
<Style BasedOn="{StaticResource tbstyle}" TargetType="{x:Type TextBlock}" />
</StackPanel.Resources>
<!--Other stuff thats working ok-->
</StackPanel>
</Grid>
</DataTemplate>
Any help is much appreciated!
P.S. I've been adding random proprieties hoping one will work, so if looks like I have random things on there, that's probably why....
Well, not sure it will help you but some words about WPF layout.
It has two steps: measure and arrange.
At the first stage, the control tries to calculate its desired state. StackPanel asks its children about their desired sizes. It does not limit their size. (TextBlock with the binding to OriginalQoute has no explicitly set width!)
At the second stage, control is arranged.
Stack panel is allowed to occupy the whole left space of the column but it arranges its children as if its size was unlimited, so TextBlock shows the text completely.
The question is how to limit the size of the TextBlock?
Try binding
<TextBlock Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=StackPanel}, Path=ActualWidth}"/>

WPF layout for autosize textblock and icon floating on the right - how?

I am trying to get a layout where an icon floats on the right end of a textblock; the textblock grows/shrinks to content. I cannot make this happen without the textblock running outside the grid. For example:
<Grid x:Name="LayoutRoot" Width="500" HorizontalAlignment="Left" ShowGridLines="True" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="textBlock" VerticalAlignment="Top" Height="25" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" Grid.Column="0" >
<TextBlock.Text>longer keeps going and going testgrand you going and then t
</TextBlock.Text>
</TextBlock>
<Rectangle Fill="#FFDE3030" Stroke="Black" VerticalAlignment="Top" Height="41" Width="41" Grid.Column="1"/>
</Grid>
Seems like the natural approach and works fine when the text is shorter than the column/grid, except the textbox and column will grow indefinitely and not honor the bounds of the grid.
The inverse, with the icon on the left, works fine with a simpler layout, and the textblock doesn’t grow indefinitely. This is achieved with this markup:
<Grid Grid.Row="1" Width="500" HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Rectangle Fill="#FFDE3030" Stroke="Black" VerticalAlignment="Top" Height="41" Width="41" Grid.Column="0"/>
<TextBlock x:Name="textBlock2" VerticalAlignment="Top" Height="25" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" Grid.Column="1" HorizontalAlignment="Left">
<TextBlock.Text>longer testgrow the textblock and it will just keep growing but it will stop when it gets too </TextBlock.Text>
</TextBlock>
</Grid>
Any help appreciated. If a grid won’t work, is there an alternate layout where I can get the icon floating on the right of the text, and the textblock will trim text when it’s too long?
Also:
No, using * size columns doesn't work because the columns are fixed, and the icon won't float at the end of the text
A DockPanel doesn't work either, or at least I or others I've asked haven't been able to. The best it can do is to have the icon half-cut-off outside the dockpanel's right side.
Can you get what you want by setting MaxWidth on the TextBlock? If you add MaxWidth="460" to your first example:
<Grid x:Name="LayoutRoot" Width="500" HorizontalAlignment="Left" ShowGridLines="True" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<TextBlock MaxWidth="460" x:Name="textBlock" VerticalAlignment="Top" Height="25" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" Grid.Column="0" >
<TextBlock.Text>longer keeps going and going testgrand you going and then t</TextBlock.Text>
</TextBlock>
<Rectangle Fill="#FFDE3030" Stroke="Black" VerticalAlignment="Top" Height="41" Width="41" Grid.Column="1"/>
</Grid>
Then the TextBlock will grow horizontally and always have the rectangle immediately on its right. It won't be wider than 460, so the TextBlock plus the Rectangle shouldn't be wider than 500. If you need the Grid to resize dynamically then you can bind TextBlock.MaxWidth to Grid.ActualWidth with a converter that subtracts the width of the Rectangle.
Edit:
Actually, it should be even simpler than that. Use star sizing on the columns, but set MaxWidth instead of Width on the Grid. That way, the grid itself will get smaller when the text is smaller so that the rectangle is always at the edge of the text.
<Grid x:Name="LayoutRoot" MaxWidth="500" HorizontalAlignment="Left" ShowGridLines="True" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="textBlock" VerticalAlignment="Top" Height="25" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" Grid.Column="0" >
<TextBlock.Text>longer keeps going and going testgrand you going and then t</TextBlock.Text>
</TextBlock>
<Rectangle Fill="#FFDE3030" Stroke="Black" VerticalAlignment="Top" Height="41" Width="41" Grid.Column="1"/>
</Grid>
Someone internally suggested this answer, which works:
<WrapPanel HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions>
<AccessText TextTrimming="CharacterEllipsis" Grid.Column="0" Margin="0,0,4,0" Text="type more typingon the long hi longer than what if you keep tyingin and get to the end and that's why it changed because you were in the middle" />
<Border Grid.Column="1" Width="10" Height="10" Background="Red" />
</Grid>
</WrapPanel>
The wrappanel seems to provide the necessary magic. I haven't tried Quartermeister's but will save it for future reference!
Our final layout is more complicated and looks like this (it's the header bar for an expander):
<WrapPanel Orientation="Vertical">
<Grid x:Name="HeaderSite" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="19" />
<ColumnDefinition Width="16" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" /> <!-- 7/14: fix from list: wrap the whole thing in a wrappanel. Allows for one * col. -->
<ColumnDefinition Width="19" />
</Grid.ColumnDefinitions>
<ToggleButton x:Name="buttonExpanderToggleButton"
Height="20" VerticalAlignment="Top"
/>
<Image x:Name="imageActivityIcon" Grid.Column="1"
Height="16" Width="16"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="0"/>
<AccessText x:Name="textActivityID"
Grid.Column="2"
VerticalAlignment="Top" Margin="5,2,0,0"
TextTrimming="CharacterEllipsis"
FontSize="12" HorizontalAlignment="Left" Text="MA77777"/>
<AccessText x:Name="textActivityHeader"
Grid.Column="3"
VerticalAlignment="Top" Margin="0,2,0,0"
TextTrimming="CharacterEllipsis"
FontSize="12" HorizontalAlignment="Left" Text="Title title title title aand Title title title title a little and if you type more what happens as you keep typing "/>
<AccessText x:Name="textActivityStatus"
FontWeight="Normal"
FontStyle="Italic"
Grid.Column="4"
TextTrimming="CharacterEllipsis"
VerticalAlignment="Top" Margin="0,2,8,0"
FontSize="12" HorizontalAlignment="Left" Text="(On Hold)"/>
<Image x:Name="imageLink"
Stretch="None" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="5"/>
</Grid>
</WrapPanel>
This works fine too even with the other auto sized columns. The key seems to be the wrappanel and the one * sized column. If you set them all to auto it doesn't work.
I hope this and Quartermeister's answer helps somebody, because this drove me #$%#$% crazy.
The below code will result in the following output, is that what you are looking for???
longer keeps going and going... [red rectangle]
<Grid Width="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="longer keeps going and going testgrand you going and then t" TextTrimming="CharacterEllipsis"/>
<Rectangle Grid.Column="1" Fill="#FFDE3030" Stroke="Black" VerticalAlignment="Top" Height="41" Width="41" />
</Grid>
I had a somewhat similar problem; I wanted to show some content with an externally-sized border area but containing two TextBlocks, where the first is auto-sized and the second is fixed-sized, and the second floats left as the first gets smaller but stops at the right edge (so the first block's text is clipped instead of the second becoming invisible).
Distilling the previous answers, it appears that the key bit of magic is simply to use HorizontalAlignment="Left" with the first column set to star-sized.
<Border BorderThickness="1" BorderBrush="Black">
<Grid HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Value}" />
<TextBlock Grid.Column="1" Text="⏫" Margin="4,0,0,0" Foreground="Blue" />
</Grid>
</Border>
It appears that the way this works is that (a bit counter-intuitively) the Border stays full width (as set by its parent layout), while the Grid will size to its content -- except that it will not get wider than the containing Border. This keeps the second TextBlock visible.

WPF: Horizontal Alignment

Probably I'm just missing something obvious, but I can't get the image in my DataTemplate to align to the right in the Grid, so that when the window is stretched, the image is "pulled" to the right as well:
<Window.Resources>
<DataTemplate x:Key="PersonTemplate" DataType="Minimal.Client.Person">
<Border BorderBrush="Purple" BorderThickness="2" CornerRadius="2" Padding="5" Margin="5">
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="200"/>
<ColumnDefinition Width="Auto" MaxWidth="200"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column ="0" Orientation="Horizontal" >
<TextBlock FontFamily="Verdana" FontSize="16" FontWeight="Bold" Text="{Binding LastName}" />
<TextBlock FontFamily="Verdana" FontSize="16" Text=", " />
<TextBlock FontFamily="Verdana" FontSize="16" Text="{Binding FirstName}" />
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Vertical" HorizontalAlignment="Right">
<Border BorderBrush="Black" BorderThickness="1">
<Image Source="{Binding Picture}" Width="180" Height="150" />
</Border>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
Any suggestions?
I think the problem is that you have set a MaxWidth of 200 for the second column (where the Image is contained). Therefore, the column will not be any wider than 200 pixels and the two columns will not use the complete available space. If you insert another column in between the two columns and make this one star-sized, the Image will be right-aligned:
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="200"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" MaxWidth="200"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column ="0" Orientation="Horizontal" >
<TextBlock FontFamily="Verdana" FontSize="16" FontWeight="Bold" Text="{Binding LastName}" />
<TextBlock FontFamily="Verdana" FontSize="16" Text=", " />
<TextBlock FontFamily="Verdana" FontSize="16" Text="{Binding FirstName}" />
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Vertical" HorizontalAlignment="Right">
<Border BorderBrush="Black" BorderThickness="1">
<Image Source="{Binding Picture}" Width="180" Height="150" />
</Border>
</StackPanel>
</Grid>
This way, it works for me. However, you should be careful when using StackPanels. They always take as much space as they need. And if they are not given that much space, part of the content will simply be hidden.
gehho.
Try taking out the "MaxWidth" from your second column definition, and setting the Width to "*".
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
This keeeps the explicit settings from positioning your second column to the left.

Resources