Silverlight: GridSplitter inside ScrollViewer behaving unexpectedly - silverlight

I'm trying to build a two-column layout where the width of the columns can be changed by using a splitter. The right column's width shouldn't change when the browser window is resized (it shouldn't be proportional to the grid width). Both columns should have a minimum width. When the browser window is too narrow to display both columns a scrollbar should appear.
This is my XAML:
<Grid x:Name="LayoutRoot" Background="White">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
<Grid Height="200" Margin="0,0,0,0" MinWidth="400" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="300" Width="300*" />
<ColumnDefinition Width="10" />
<ColumnDefinition MinWidth="100" Width="100"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Background="Red" x:Name="LeftColumn"></Grid>
<sdk:GridSplitter Grid.Column="1" HorizontalAlignment="Center" Width="10" Background="Black" />
<Grid Grid.Column="2" Background="Green" x:Name="RightColumn"></Grid>
</Grid>
</ScrollViewer>
</Grid>
The problem I'm having is when the splitter is dragged to the left and the left column's minwidth is reached - the right column begins to grow very rapidly and the scrollbar appears. Removing the width setting from the right column eliminates the weird behavior but now the right column starts to grow proportionally when the window is resized...
I'd like the splitter to behave the same way as when it is dragged to the right - I'd like it to stop after the minwidth is reached.

You should disable the "HorizontalScrollBarVisibility".
This code works for me:
<Grid x:Name="LayoutRoot" Background="White">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Disabled">
<Grid Height="200" Margin="0,0,0,0" MinWidth="400" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="300" Width="300*" />
<ColumnDefinition Width="10" />
<ColumnDefinition MinWidth="100" Width="100"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Background="Red" x:Name="LeftColumn"></Grid>
<sdk:GridSplitter Grid.Column="1" HorizontalAlignment="Center" Width="10" Background="Black" />
<Grid Grid.Column="2" Background="Green" x:Name="RightColumn"></Grid>
</Grid>
</ScrollViewer>
</Grid>
The ScrollViewer gives the grid endless space to grow. Hence the minWidth never stops it.
Obviously there is no need of ScrollViewer that is disabled both vertically and horizontally. Better move the scroll bar inside the grid surrounding the content of every column.

I think I was finally able to come up with a workaround. I'm forcing the width in the code-behind when the layout is changing.
XAML:
<Grid x:Name="LayoutRoot" Background="White">
<ScrollViewer x:Name="Scroller" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
<Grid x:Name="Workspace" Height="200" Margin="0,0,0,0" MinWidth="400" VerticalAlignment="Top" LayoutUpdated="Workspace_LayoutUpdated">
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="300" Width="300*" />
<ColumnDefinition Width="10" />
<ColumnDefinition MinWidth="100" Width="100"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Background="Red" x:Name="LeftColumn"></Grid>
<sdk:GridSplitter Grid.Column="1" HorizontalAlignment="Center" Width="10" Background="Black" />
<Grid Grid.Column="2" Background="Green" x:Name="RightColumn"></Grid>
</Grid>
</ScrollViewer>
</Grid>
Code behind:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private void Workspace_LayoutUpdated(object sender, EventArgs e)
{
Workspace.Width = Scroller.ViewportWidth - 1;
}
}

The reason it behaves this way is that you have specified the first column Width="300*" with asterisks,
and the third column Width="100" without asterisks.
Just put asterisks to the first and third columns, or remove respectively, and it will work the way you wish.

Related

Display dynamic width of container in WPF

