WPF Scrollviewer not working with dynamic height - wpf

Trying to add a Scrollviewer to a TextBlock so that users can scroll down the contents, which are often rather longer than the available screen real-estate.
Apologies for what's probably a dumb question: I can see there's lots of topics about this, and that the problem is usually a fixed height somewhere, but I'm struggling to see which element is causing the problem in my XAML:
<Popup StaysOpen="True" Placement="Center" IsOpen="{Binding SummaryOpen}" PlacementTarget="{Binding ElementName=Areas}">
<Border Background="LightGray" BorderBrush="Black" Padding="5" BorderThickness="1">
<Grid Width="500">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="350" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<Label Content="{Binding Name}" />
<Label Content=": " />
<Label Content="{Binding Description}" />
</StackPanel>
<Border Grid.Row="1" BorderBrush="Black" BorderThickness="1">
<StackPanel Background="White" Margin="-1,1,1,-1">
<!-- this is the rogue element -->
<ScrollViewer VerticalScrollBarVisibility="Auto">
<TextBlock Text="{Binding Summary}" TextWrapping="Wrap" />
</ScrollViewer>
</StackPanel>
</Border>
</Grid>
</Border>
</Popup>
The ScrollViewer appears, but never contains an actual scroll bar, regardless of how much content there is in the TextBlock.
If someone could explain where the problem is and how you fix it, I'd be most appreciative.

The rogue element is actually the parent StackPanel -- that panel isn't "fixed height" per se, but it doesn't work as a parent of a ScrollViewer. The reason is that it reports its available height as infinite, so the child ScrollViewer thinks it can extend as far as its children require, and so it doesn't need to scroll.
It looks like you could just as easily use a Border, or a Grid, either of which will limit their height to the parent height and thus fix the issue:
<Border Grid.Row="1" BorderBrush="Black" BorderThickness="1">
<Border Background="White" Margin="-1,1,1,-1">
<!-- this is the rogue element -->
<ScrollViewer VerticalScrollBarVisibility="Auto">
<TextBlock Text="{Binding Summary}" TextWrapping="Wrap" />
</ScrollViewer>
</Border>
</Border>

Related

XAML/WPF - ScrollViewer which has StackPanel inside is not scrolling

