preventing WPF Grid from infinit growth - wpf

My question is similar to:
Prevent WPF control from expanding beyond viewable area
with the difference that the accepted solution (specific to the issue with StackPanel) does not really help me much.
I have a Window that currently only contains the following Grid:
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="265"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="147*"/>
<RowDefinition Height="173*"/>
</Grid.RowDefinitions>
<Button Content="Button" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="75" Click="Button_Click" Grid.Row="1" Grid.Column="1"/>
<local:BatteryGraphicsView x:Name="batView" HorizontalAlignment="Stretch" ClipToBounds="True" Width="265"/>
</Grid>
I want now that if my GraphicsView extends over the width of its column, that the scrollbars are displayed and the control does not paint it self beyond the bounds of the column.
This happens as soon as I press the button.
The user control basically is a grid that contains WinFormsHosts, when pressing the button there are more columns created for the grid and so the grid grows
(Usercontrol grid xaml)
<ScrollViewer VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Auto" ClipToBounds="True">
<Grid x:Name="Maingrid" ClipToBounds="True">
<Grid.RowDefinitions>
<RowDefinition Height="40*"/>
<RowDefinition Height="20*"/>
<RowDefinition Height="40*"/>
</Grid.RowDefinitions>
<WindowsFormsHost x:Name="host1" />
<WindowsFormsHost x:Name="host2" Grid.Row="2"/>
</Grid>
</ScrollViewer>
While the Scollviewer realises that it needs to display it self, the mainGrid anyway is displayed beyond the bounds of the column.
In multiple related posts I saw that you need to set the "ClipToBounds" property to true, but I set the property for every control and there is still no reaction.

The problem is that your ScrollViewer inside your UserControl doesn't know what the GridColumn & and GridRow sizes are.
There are two things you can do.
1: Specify a MAX Width for your Column & Max Height for you Row
2: Instead of adding your scrollViewer in the UserControl, add it in your grid and give it a height and width.

Related

Auto Width ListBox with a ScrollBar