<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="0.55*" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Background="Gray">
<TextBlock x:Name="EmployeeNameTextBlock"
VerticalAlignment="Center"
Margin="50,0,0,0"
FontSize="18"
RenderTransformOrigin="0.5,0.5"
Text="Content09asdfadsdsdasdfasd92168132 "
TextWrapping="NoWrap"
/>
<ToggleButton x:Name="btn"
HorizontalAlignment="Left"
Margin="0,0,0,0"
VerticalAlignment="Center"
Height="30"
Width="30" >
Button
</ToggleButton>
</StackPanel>
<Label Grid.Column="1" Background="Yellow" />
</Grid>
This is my code i have issue in displaying the toggle button, you can refer to screen shot below for currently displaying and expectation
conditions
* button should be always end of the text block even content is small or big
* if content is bigger then container, textbox size should stop at before less width of button. so that we can see toggle button on display. as shown in expected screen shot.
Result Screenshot long text
Result Screenshot small text
Expected screenshot long text
Expected Screenshot small text
Please any one help on this
Thanks in advance
Solution
<Grid SizeChanged="Grid_SizeChanged">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="0.55*" />
</Grid.ColumnDefinitions>
<Border Name="rightBlk" Background="Green" Grid.Column="1" />
<Grid VerticalAlignment="Center" Background="Gray">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="EmployeeNameTextBlock"
VerticalAlignment="Center"
Margin="50,0,0,0"
TextTrimming="CharacterEllipsis"
FontSize="18"
RenderTransformOrigin="0.5,0.5"
Text="Content09asasdfasdfd "
TextWrapping="NoWrap"
Padding="0,0,30,0"
/>
<ToggleButton x:Name="btn"
HorizontalAlignment="Right"
Margin="0,0,0,0"
VerticalAlignment="Center"
Height="30"
Width="30" >
Button
</ToggleButton>
</Grid>
<Label Grid.Column="1" Background="Yellow" />
</Grid>
Code Behide
private void Grid_SizeChanged(object sender, SizeChangedEventArgs e)
{
EmployeeNameTextBlock.MaxWidth = ((Grid)sender).ActualWidth - 50 - rightBlk.ActualWidth;
}
Thank you #lvan and #Neptune for helping me to solve this issue.
You can replace the StackPanel with a Grid. This way you have more control on distribution and can give your Button priority to get its width first before allocating the remaining to the TextBlock:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="0.55*" />
</Grid.ColumnDefinitions>
<Grid VerticalAlignment="Center" Background="Gray">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="EmployeeNameTextBlock"
VerticalAlignment="Center"
Margin="50,0,0,0"
TextTrimming="CharacterEllipsis"
FontSize="18"
RenderTransformOrigin="0.5,0.5"
Text="Content09asdfadsdsdasdfasd92168132 "
TextWrapping="NoWrap"
/>
<ToggleButton x:Name="btn" Grid.Column="1"
HorizontalAlignment="Left"
Margin="0,0,0,0"
VerticalAlignment="Center"
Height="30"
Width="30" >
Button
</ToggleButton>
</Grid>
<Label Grid.Column="1" Background="Yellow" />
</Grid>
I added an attribute TextTrimming if you want to indicate to user there's missing text but you can remove it if that's not needed.
The code above produces this result when the width is limited:
You can get what you are looking for with either you code and adding
MaxWidth="270" and TextTrimming="CharacterEllipsis" to textbox
or Neptunes code and changing
<ColumnDefinition Width="*" /> to "Auto" and also adding MaxWidth="270" to textbox.
I don't think you can do it without setting a MaxWidth on the textbox but that means you'll have to recalculate it if you resize your window.
To recalculate:
Subscribe to main window size changed event:
SizeChanged += MainWindow_SizeChanged; within main window constructor or in xaml
And add to codebehind:
private void MainWindow_SizeChanged(object sender, SizeChangedEventArgs e)
{
EmployeeNameTextBlock.MaxWidth = StackPanelName.ActualWidth - btn.ActualWidth;
}
You might need to use .Width instead of Actual width, and you can also try using the column instead of the stack panel but this illustrates the recalculation you need to do.

Avoid WPF statusbar gets totally collapsed when window gets resized vertically to a smaller size