I have the following StackPanel inside a ScrollViewer that shows User Control elements Whenever a specific event occurs:
Note: many UserControls might appear in the StackPanel that's why I added a Scrollviewer
<ScrollViewer
VerticalScrollBarVisibility="Auto"
Grid.Row="2"
CanContentScroll="True"
Grid.ColumnSpan="2">
<StackPanel Orientation="Vertical">
<ItemsControl ItemsSource="{Binding UserControls}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<views:UserControl/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
Although, the StackPanel is still going out of range and the scroll bars doesn't show and doesn't work!
I tried fixing the height of both the StackPanel and the ItemsControl but it does't seem to work either...
Window Layout containing the ScrollViewer:
<Grid Margin="0,15,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label
Content="This is a Label"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="5,5,0,0"
FontSize="15"
Grid.Row="0" Grid.ColumnSpan="2">
</Label>
<StackPanel Grid.Row="1" Orientation="Horizontal" Grid.ColumnSpan="2">
<ComboBox
ItemsSource="{Binding Something}"
Text="Confirm with..."
SelectedItem="{Binding Something}"/>
<Button
HorizontalAlignment="Left"
Margin="5"
Content="Add new UserControl"
Command="{Binding Path=AddUserControl}"/>
</StackPanel>
<ScrollViewer
VerticalScrollBarVisibility="Auto"
Grid.Row="2"
CanContentScroll="True"
HorizontalScrollBarVisibility="Auto">
<StackPanel Orientation="Vertical">
<ItemsControl ItemsSource="{Binding UserControls}" Height="300">
<ItemsControl.ItemTemplate>
<DataTemplate>
<views:UserControl/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</Grid>
Here's my UserControl that is added to the StackPanel Inside the ScrollViewer:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel
Orientation="Horizontal"
Grid.Row="0">
<Button
Name="DeleteFilter"
HorizontalAlignment="Left"
Margin="5"
Content="-"/>
<ComboBox
Margin="5"
IsEditable="False"
IsReadOnly="True"
Width="150"
ItemsSource="{Binding SomeObject}"
DisplayMemberPath="Name"
SelectedItem="{Binding SomeObjectProperty}"/>
<ComboBox
Margin="5"
IsEditable="False"
IsReadOnly="True"
Width="150"
ItemsSource="{Binding AnotherObject}"
DisplayMemberPath="Name"
SelectedItem="{Binding AnotherObjectProperty}"/>
<TextBox
x:Name="Value"
Text="{Binding TextBoxValueString}"
TextAlignment="Center"
Width="100"
Margin="5"
Visibility="{Binding TextBoxVisibility}"/>
</StackPanel>
</Grid>
I'm new to XAML and WPF.
Any Suggestions?
ScrollViewers and StackPanel don't work well together. This is because a StackPanel measures its child elements with infinite horizontal space if its Orientation property is set to Horizontal and infinite vertical space if it is set to Vertical. Please refer to my answer here for more information:
How to scroll the datagrid in stackpanel?
So you should replace the StackPanel with another Panel or remove it altogether:
<ScrollViewer
VerticalScrollBarVisibility="Auto"
Grid.Row="2"
CanContentScroll="True"
HorizontalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding UserControls}" Height="300">
<ItemsControl.ItemTemplate>
<DataTemplate>
<views:UserControl/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
I was able to get it run with this setting:
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"
Grid.RowSpan="10">
<StackPanel Orientation="Vertical"
Grid.RowSpan="6"
Name="SPanel"
Margin="0,0,-0.4,1.4"
CanVerticallyScroll="True">
<Border BorderBrush="Black"
BorderThickness="1"
Margin="3"
Grid.Row="0"
Name="ChartHolder0">
<dvc:Chart Cursor="Cross"
Background="#FFFFFCF2" />
</Border>
<Border BorderBrush="Black"
BorderThickness="1"
Margin="3"
Grid.Row="0"
Name="ChartHolder0">
<dvc:Chart Cursor="Cross"
Background="#FFFFFCF2" />
</Border>
<Border BorderBrush="Black"
BorderThickness="1"
Margin="3"
Grid.Row="0"
Name="ChartHolder0">
<dvc:Chart Cursor="Cross"
Background="#FFFFFCF2">
</dvc:Chart>
</Border>
</StackPanel>
</ScrollViewer>
And this is what I get:
Was looking for a fix for this problem and Mishka's answer worked for me.
I don't have enough reputation to comment on an answer, but wanted to say that Background="White" fix from Mishka does work for me on Silverlight (didn't try WPF).
<ScrollViewer Background="White">
<StackPanel Margin="5" Background="White">
Works, if I only put Background on the StackPanel the 5 pixel Margin on the stackpanel doesn't scroll. If I don't put Background on either then both the 5 pixel margin and any margins on controls inside the stackpanel dont scroll.
You are missing a Background for either the StackPanel or ItemsControl(Your choise).
Default Background is Null.
With Background Null, the ScrollViewer doesn't get mouse events for the mouse wheel,
and doesn't scroll.

Issue with WPF WrapPanel inside a ScrollViewer

There are a number of similar questions on SO but so far I have not been able to resolve my problem using them.
I have a bunch of Controls inside a WrapPanel and the WrapPanel is inside a ScrollViewer. The ScrollViewer is inside a Grid.
I am trying to get all the <Border> controls in the WrapPanel to have an Orientation of 'Vertical' (so that they flow down and when there is no more space left vertically they wrap horizontally) with a HorizontalScrollBar that appears when there is no more space left Horizontally.
My code so far is as follows:
<Grid x:Name="configGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto" Width="{Binding ElementName=configGrid, Path=ActualWidth}" Height="{Binding ElementName=configGrid, Path=ActualHeight}">
<WrapPanel HorizontalAlignment="Left" Orientation="Vertical" x:Name="ConfigWrapPanel" Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ScrollViewer}}, Path=ActualWidth}">
<Border BorderBrush="White" BorderThickness="1,1,1,1" CornerRadius="2" Margin="10">
<Expander IsExpanded="True" BorderThickness="0" Header="General">
// some controls here
</Expander>
</Border>
<Border BorderBrush="White" BorderThickness="1,1,1,1" CornerRadius="2" Margin="10">
<Expander IsExpanded="True" BorderThickness="0" Header="Another Block">
// some controls here
</Expander>
</Border>
// many more <border> blocks here....
</WrapPanel>
</ScrollViewer>
</Grid>
This almost works as expected, the various content flows vertically and when there is not enough room at the bottom it moves up and right and starts at the top again. But I never get any horizontal scrollbars and the controls just disappear off the right of the screen.
I'm sure this is something really simple I'm missing but I can't quite figure it out.
As a bit of further info, the various Border controls and sub elements are all of dynamic width and height (which is why I opted for a vertical orientation WrapPanel rather than Horizontal)
Any help would be greatly appreciated.
You have to remove the Width from your WrapPanel.
That width wants to stretch to infinity, which prevents the ScrollViewer from corrent measuring the boundaries of the WrapPanel resulting in never showing the ScrollBar.
Code below shows a working example:
<Grid x:Name="configGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto" Height="{Binding ElementName=configGrid, Path=ActualHeight}">
<WrapPanel HorizontalAlignment="Left" Orientation="Vertical" x:Name="ConfigWrapPanel" >
<Border BorderBrush="White" BorderThickness="1,1,1,1" CornerRadius="2" Margin="10">
<Expander IsExpanded="True" BorderThickness="0" Header="General">
// some controls here
</Expander>
</Border>
<Border BorderBrush="White" BorderThickness="1,1,1,1" CornerRadius="2" Margin="10">
<Expander IsExpanded="True" BorderThickness="0" Header="Another Block">
// some controls here
</Expander>
</Border>
</WrapPanel>
</ScrollViewer>
</Grid>