The code below and screenshots show the issue I'm having below.
I need to have a listbox with Width set to Auto as the content size is variable. However, the number of items going into the listbox will also change, so sometimes a ScrollBar will be needed. The problem is, Auto Width doesn't seem to play well with a Scrollbar as per the images below. I could add a margin but I do not want a gap when no scroll bar is required.
I notice that this is only a problem when the textbox has a long string placed in it, if there is a short/no string entered, then the scrollBar presents correctly.
Is there a way to do this other than detecting if a ScrollBar is needed and dynamically adding a margin? I feel like there should be a way to achieve this in my XAML while still keeping the listbox column to be Width=Auto.
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 200; i++)
{
lbxDocs.Items.Add("TESTSTRING"+i);
}
txtImagePath.Text =
#"uPPvU18ijL9Tz6fqvwLsymkdxuS8h8jS9afzQ8i7LLXvrb2JO2hmPImVF5Dq5PZpdOkw2sTX9j6GeGL7IsaDuaf1ltY0MdzCRHGTZlUVkMa43meW3gavXAWMHyLPiyfGlHxuXcQOoH8ldxkYuxhVRcSJY3ZyCzlCsPjWuINTQyJCAU5hiDqroXWI8"+
"uPPvU18ijL9Tz6fqvwLsymkdxuS8h8jS9afzQ8i7LLXvrb2JO2hmPImVF5Dq5PZpdOkw2sTX9j6GeGL7IsaDuaf1ltY0MdzCRHGTZlUVkMa43meW3gavXAWMHyLPiyfGlHxuXcQOoH8ldxkYuxhVRcSJY3ZyCzlCsPjWuINTQyJCAU5hiDqroXWI8";
}
<Window x:Class="Grid_ScrollBar.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Grid_ScrollBar"
mc:Ignorable="d" Loaded="MainWindow_OnLoaded"
TextOptions.TextRenderingMode="ClearType" WindowStartupLocation="CenterScreen"
TextOptions.TextFormattingMode="Display" Height="400" Width="500">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Name="txtImagePath"/>
<ListBox Name="lbxDocs" Grid.Column="1" Grid.Row="1"/>
</Grid>
</Window>
Scroll Bar is cut off;
Once we hit the longer string item in the ListBox the width increases to show the rest of the scrollbar;
The problem is that you are telling to your textbox to take all the available space, i.e. all the space left by your ListBox. Now since the firsts elements of your listbox take "less" space, WPF grant them the minimum space possible, and it doesn't take in count the scrollbar size.
When you have this kind of problem (size of content that can very) my best advice is to not set width to auto. Instead go with proportional sizing (this will avoid also the the effect of physically shift columns to make more room for your listbox items).
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="9*"/>
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Name="txtImagePath" Grid.Column="0" Grid.Row="0" TextWrapping="Wrap" Margin="0,0,10,0"/>
<ListBox Name="lbxDocs" Grid.Column="1" Grid.Row="1" />
</Grid>
you can add also TextWrapping="Wrap" to show the full text.
However if you really want to keep the width to auto you can force wpf to take in count the scrollbar using
<ScrollViewer Grid.Column="1" Grid.Row="1">
<ListBox Name="lbxDocs"/>
</ScrollViewer>
EDIT: to fix the problem with the scrollviewer you need to set the height of the listbox to the heigh of the scrollviewer, or the Auto setting will not work:
<ScrollViewer x:Name="test" Grid.Column="1" Grid.Row="1" VerticalScrollBarVisibility="Auto">
<ListBox Name="lbxDocs" Height="{Binding ElementName=test, Path=ActualHeight}"/>
</ScrollViewer>
try adding the scrollview to your listview without changing the rest of xaml
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Name="txtImagePath"/>
<ListBox Name="lbxDocs"
Grid.Column="1"
Grid.Row="1"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
/>
Width = "*" means you will use all the rest of the available space.
Width = "Auto" means you will adjust the size of your content so that everything fits and not over any excess space
so (as you did) in your window will be column 1 adjusted to the size of the listview and column 0 with the rest of the window.

How to make TextBox fill all available space without later resizing to its content?

I have the following Grid with a TextBox in it:
<UserControl ...>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30px"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1px"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="1" Grid.RowSpan="3" AcceptsReturn="True"
TextWrapping="Wrap" HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Visible" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
<!-- content in other cells of the Grid's first column -->
</Grid>
</UserControl>
I want the TextBox to fill all space that is available on the control (width and height-wise) and should the user enter more text than fits into it I want its vertical scrollbar to become active, without resizing the TextBox. What happens instead is that the size of the TextBox changes to accomodate the content and the whole grid grows with it.
How do I achieve what I need?
Edit: Here's an illustration of what happens.
The above screenshot is of the situation when the content fits comfortably into the TextBox. The below screenshot is of the situation when there is not enough room to fit all content - the TextBox is then resized to fit it and this resizes also the grid that it is placed in, making it look broken.
Edit 2: The project that demonstrates this behavior is here.
I have managed to solve this by adding an invisible Border in the same cells of the Grid as the TextBox, then setting TextBox' Width and Height to ActualWidth and ActualHeight of that Border respectively:
<Border x:Name="b" Grid.Column="1" Grid.RowSpan="3"
HorizontalAlignment="Stretch"/>
<TextBox AcceptsReturn="True" TextWrapping="Wrap" Grid.Column="1"
Grid.RowSpan="3" Width="{Binding ActualWidth, ElementName=b}"
Height="{Binding ActualHeight, ElementName=b}"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Visible" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
This then caused the TextBox to stay of fixed Height regardless of its content, but there was another problem with its Width: it grew when the interface was expanded, but didn't shrink afterwards, because of the underlying ScrollViewer. The trick was to set that ScrollViewer's HorizontalScrollBarVisibility to Disabled, instead of Hidden as I had done previously.
I pushed changes of the example project to GitHub, so the solution is now available here.
The TextBox doesn't fill the Grid. You can confirm this yourself by specifying a Background for the Grid:
<UserControl>
<Grid Background="Yellow">
...
This is because the height of the Grid is 30 px + whatever the height of the TextBox is + 1 px. For the contents of the row 2 or 3 to fill the Grid, you need to change the Height of at least one of the RowDefinitions to *:
<UserControl>
<Grid Background="Yellow">
<Grid.RowDefinitions>
<RowDefinition Height="30px"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="1px"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="1" Grid.RowSpan="3" AcceptsReturn="True"
TextWrapping="Wrap" HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Visible" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
</UserControl>