I have below WPF window which contains a dockpanel as main container. Then I place a main grid at the top (which contains some other grids) and a statusbar at the bottom.
<Window>
<DockPanel>
<Grid DockPanel.Dock="Top">
<!-- Grid stuff here -->
</Grid>
<StatusBar DockPanel.Dock="Bottom"
VerticalAlignment="Bottom">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem Grid.Column="0">
<TextBlock Text="Item1"/>
</StatusBarItem>
<Separator Grid.Column="1" />
<StatusBarItem Grid.Column="2">
<TextBlock />
</StatusBarItem>
<Separator Grid.Column="3" />
<StatusBarItem Grid.Column="4">
<TextBlock Text="AnotherItem" />
</StatusBarItem>
</DockPanel>
</Window>
I have below problem:
When window is resized vertically to a smaller size, there comes a moment in which statusbar height is reduced and even completely collapsed. So how to avoid this? I want statusbar never gets collapsed and keep all the time its height.
Grids within Grids are so hot right now.
<Window x:Class="GridRoot.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Border BorderBrush="Black" BorderThickness="10" Background="CornflowerBlue">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">
Hello!
</TextBlock>
</Border>
</Grid>
<StatusBar Grid.Row="1">
<StatusBarItem>
<TextBlock>
GET OFF ME!
</TextBlock>
</StatusBarItem>
</StatusBar>
</Grid>
</Window>
The top row takes up as much space is available, and the bottom row takes up as much space as its content desires. Since the StatusBar control pretty much has a set height, it will always stay visible on the bottom.
Some folks might have issue with putting a Grid inside another Grid, but there's absolutely no reason not to.
The above example, tiny
and embiggened

Stretch WPF Dockpanel contents horizontally

I have a DockPanel and it contains a ScrollViewer [ center aligned ] and a button on left and right .
My xaml is like
<DockPanel LastChildFill="True">
<Button VerticalAlignment="Stretch" HorizontalAlignment="Stretch" DockPanel.Dock="Left">Left</Button>
<Button VerticalAlignment="Stretch" HorizontalAlignment="Stretch" DockPanel.Dock="Right">Right</Button>
<ScrollViewer Name="scrollAreaPageView" HorizontalAlignment="Center" VerticalAlignment="Center"
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
</ScrollViewer>
</DockPanel>
And it generates the output as expected , but Left and right butons are not stretched fully to left and right to the ScrollViewer( They are on corners only).
The screen shot of output is
How can i make it stretch fully to left and right of center scrollViewer
A DockPanel may not be ideal in this scenario you may perhaps use Grid here with the desired column definition
sample
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button>Left</Button>
<ScrollViewer Name="scrollAreaPageView"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Grid.Column="1">
</ScrollViewer>
<Button Grid.Column="2">Right</Button>
</Grid>
in above example the space available after subtracting the space required b
Alternate approach
I attempted to do it pure xaml, this approach will helo you achieve the desired without code behind. here is the example
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="{Binding ActualWidth,ElementName=scrollAreaPageView}" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button>Left</Button>
<Button Grid.Column="2">Right</Button>
</Grid>
<ScrollViewer HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="50,0"
Name="scrollAreaPageView"
HorizontalScrollBarVisibility="Auto">
</ScrollViewer>
</Grid>
Margin in the scrollAreaPageView element defines minimum width of the buttons. give it a try and see if that helps

Is there a way to have the GridSplitter not to push elements out of the window?