Why are letters scaling to different sizes in comparable view boxes within WPF?

I am attempting to create an onscreen keyboard using a Grid for key layout. Each key consists of a Border with a TextBlock containing a letter. To make the letters scale I have wrapped each TextBlock in a ViewBox, for example;
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="0" Grid.Column="0">
<Border BorderThickness="1" BorderBrush="Gray">
<Viewbox>
<TextBlock Text="a" />
</Viewbox>
</Border>
</Grid>
<Grid Grid.Row="0" Grid.Column="1">
<Border BorderThickness="1" BorderBrush="Gray">
<Viewbox>
<TextBlock Text="b" />
</Viewbox>
</Border>
</Grid>
<Grid Grid.Row="0" Grid.Column="2">
<Border BorderThickness="1" BorderBrush="Gray">
<Viewbox>
<TextBlock Text="c" />
</Viewbox>
</Border>
</Grid>
</Grid>
The problem is when you shrink the control by resizing the window horizontally (i.e. squash the borders together horizontally). As each letter has a slightly different width and height, the amount of zooming/scaling applied by each viewbox is not exactly the same. This results in the letters being rendered at different vertical heights, i.e. the "b" will be on a horizontal plane above the "a" and "c", which looks a little wrong.
The only work around I can think of (which works) relies on fixing the widths of each textblock, e.g. setting Width="10". This, however, feels unsatisfactory as it requires knowledge of the font which will be used to display each letter and an assumption about the maximum width. A middle ground would be to achieve this automatically the largest possible letter/glyph in each viewbox by including a hidden letter in each textblock;
<Grid Grid.Row="0" Grid.Column="0">
<Border BorderThickness="1" BorderBrush="Gray">
<Viewbox>
<Grid>
<TextBlock Text="a" />
<TextBlock Text="X" Visibility="Hidden" />
</Grid>
</Viewbox>
</Border>
</Grid>
I don't like that solution though and would love a reliable way to ensure all textblocks are the same size and so scale acceptably, without hard coding values or assumptions about the font.
Any ideas?
Thanks.
Using a uniform grid and binding to a primary textblock you get a pretty decent scaling:
<UniformGrid Rows="3" Columns="10">
<Viewbox>
<Border>
<TextBlock TextAlignment="Center"
Width="{Binding ActualWidth, ElementName=textBlock, Mode=OneWay}"
Height="{Binding ActualHeight, ElementName=textBlock, Mode=OneWay}">
Q
</TextBlock>
</Border>
</Viewbox>
<Viewbox>
<Border>
<TextBlock x:Name="textBlock" TextAlignment="Center">
W
</TextBlock>
</Border>
</Viewbox>
<Viewbox>
<Border>
<TextBlock Width="{Binding ActualWidth, ElementName=textBlock, Mode=OneWay}"
Height="{Binding ActualHeight, ElementName=textBlock, Mode=OneWay}"
TextAlignment="Center">
E
</TextBlock>
</Border>
</Viewbox>
<Viewbox>
<Border>
<TextBlock Width="{Binding ActualWidth, ElementName=textBlock, Mode=OneWay}"
Height="{Binding ActualHeight, ElementName=textBlock, Mode=OneWay}"
TextAlignment="Center">
R
</TextBlock>
</Border>
</Viewbox>
</UniformGrid>
You'll see each control is bound to the W key - assuming that is the biggest. If you are unsure, you can add a different element as hidden and bind to that - as you implied in your question. The important thing is that the grid sets the size of the main control element.
This MSDN question is answered correctly; http://social.msdn.microsoft.com/Forums/vstudio/en-US/c052fa89-4788-4d85-b266-fdd5c637a0ff/sharing-viewbox-zoom-level-between-items?forum=wpf
The solution relies on leveraging the SharedSizeGroup behaviour on a grid to ensure that the viewbox of every key is the same size as every other viewbox, like so;
<Viewbox>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="col"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="row"/>
</Grid.RowDefinitions>
<TextBlock Text="a" />
</Grid>
</Viewbox>
Other solutions involving hard coding the width/height of the viewbox, or binding to a common viewbox and filling it with the largest possible glyph work, but are not perfect solutions. The above solution makes no assumptions and relies on built in WPF measure/arrange logic to produce the desired outcome.

