WPF TextBox Wrapping - wpf

I am trying to figure out how to get a textbox to wrap its contents, however the situation isn't quite the same as the typical "it doesn't wrap" scenario. My textbox is contained inside a DataTemplate which is used inside a Telerik RadTabControl instance (using a ContentTemplatePresenter to determine which view to display) and the XAML for the DataTemplate looks like this:
<DataTemplate x:Key="NotesTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Use the box below to record any general notes associated with this item." Style="{StaticResource Default}" />
<TextBox TextWrapping="Wrap" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" GridRow="1" Margin="20" Text="{Binding Notes, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</DataTemplate>
The reason I say it doesn't fall under the normal "it doesn't wrap" scenario is it used to wrap until I had to change the view to be resizable to anything to support the varying screen sizes the app will be run on. When I did that the TextBox stopped wrapping because (presumably) as the user types something the TextBox says "I need more space" so the parent obliges and the box continues out to the right indefinitely (although the view gets scrollbars). I tried setting a MaxWidth using Binding/RelativeSource, but since the parent is specifically designed to grow that approach won't work. What I need to have happen is the box should be the width of its' containing parents' VisibleWidth. Meaning, if the Window itself is 1024x768, the TextBox's MaxWidth should be 1024 and any text thereafter would automatically wrap, but if the Window grows to 1280x1024 the box should now be 1280 and the text wrap accordingly. I tried this scenario with this binding expression, but no luck:
MaxWidth="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path=ActualWidth}"
The Window size itself isn't growing so if I could get the Window's Width (minus a certain amount to cover the width of the tabs that are part of the TabControl) I believe that would work.
Any ideas?

although the view gets scrollbars
Disable the horizontal scrollView, so it will be forced to wrap. You can try to disable it on the TextBox itself, or on the wrapping Grid.
<DataTemplate x:Key="NotesTemplate">
<Grid ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Use the box below to record any general notes associated with this item." Style="{StaticResource Default}" />
<TextBox TextWrapping="Wrap" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" Grid.Row="1" Margin="20" Text="{Binding Notes, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</DataTemplate>

Related

Listbox content not dynamically re-sizing when Listbox size changes

I've seen quite a few people asking this question and i feel like my question is slightly different. I have a listbox that holds a series of identical custom made user controls. These controls are added to the listbox at runtime. Now i currently have my listbox items resizing themselves properly upon first creation and insertion into the control.
Here is the strange part. If I resize the listbox the controls that have been visible previously are not resized to the new listbox width. In other words if i add 12 controls and the box only shows 4 (the rest are hidden by a scrollbar) then if i resize the box the first 4 controls will still be the original width and if i scroll then the other 8 will be the correct size. Also if i manipulate the list items in any way they resize themselves to the proper width automatically. SEE EDIT2
I've tried attaching to the sizeChanged event and issuing the following on both the listbox and the items but it has had no effect. I think i need to find some way of resetting the layout information for the listbox items but i can't find the command.
item.InvalidateArrange();
item.InvalidateMeasure();
Layers.UpdateLayout();
item.UpdateLayout();
I think this has something to do with the items i'm adding because even if i detach the items from the lisbox and then attach them they remain the wrong width.
Here is my listbox code:
<ListBox x:Name="Layers" VerticalContentAlignment="Top" Margin="0,17,0,0" BorderThickness="0,1,0,0" HorizontalContentAlignment="Stretch" SizeChanged="Layers_SizeChanged">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Here is the code for my items
<UserControl x:Class="ListOverlayItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:data="clr-namespace:Data"
HorizontalContentAlignment="Stretch">
<UserControl.Resources>
<data:Translator x:Key="translatorString" />
</UserControl.Resources>
<Border BorderBrush="Silver" BorderThickness="0,0,0,0" Name="border1" HorizontalAlignment="Stretch">
<Grid x:Name="layout" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="19"/>
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<!-- Column 0 -->
<ScrollBar Name="arrangeIcon" FlowDirection="LeftToRight" Maximum="10" SmallChange="1" Value="5" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2"/>
<!-- Column 1 -->
<Slider Name="OverlayOpacity" Height="25" Grid.Column="1" Margin="6,0,0,0" Grid.Row="1" Maximum="1" LargeChange="0.1" ValueChanged="OverlayOpacity_ValueChanged"/>
<TextBlock x:Name="OverlayName" Text="{Binding Path=LocationName}" Foreground="#FFF08D2D" Margin="10,2,0,0" Grid.Column="1" FontSize="12" Height="18" VerticalAlignment="Top" />
<!-- Column 3 -->
<Button Name="SettingsButton" Grid.Column="3" Content="{Binding TRK_OV_Settings, Source={StaticResource translatorString}}" VerticalAlignment="Center" Click="SettingsButton_Click" />
<CheckBox x:Name="OverlayEnabled" FlowDirection="LeftToRight" Grid.Column="2" DataContext="{Binding}" HorizontalAlignment="Right" VerticalAlignment="Center" Grid.RowSpan="2" Checked="OverlayEnabled_Checked" Unchecked="OverlayEnabled_Unchecked" />
<TextBlock Name="percentage" Text="100%" FontSize="9" TextAlignment="Right" Grid.Column="2" Grid.Row="1" VerticalAlignment="Center" Margin="2,6,26,6" MinWidth="30"/>
</Grid>
</Border>
Again it seems like my UserControl is correctly able to scale itself its just not getting the command to do so when the parent container is resized.
EDIT: Whoops, removed the WPF tag because it was incorrectly added. I think i've got it down to slider being the culprit. If i set the slider to a fixed size instead of 'stretch' then the item correctly scales just fine. So the question now is how do i force the slider to resize itself.
EDIT2: I know what is causing the issue but i don't know how to solve it. What is happening is the slider that i have in my user control will NOT resize along with the rest of the control unless i change the value of the slider during the resize. The instant i change its value even a fraction it resizes itself automatically. How can i force it to resize?
I determined that the slider was causing the issue and i tried many ways to force the slider to re-draw when it needed to but i was unsuccessful. My final solution was to remove the slider entirely. Works fine now.

ListBox in WPF page looks fine on my PC, but the data shrinks on smaller resolutions

I've tried almost every combination of alignments and margins, to no success. The ListBox is on a WPF page, which is bound to a frame on the main window.
The ListBox itself is fine. It aligns just as I expect it would. My ListBoxItem contains an Image and a TextBlock. The TextBlock gets cut off.
As you can see from the following image, things are mostly good.
The ListBox is appropriately offset from the left edge and the top of that blue box. The content Image and TextBlock are fairly centered and so is the ListBoxItem outline. This is on my development machine.
I thought the whole reason for using grids, grid lines, and alignment properties, was so we didn't have to set a hard coded size. That our controls would resize themselves. Which does actually work just fine for everything else.
But when I place this on a small screen, I get this:
The ListBox itself is still aligned correctly. But now the ListBoxItem is forced down. It's top alignment is still good, but the bottom is pushed down so we can't see the TextBlock or the bottom of the ListBoxItem.
Here's my XAML:
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1">
<Grid.RowDefinitions>
<RowDefinition Height="292*" />
<RowDefinition Height="30*"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="1" Name="lbButtons" Margin="2,0,0,1.5" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Visibility="Visible" >
<StackPanel Name="spListBoxItems" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Horizontal">
<ListBoxItem Margin="0,0,0,0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Margin="10,5,10,5">
<Image Source="/COBAN.CobanConsole.UI;component/Images/Buttons/Light/record48_light.png" />
<TextBlock Text="Record" Margin="5,5,0,0"></TextBlock>
</StackPanel>
</ListBoxItem>
</StackPanel>
</ListBox>
</Grid>
Not much to it. Any ideas on what's going on?
You're using * units on your grid row definitions. Those are relative measures: they just tell you what proportion of the space the rows can take up. If you need your listbox to have a specific height, you should change your second row to have an absolute size instead, like this:
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="30"/>
</Grid.RowDefinitions>

WPF UserControl - Margins Misbehaving

I'm fairly new to WPF custom controls and have started by playing with a basic "LabelEdit" - basically a Label control and a TextBox. I have binding for 4 properties - Text, Label, TextWidth and LabelWidth (perhaps not what you would name them in a production environment, but this is just so that I can educate myself!).
All seems to work well. I also have an event which fires when the size of the label changes and causes an "ActualLabelWidth" DependencyProperty to change, so that a series of LabelEdit controls can all have the same Label Width. Here's the XAML for the LabelEdit:
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<Grid.RowDefinitions >
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding LabelWidth}" />
<ColumnDefinition Width="{Binding TextWidth}" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="{Binding Label, FallbackValue=LabelEdit}" SizeChanged="Label_SizeChanged" />
<TextBox Grid.Column="1" Grid.Row="0" Text="{Binding Text}" />
</Grid>
...and for the MainWindow using it:
<ajdata:LabelEdit Text="{Binding Title}" Label="Title:" LabelWidth="{Binding ElementName=lblForename, Path=ActualLabelWidth}" TextWidth="100" />
<ajdata:LabelEdit Text="{Binding Surname}" Label="Surname:" LabelWidth="{Binding ElementName=lblForename, Path=ActualLabelWidth}" TextWidth="300" />
<ajdata:LabelEdit Text="{Binding Forename}" Label="Forename(s):" LabelWidth="Auto" TextWidth="300" Name="lblForename" />
So the label with the largest piece of text sets the width for the others.
The problem occurs when I give the Label a margin ("0,0,5,0") to space it from the TextBox element. In this situation, the LabelEdit with the "Auto" width seems to work fine. The bound versions, however, appear not to honour the margins. This means the TextBox part of the element appears positioned left of where it should be.
Does anyone know what I need to do so that all the Labels end up the same width with margins taken in to account? I realise I could probably insert an extra piece of code in my event handler, but would rather make the XAML do its job, if possible. Many thanks.
I have now solved this (but am still perhaps unsure why). Instead of setting margins of "0,0,5,0" on the label, I have set margins of "5,0,0,0" on the TextBox. This way, everything remains nicely lined up, whatever the label width.

Expander, Grid & ListBox = No Virtualization

I have a ListBox, inside a Grid, inside an Expander. The ListBox is bound to an IList.
When I expand the Expander control for the first time, the ListBox processes all of the items in the IList (which can be thousands) instead of only processing the items that would be visible on the screen.
If however I fix the height of the ListBox control, it behaves as expected and only accesses those items in the IList that will be visible.
Effectively, the Virtualization is not working, though I believe that this is more related to the ListBox not being able to determine a height when the content items are being prepared.
The XAML is basically as follows (some stuff removed for simplification)...
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Expander ExpandDirection="Right"
Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
Header="Documents"
IsExpanded="False">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
</Grid.RowDefinitions>
</Grid>
<ListBox Name="listBox"
Grid.Row="1"
ItemsSource="{Binding Path=Items}"
SelectedIndex="{Binding Path=SelectedIndex}"
SelectedItem="{Binding Path=SelectedItem}"
SelectionMode="Single"
Width="250">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
Style="{StaticResource prompt}">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}{1:00000}">
<Binding Path="..."
FallbackValue="0" />
<Binding Path="..." />
</MultiBinding>
</TextBlock.Text></TextBlock>
<TextBlock Grid.Column="1"
Style="{StaticResource prompt}">
<TextBlock.Text>
<Binding Path="ItemCount"
StringFormat="{}{0} Items"
FallbackValue="" />
</TextBlock.Text></TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Expander>
<v:DocumentView x:Name="documentView"
Grid.Column="1"
Grid.Row="0"
DocumentID="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type v:BatchView}}, Path=ViewModel.SelectedItem.ID}"
IsActive="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type v:BatchView}}, Path=IsActive}" />
<StackPanel Grid.Column="1"
Grid.Row="1"
Style="{StaticResource buttonStackStyle}">
<Button Command="{Binding Path=PreviousCommand}"
Style="{StaticResource previousButtonStyle}" />
<Button Command="{Binding Path=NextCommand}"
Style="{StaticResource nextButtonStyle}" />
</StackPanel>
</Grid>
Can anybody suggest how I might set the Height of the ListBox to the ActualHeight of the Grid.Row parent? Alternatively, can anybody provide a better solution?
Thanks.
Short version: remove Grid.RowSpan from the expander.
Long version:
Background: (in really broad strokes)
When you define the height of RowDefinition three things can happen, depending what type of units you end up using:
Pixel - any UIElement that is placed in that row will have the defined row height passed to the Measure and Arrange methods of the element.
Auto - the grid will pass infinity as the height for Measure and then element.DesiredSize.Height for Arrange.
Star - the grid will consider the heights of all rows with units of pixels and auto; calculate the height that is left from its available height and divide it by the "total of stars" that were defined for all the rows - this is the height of one star; then, each row height is assigned depending on the multiplier for its star definition; this height is passed to the Measure and Arrange methods.
Same logic is applied to column definitions only regarding width instead of height.
So, star definition is "stopping" the element, pixel definition is also "stopping" but it can be outside of the rendered view and auto definition is "letting" the element to be what ever size it wants to be.
All this logic is recursive so you need to think in two directions (explanation below).
In your case
In one direction. The ListBox is in a star row so it'll be stopped. The parent grid is also stopped (since the template for expander uses DockPanel that is also a "stopping panel"). The expander is defined to begin in a star row but it spans to an auto row - this means that it will be allowed to grow in height 'till infinity. Oops...time to reverse.
Now the reverse direction. The expander is not stopped, the child grid is not stopped (since the grid assumes it has infinite height available), thus the list box is not stopped, the ScrollViewer in the template of list box is not stopped so it's ViewportHeight is infinite, for the VirtualizingStackPanel that arranges the items (and is a child of the scroll viewer) this means all items are in the view == render all elements.
For a WPF window with default template, you can always assume the window is stopping its child element. So if removing the row span definition has not resolved the issue, continue traversing up until you find another element that is not stopping its child height and change its definitions or change the panel to stop the height from growing to infinity (scroll viewers are notorious for creating these behaviours, especially the ones that are hidden in templates).