With the XAML below when I drag the GridSplitter to the left it pushes elements out of the window.
How can I keep all elements within the window frame?
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="0" />
<Button Grid.Column="1" Content="1" />
<Button Grid.Column="2" Content="2" />
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Left" />
</Grid>
Thanks
The only way I know to solve your problem is have the columns that are left and right of your gridsplitter have the width property set as Width="*" and give the GridSplitter its own column with a HorizontalAlignment set as HorizontalAlignment="Stretch". Your code would then end up looking like this.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="0" />
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch"/>
<Button Grid.Column="2" Content="1" />
<Button Grid.Column="3" Content="2" />
</Grid>
I was having this same issue, and came up with this solution. Basically, the idea is to dynamically change the MaxWidth of the appropriate column when the grid/columns change. I've shown with two columns here, but I've used this approach with three columns successfully.
With this approach, if you resize the window down to not fit the contents of the grid anymore, the grid stops changing size, so the ResizeGrid_SizeChanged event stops getting called. To solve for this, you can also listen for the window (or user control) size change events. You may also need to bind the MaxWidth of your grid appropriately, or use the control size directly if your grid fills the window/UserControl. I've shown how to bind the MaxWidth property through XAML here. If you didn't want to do that, you could replace "ResizeGrid.MaxWidth" with "ActualWidth" inside the window code behind, and remove the "MaxWidth" binding on the "ResizeGrid" object.
XAML:
<Window x:Class="App.Window1"
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:Default="clr-namespace:App"
mc:Ignorable="d"
SizeChanged="Window_SizeChanged"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid x:Name="ResizeGrid" SizeChanged="ResizeGrid_SizeChanged"
MaxWidth="{Binding ActualWidth, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Default:Window1}}}">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="C0" Width="150" MinWidth="50" />
<ColumnDefinition Width="5" />
<ColumnDefinition x:Name="C2" Width="*" MinWidth="50" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Background="Green">
<Label Content="Left" />
<Label Content="Right" HorizontalAlignment="Right" />
</Grid>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" DragCompleted="GridSplitter_DragCompleted" />
<Grid Grid.Column="2" Background="Red">
<Label Content="Left" />
<Label Content="Right" HorizontalAlignment="Right" />
</Grid>
</Grid>
</Grid>
</Window>
C# Code Behind
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateGridSplitterWidths();
}
private void ResizeGrid_SizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateGridSplitterWidths();
}
private void GridSplitter_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
{
UpdateGridSplitterWidths();
}
private void UpdateGridSplitterWidths()
{
C0.MaxWidth = Math.Min(ResizeGrid.ActualWidth, ResizeGrid.MaxWidth) - (C2.MinWidth + 5);
}
Just to add to #Joseph's answer, which pointed me in the right direction, it still works when you fraction the "*", or include min/max widths.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.35*"
MinWidth="48" />
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"
MaxWidth="48" />
</Grid.ColumnDefinitions>
This lets you set the initial width of each column in keeping with your intent, as well as providing the functionality of not pushing things off screen.

WPF Layout Control

I am new to WPF.
I have a wpf window which contains a grid which is dynamic in size along with its columns. This window is supposed to be a small utility type window that is always ontop.
The issue is as the user types into the richtextbox it expands of the bottom of the page, I would like a scroll bar to appear.
I have tried placing it in a container but this doesnt work.
I want the grid to resize if the user decides to resize the window.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="202" Width="927" WindowStyle="ToolWindow" ShowInTaskbar="True" Topmost="True">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<GridSplitter HorizontalAlignment="Right"
VerticalAlignment="Stretch"
ResizeBehavior="PreviousAndNext"
Grid.Column="1"
Width="1"
ResizeDirection="Columns"/>
<GridSplitter HorizontalAlignment="Right"
ResizeBehavior="PreviousAndNext"
VerticalAlignment="Stretch"
Grid.Column="3"
Width="1"
ResizeDirection="Columns"/>
<StackPanel Grid.Column="2" Height="Auto">
<Label Background="SteelBlue" HorizontalAlignment="Stretch" Foreground="white" Height="25">Note</Label>
<RichTextBox ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.CanContentScroll="True"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" >
</RichTextBox>
</StackPanel>
</Grid>
</Window>
Have you tried:
<RichTextBox ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.CanContentScroll="True"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
***Height="300">***
</RichTextBox>
StackPanels do not do vertical layout, you should probably either use a DockPanel or a Grid with two Rows instead, that way the RichTextBox is bounded and knows when to use its scrolling functionality.
Thank H.B replacing StackPanel with grid worked. Alex adding the height didnt work sorry.
So I replace the StackPanel with
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Label Grid.Row="0" Background="SteelBlue" HorizontalAlignment="Stretch" Foreground="white" Height="25">Note</Label>
<RichTextBox ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.CanContentScroll="True"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Grid.Row="1">
</RichTextBox>
</Grid>

Resources