Stopping WPF TextBox from growing with text

I am trying to modify my simple control so that the text box does not grow as the user types in long text. I took a look at some solutions posted here at Stackoverflow that suggest using a Grid and an invisible Border and binding the Width of the text box to the ActualWidth of the Border, but I can't seem to get it to work in my setup.
Here is the xaml of my control:
<StackPanel Margin="5,0">
<WrapPanel Margin="0,0,0,5">
<TextBlock Foreground="White" Margin="0,0,2,0">TEXT</TextBlock>
<TextBlock Foreground="#FF0099CC" FontWeight="Bold">MORE TEXT</TextBlock>
</WrapPanel>
<Border Margin="2,4,0,4" BorderThickness="1" SnapsToDevicePixels="True" Background="Black"
BorderBrush="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}">
<StackPanel Orientation="Horizontal">
<Image Source="..\Resources\zoom.png" Width="13"/>
<TextBox Foreground="White" Background="Black" BorderBrush="Transparent">THIS IS SOME TEXT</TextBox>
</StackPanel>
</Border>
</StackPanel>
Any ideas? I need the zoom.png to appear "inside" of the text box so I use a horizontal stack panel and just place the image and text box side by side, both surrounded by the same border.
Is there a way for me to stop my text box from auto growing as text is typed?
Thanks.
UPDATE:
Below is the xaml I am testing with.
<Window x:Class="Desktop.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:composite="http://www.codeplex.com/CompositeWPF"
Title="MyShell" Height="50" Width="900"
WindowStyle="None"
ShowInTaskbar="False"
AllowsTransparency="True"
Background="Transparent"
ResizeMode="CanResizeWithGrip"
WindowStartupLocation="CenterScreen">
<Border BorderBrush="Black"
BorderThickness="1.5"
CornerRadius="5"
Background="Gray">
<ItemsControl composite:RegionManager.RegionName="MainRegion">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel></WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Border>
</Window>
<UserControl x:Class="Desktop.SearchTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="50" Margin="0">
<StackPanel Margin="5,0">
<WrapPanel Margin="0,0,0,5">
<TextBlock Foreground="White" Margin="0,0,2,0">TEXT</TextBlock>
<TextBlock Foreground="#FF0099CC" FontWeight="Bold">MORE TEXT</TextBlock>
</WrapPanel>
<Border Margin="2,4,0,4" BorderThickness="1" SnapsToDevicePixels="True" Background="Black"
BorderBrush="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}">
<Grid x:Name="grdTest">
<Image HorizontalAlignment="Left" Source="..\Resources\zoom.png" Width="13"/>
<TextBox Margin="16,0,0,0" Foreground="White" Background="Black" BorderBrush="Transparent" Width="{Binding ElementName=grdTest, Path=ActualWidth}">THIS IS SOME TEXT</TextBox>
</Grid>
</Border>
</StackPanel>
</UserControl>
I just add my user control to the Window's MainRegion.
I did some more searching and found the solution. I used the below Grid to replace the grid in my original post and now the text box keeps its original size and does not auto grow on long user input. Thanks to all who looked into this.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Image Source="..\Resources\zoom.png" Width="13"/>
<Border x:Name="b" Grid.Column="1"/>
<TextBox Width="{Binding ActualWidth, ElementName=b}" Foreground="White" Background="Black" BorderBrush="Transparent"
Grid.Column="1"
VerticalAlignment="Center"
Text="THIS IS SOME TEXT"/>
</Grid>
Try to put ScrollViewer.HorizontalScrollBarVisibility="Disabled" inside Grid.
Name the Grid as x:Name="grdTest" and try this
<Grid x:Name="grdTest">
<Image HorizontalAlignment="Left" Source="..\Resources\zoom.png" Width="13"/>
<TextBox Margin="16,0,0,0" Foreground="White" Background="Black" BorderBrush="Transparent"
Width="{Binding ElementName=grdTest, Path=ActualWidth}">THIS IS SOME TEXT</TextBox>
</Grid>
To stop the grow behavior set an explicit size to the TextBox or place it in a grid with the HorizontalAlignment set to stretch
Option 1:
<TextBox Width="60">THIS IS SOME TEXT</TextBox>
Option 2:
<TextBox HorizontalAlignment="Stretch">THIS IS SOME TEXT</TextBox>
I had a very similar issue, and it turned out that my Window property "SizeToContent" was set to "WidthAndHeight", I removed setting that property, (which then defaults to "Manual") and this fixed my similar issue.
To scale the border containing the textbox to the width of the outter stack panel, change the inner stack panel to a grid and set a left margin on the text box so it doesn't overlap with the image. Also set the image's horizontal alignment to left (if that is where you want it) or it will default to the center of the border.
<StackPanel Margin="5,0">
<WrapPanel Margin="0,0,0,5">
<TextBlock Foreground="White" Margin="0,0,2,0">TEXT</TextBlock>
<TextBlock Foreground="#FF0099CC" FontWeight="Bold">MORE TEXT</TextBlock>
</WrapPanel>
<Border Margin="2,4,0,4" BorderThickness="1" SnapsToDevicePixels="True" Background="Black"
BorderBrush="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}">
<Grid>
<Image HorizontalAlignment="Left" Source="..\Resources\zoom.png" Width="13"/>
<TextBox Margin="16,0,0,0" Foreground="White" Background="Black" BorderBrush="Transparent">THIS IS SOME TEXT</TextBox>
</Grid>
</Border>
</StackPanel>
This article shows a solution for a textbox that is displayed inside a scrollviewer's viewport (in treeview or listbox). A PART_MeasureTextBlock is used (bound to the textbox) to determine the available size and set it to the textbox.
Please have look here and tell me what you think about it:
http://www.codeproject.com/Articles/802385/A-WPF-MVVM-In-Place-Edit-TextBox-Control