WPF Covering area taken by Grid Row (or better, covering 2 rows)

I have a grid with 3 rows. Row #2 is tallest and it hosts WebBrowser control. I want to cover webbrowser with a Border at certain times.
What is WPF way to do that?
What I have below is my starter xaml. I use Visibility to collapse my border to show WebBrowser or set my Border to Visible to cover WebBrowser.
However, after doing so I end up having half of 2nd row covered with browser and other half with nothing.
Basically, I want to simulate a toast message that shows and covers entire area of WebBrowser control in GridRow 2.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
Grid.Row="2">
<WebBrowser x:Name="wb"
Loaded="wb_Loaded"/>
</ScrollViewer>
<Border x:Name="myBorder" Background="White"
Visibility="Collapsed"
Grid.Row="2" RowSpan="2">
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
//some text here
</StackPanel>
</Border>
<DockPanel HorizontalAlignment="Stretch" Grid.Row="3">
...
</DockPanel>
</Grid>
Use a Grid in Row:2 and show/hide Border/BrowserControl, as Grid allows you to stack things on top of each other.

Preserving minimum sizes with multiple WPF grids and splitters

I have a problem getting an arrangement of grids, splitters and an expander to work correctly, especially regarding setting min heights. I’ve followed a number of suggestions posted here (especially regarding how to combine a splitter and an expander), and have got the basic architecture working, but I can’t get the final details to work right. I’m sure that I could get something working by trial-and-error, especially if I do some brute force work in the code-behind, but I’m guessing there are some fundamental principles of how WPF resizing works that I’m missing here.
The following xaml is a simplified version of the actual structure, with colors and borders added for clarity. I’ve removed my attempts at setting MinHeights. (There is currently no code-behind.)
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="450"
Height="600">
<Grid Background="Yellow">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox x:Name="Tree" Grid.Row="0" Text="Tree" BorderThickness="2" BorderBrush="Red" />
<Expander IsExpanded="True" Grid.Row="1" Header="Expand/Collapse Details" ExpandDirection="Down"
BorderThickness="2" BorderBrush="Black" VerticalAlignment="Bottom">
<Expander.Content>
<Grid x:Name="LowerAreaGrid" Margin="4">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<GridSplitter HorizontalAlignment="Stretch" Grid.Row="0" Height="8" Background="DarkGray"
ResizeDirection="Rows" ResizeBehavior="CurrentAndNext" />
<Grid x:Name="DetailsPaneAndSubpane" Margin="4" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="*" MaxHeight="{Binding ActualHeight, ElementName=DetailsPane}"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto" Grid.Row="0">
<TextBox x:Name="DetailsPane" Text="This is the Details Pane. It is 150 pixels high. If the available space is too small, it will grow a scrollbar to see all the content."
TextWrapping="Wrap" BorderThickness="4" BorderBrush="Green" Height="150"/>
</ScrollViewer>
<GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" Height="8" Background="Brown"
ResizeDirection="Rows" ResizeBehavior="PreviousAndNext" />
<Border BorderThickness="4" Grid.Row="2" BorderBrush="Blue">
<Grid x:Name="SubpaneAndButtons" Margin="4" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="This is the Details Subpane. It can grow and shrink."
BorderThickness="2" BorderBrush="Blue"/>
<Grid x:Name="SubpaneButtons" Grid.Row="1" Background="Blue" Margin="4">
<Button Width="200">Do something with subpane</Button>
</Grid>
</Grid>
</Border>
</Grid>
</Grid>
</Expander.Content>
</Expander>
<Grid x:Name="CommandButtons" Grid.Row="2" Background="Pink" Margin="4">
<Button Width="200">Do something with whole window</Button>
</Grid>
</Grid>
</Page>
As you can see, the window has two main areas. There is a tree in the top pane, and when an item in the tree is selected, the bottom area (which is contained in an expander) shows details about the item. There is a splitter that can change the relative sizes of the top and bottom areas. The details area is further divided into two parts: a Details Pane which has a fixed maximum size, and a bottom Details Subpane which can grow and shrink. A second splitter controls the relative sizes of the Details Pane and Details Subpane.
There’s also a button at the bottom of the window that represents a set of buttons that are commands that apply to the whole window. There’s a button at the bottom of the Details Subpane that represents a set of buttons that are commands that apply only to the Details Subpane.
Here’s what doesn’t work:
Problem: the second (brown) splitter can’t be moved up initially. (It’s not expected to be able to move down, since the Details Pane is at its maximum size.) You have to move the gray splitter a little bit, then the brown splitter works fine even if you move the gray splitter back to where it was.
The Tree should have a MinHeight of 75. Problem: putting this in the tree’s RowDefinition causes the rest of the content to slide off the end of the window when you drag the slider up past the 75 pixel mark. (If you don’t have a MinHeight, this sliding occurs when the splitter is dragged up past the top of the window.)
The Details Pane and the Details Subpane should have a MinHeight of 50. Problem: moving either of the splitters down can squish these areas down to nothing.
Neither splitter should affect the “Do something with subpane” button row. It should always be fully visible, unless the expander is collapsed. Problem: again, moving either of the splitters down can squish this area down to nothing.
Here’s what works, and should keep working after all changes are made:
When the “Expand/Collapse Details” expander is collapsed, it collapses everything but the “Do something with whole window” button row. The Tree takes all the remaining space.
The Details Pane is a fixed size (150 pixels). It can’t grow larger than that, and if it is given less space than that, it will grow a scrollbar to allow the entire 150 pixels to be seen still.
What changes need to be made (either in xaml or code-behind) to get this to work?