WPF Grid Column MaxWidth not enforced

This problem stems from not being able to get my TextBlock to wrap. Basically as a last-ditch attempt I am setting MaxWidth on my container grid's columns. I was surprised to find that my child label and textbox still do whatever they want (bad children, BAD) and are not limited by my grid column's MaxWidth="200".
What I'm really trying to do is let my TextBlock fill available width and wrap if necessary. So far after trying many variations of HorizontalAlignment="Stretch" on every known parent in the universe, nothing works, except setting an explicit MaxWidth="400" or whatever number on the TextBlock. This is not good because I need the TextBlock to fill available width, not be limited by some fixed number. Thanks!
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="200" SharedSizeGroup="A" />
<ColumnDefinition MaxWidth="200" SharedSizeGroup="B" />
</Grid.ColumnDefinitions>
<Label VerticalAlignment="Top" Margin="0 5 0 0" Grid.Column="0" Style="{StaticResource LabelStyle}" Width="Auto" Content="{Binding Value.Summary}" />
<TextBlock Grid.Column="1" Margin="5,8,5,8" FontWeight="Normal"
Background="AliceBlue"
Foreground="Black" Text="{Binding Value.Description}"
HorizontalAlignment="Stretch"
TextWrapping="Wrap" Height="Auto" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I've tried to replicate your problem by pasting everything between your Grid elements in to Kaxaml but everything wraps as you would expect it to. (I inserted regular strings where you were doing bindings and removed the Label style).
It could be that the problem is higher up the tree.
I'd suggest pasting chunks in to Kaxaml or similar to test and see which parent breaks your UI.
I provided an answer to this question, only it was using an ListView instead of an ItemsControl but the issue is likely the same. There is probably a ScrollViewer surrounding your ItemPresenter and you need to edit a copy of the ItemsControl template.
WPF ListView TextBlock TextWrapping

Resources