How to get StackPanel's children to fill maximum space downward?

I simply want flowing text on the left, and a help box on the right.
The help box should extend all the way to the bottom.
If you take out the outer StackPanel below it works great.
But for reasons of layout (I'm inserting UserControls dynamically) I need to have the wrapping StackPanel.
How do I get the GroupBox to extend down to the bottom of the StackPanel, as you can see I've tried:
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch"
Height="Auto"
XAML:
<Window x:Class="TestDynamic033.Test3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test3" Height="300" Width="600">
<StackPanel
VerticalAlignment="Stretch"
Height="Auto">
<DockPanel
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Height="Auto"
Margin="10">
<GroupBox
DockPanel.Dock="Right"
Header="Help"
Width="100"
Background="Beige"
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch"
Height="Auto">
<TextBlock Text="This is the help that is available on the news screen." TextWrapping="Wrap" />
</GroupBox>
<StackPanel DockPanel.Dock="Left" Margin="10" Width="Auto" HorizontalAlignment="Stretch">
<TextBlock Text="Here is the news that should wrap around." TextWrapping="Wrap"/>
</StackPanel>
</DockPanel>
</StackPanel>
</Window>
Answer:
Thanks Mark, using DockPanel instead of StackPanel cleared it up. In general, I find myself using DockPanel more and more now for WPF layouting, here's the fixed XAML:
<Window x:Class="TestDynamic033.Test3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test3" Height="300" Width="600" MinWidth="500" MinHeight="200">
<DockPanel
VerticalAlignment="Stretch"
Height="Auto">
<DockPanel
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Height="Auto"
MinWidth="400"
Margin="10">
<GroupBox
DockPanel.Dock="Right"
Header="Help"
Width="100"
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch"
Height="Auto">
<Border CornerRadius="3" Background="Beige">
<TextBlock Text="This is the help that is available on the news screen." TextWrapping="Wrap"
Padding="5"/>
</Border>
</GroupBox>
<StackPanel DockPanel.Dock="Left" Margin="10" Width="Auto" HorizontalAlignment="Stretch">
<TextBlock Text="Here is the news that should wrap around." TextWrapping="Wrap"/>
</StackPanel>
</DockPanel>
</DockPanel>
</Window>
It sounds like you want a StackPanel where the final element uses up all the remaining space. But why not use a DockPanel? Decorate the other elements in the DockPanel with DockPanel.Dock="Top", and then your help control can fill the remaining space.
XAML:
<DockPanel Width="200" Height="200" Background="PowderBlue">
<TextBlock DockPanel.Dock="Top">Something</TextBlock>
<TextBlock DockPanel.Dock="Top">Something else</TextBlock>
<DockPanel
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Height="Auto"
Margin="10">
<GroupBox
DockPanel.Dock="Right"
Header="Help"
Width="100"
Background="Beige"
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch"
Height="Auto">
<TextBlock Text="This is the help that is available on the news screen."
TextWrapping="Wrap" />
</GroupBox>
<StackPanel DockPanel.Dock="Left" Margin="10"
Width="Auto" HorizontalAlignment="Stretch">
<TextBlock Text="Here is the news that should wrap around."
TextWrapping="Wrap"/>
</StackPanel>
</DockPanel>
</DockPanel>
If you are on a platform without DockPanel available (e.g. WindowsStore), you can create the same effect with a grid. Here's the above example accomplished using grids instead:
<Grid Width="200" Height="200" Background="PowderBlue">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<TextBlock>Something</TextBlock>
<TextBlock>Something else</TextBlock>
</StackPanel>
<Grid Height="Auto" Grid.Row="1" Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<GroupBox
Width="100"
Height="Auto"
Grid.Column="1"
Background="Beige"
Header="Help">
<TextBlock Text="This is the help that is available on the news screen."
TextWrapping="Wrap"/>
</GroupBox>
<StackPanel Width="Auto" Margin="10" DockPanel.Dock="Left">
<TextBlock Text="Here is the news that should wrap around."
TextWrapping="Wrap"/>
</StackPanel>
</Grid>
</Grid>
The reason that this is happening is because the stack panel measures every child element with positive infinity as the constraint for the axis that it is stacking elements along. The child controls have to return how big they want to be (positive infinity is not a valid return from the MeasureOverride in either axis) so they return the smallest size where everything will fit. They have no way of knowing how much space they really have to fill.
If your view doesn’t need to have a scrolling feature and the answer above doesn't suit your needs, I would suggest implement your own panel. You can probably derive straight from StackPanel and then all you will need to do is change the ArrangeOverride method so that it divides the remaining space up between its child elements (giving them each the same amount of extra space). Elements should render fine if they are given more space than they wanted, but if you give them less you will start to see glitches.
If you want to be able to scroll the whole thing then I am afraid things will be quite a bit more difficult, because the ScrollViewer gives you an infinite amount of space to work with which will put you in the same position as the child elements were originally. In this situation you might want to create a new property on your new panel which lets you specify the viewport size, you should be able to bind this to the ScrollViewer’s size. Ideally you would implement IScrollInfo, but that starts to get complicated if you are going to implement all of it properly.
An alternative method is to use a Grid with one column and n rows. Set all the rows heights to Auto, and the bottom-most row height to 1*.
I prefer this method because I've found Grids have better layout performance than DockPanels, StackPanels, and WrapPanels. But unless you're using them in an ItemTemplate (where the layout is being performed for a large number of items), you'll probably never notice.
You can use SpicyTaco.AutoGrid - a modified version of StackPanel:
<st:StackPanel Orientation="Horizontal" MarginBetweenChildren="10" Margin="10">
<Button Content="Info" HorizontalAlignment="Left" st:StackPanel.Fill="Fill"/>
<Button Content="Cancel"/>
<Button Content="Save"/>
</st:StackPanel>
First button will be fill.
You can install it via NuGet:
Install-Package SpicyTaco.AutoGrid
I recommend taking a look at SpicyTaco.AutoGrid. It's very useful for forms in WPF instead of DockPanel, StackPanel and Grid and solve problem with stretching very easy and gracefully. Just look at readme on GitHub.
<st:AutoGrid Columns="160,*" ChildMargin="3">
<Label Content="Name:"/>
<TextBox/>
<Label Content="E-Mail:"/>
<TextBox/>
<Label Content="Comment:"/>
<TextBox/>
</st:AutoGrid>

Resources