Scroll bar for user control in Silverlight

I've got a user control defined as this:
<UserControl
x:Class="Client.UserControl1"
d:DesignWidth="640" d:DesignHeight="480" MinWidth="400" MinHeight="400"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="24"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<RichTextBox Margin="10" Grid.Row="1" HorizontalScrollBarVisibility="Auto">
<Paragraph><Run Text="RichTextBox"/></Paragraph>
</RichTextBox>
<sdk:Label Margin="0" Height="24" HorizontalAlignment="Center"
Content="Message"/>
</Grid>
</UserControl>
I then use this control in a page as follows:
<navigation:Page
Style="{StaticResource PageStyle}" ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<Grid x:Name="LayoutRoot" Background="Blue"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Client:UserControl1 Margin="10"/>
</Grid>
</navigation:Page>
I want the user control to fill the page. As the page expands, so does the user control. This works fine. However, I specify a minimum height and width for the user control. If the page is smaller than this, I want scroll bars to be shown. However, when I make the page smaller than the minimum size, no scrollbars appear.
I've tried wrapping the user control in a scrollviewer, but this does not give me the behaviour I want. It this case, the rich text box contains is now drawn in its entirety, i.e. it no longer has any scroll bars of its own. I still want the rich text box to be bound by the size of the page, i.e. if the browser is maximized and the rich text box contains 100s of lines of text, I can still see the border of the rich text box, and the rich text box will have scroll bars to allow viewing of its contenxt. When the browser window is smaller than the min size of the control, then I want scrollbars for the user control.
Is this possible?
Cheers
Just wrap a ScrollViewer around the "Client:UserControl1" on your page.
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<<Client:UserControl1 Margin="10"/>
</ScrollViewer>
Also remove all other references to ScrollViewer.HorizontalScrollBarVisibility and ScrollViewer.VerticalScrollBarVisibility.
Cheers

Resources