ListView scrollbar messes up my layout - c#

I have a WPF ListBox that typically shows 4 or 5 items. For my application that means I almost never have to display a scrollbar (there is enough space).
However, in case there are more items in the list, I need to show the vertical scroll bar, but as a result my content has less space and doesn't look nice anymore on a "backdrop" I've created behind the listbox.
I like to "reserve" room in my layout for the scrollbar to appear. Is there a way to do that? (maybe having the scrollbar overlayed on the content)

What about wrapping it with a ScrollViewr?
<ScrollViewer VerticalScrollBarVisibility="Auto">
<!-- your ListBoxHere -->
</ScrollViewer>

Alright, found a solution.
I've created a new default style for the ScrollViewer by following this MSDN article. Then I've changed the part where the scrollbars are placed.
Original behaviour
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Grid.Column="1">
<ScrollContentPresenter CanContentScroll="True" Content="{TemplateBinding ScrollViewer.Content}" />
</Border>
<ScrollBar Orientation="Vertical" Grid.Row="0" Grid.Column="0" Minimum="0" Maximum="{TemplateBinding ScrollViewer.ScrollableHeight}" Value="{TemplateBinding ScrollViewer.VerticalOffset}" ViewportSize="{TemplateBinding ScrollViewer.ViewportHeight}" Name="PART_VerticalScrollBar" Visibility="{TemplateBinding ScrollViewer.ComputedVerticalScrollBarVisibility}" />
<ScrollBar Orientation="Horizontal" Grid.Row="1" Grid.Column="1" Minimum="0" Maximum="{TemplateBinding ScrollViewer.ScrollableWidth}" Value="{TemplateBinding ScrollViewer.HorizontalOffset}" ViewportSize="{TemplateBinding ScrollViewer.ViewportWidth}" Name="PART_HorizontalScrollBar" Visibility="{TemplateBinding ScrollViewer.ComputedHorizontalScrollBarVisibility}"/>
</Grid>
New behaviour
<Grid>
<!-- Presentation below is different from the default: the scrollbar is overlayed on the content. -->
<ScrollContentPresenter CanContentScroll="True" Content="{TemplateBinding ScrollViewer.Content}" />
<ScrollBar Orientation="Vertical" HorizontalAlignment="Right" Minimum="0" Maximum="{TemplateBinding ScrollViewer.ScrollableHeight}" Value="{TemplateBinding ScrollViewer.VerticalOffset}" ViewportSize="{TemplateBinding ScrollViewer.ViewportHeight}" Name="PART_VerticalScrollBar" Visibility="{TemplateBinding ScrollViewer.ComputedVerticalScrollBarVisibility}" />
<ScrollBar Orientation="Horizontal" VerticalAlignment="Bottom" Minimum="0" Maximum="{TemplateBinding ScrollViewer.ScrollableWidth}" Value="{TemplateBinding ScrollViewer.HorizontalOffset}" ViewportSize="{TemplateBinding ScrollViewer.ViewportWidth}" Name="PART_HorizontalScrollBar" Visibility="{TemplateBinding ScrollViewer.ComputedHorizontalScrollBarVisibility}"/>
</Grid>
Now the scrollbars are overlayed on the content so they don't take additional space when visible. Of course the content should now reserve space for the content.

As you said in your question, you just need to reserve the width for the vertical scrollbar, following XAML code will give you some idea.
<ScrollViewer VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="30"/> <!-- reserved for the vertical scrollbar -->
</Grid.ColumnDefinitions>
<Listbox .../>
</Grid>
</ScrollViewer>

Related

WPF: How do I get a text box to fill all available horizontal space?

I'm creating a form in WPF. The form has TextBoxes with TextBlocks to their left. A screenshot with GridLines is below:
I want it to look like this (forgive the MS Paint style):
The problem is: the text boxes are really small unless I set the HorizontalAlignment to Stretch, but if I do that, I can't align them to the left. How do I get the Grid containing TextBlock/TextBox to align to the left and make the TextBox fill all available space?
It's trivially easy if I hardcode widths, but that obviously won't work with resizing. I've tried playing with Stack and Dock Panels to hold the Grids with no luck.
I can't use HorizontalAlignment=Stretch, MaxWidth, and Left aligned at the same time? because there is no other control that is the width of the space I am trying to fill.
I also can't use How to get controls in WPF to fill available space? because nothing in my xaml has HorizontalContentAllignment. I tried wrapping stuff in ContentControls and using an ItemsControl to force it with no luck.
The xaml for the form is below. Note, this xaml is for the control that is under the 'Create' header and to the right of the 3 buttons.
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!--Left hand side content goes here-->
<DockPanel Grid.Row="0" Grid.Column="1" Grid.RowSpan="2"
Margin="20,0,0,0">
<Grid ShowGridLines="True" DockPanel.Dock="Top" HorizontalAlignment="Stretch" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="TextBlockColumn"/>
<ColumnDefinition SharedSizeGroup="TextBoxColumn"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="Input Name:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch" VerticalAlignment="Center"
Style="{StaticResource FormTextBox}"
Margin="5,0,5,0"/>
</Grid>
<Grid ShowGridLines="True" DockPanel.Dock="Top" HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="TextBlockColumn"/>
<ColumnDefinition SharedSizeGroup="TextBoxColumn"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="Input Name:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch" VerticalAlignment="Center"
Style="{StaticResource FormTextBox}"
Margin="5,0,5,0"/>
</Grid>
</DockPanel>
</Grid>
The FormTextBox style is:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="TextBox"
x:Key="FormTextBox">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border CornerRadius="10"
Background="{StaticResource InputBrush}">
<Grid>
<Rectangle StrokeThickness="1"/>
<TextBox Margin="1"
Text="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=Text,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
BorderThickness="0"
Background="Transparent"
VerticalContentAlignment="Center"
Padding="5"
Foreground="{StaticResource TextBrush}"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It rounds the corners, changes the background color, and removes the dotted line that shows up if it was selected before you alt-tabbed out and back into the application.
You indicate that this is the desired layout:
There are a few problems with your current code. First, using a DockPanel to host the TextBlock/TextBox pairs, and second, no control has Grid.IsSharedSizeScope=true set on it. You also have defined a shared size for the text box column when in reality you just want it to take up all the available space.
Here is some code that achieves the desired layout:
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!--Left hand side content goes here-->
<StackPanel Orientation="Vertical" Grid.Column="1"
Grid.RowSpan="2"
Margin="20,0,0,0"
Grid.IsSharedSizeScope="True" >
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Text" Value="Input Name:"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</StackPanel.Resources>
<Border BorderBrush="Blue" BorderThickness="5">
<Grid ShowGridLines="True" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="TextBlockColumn"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock/>
<TextBox Grid.Column="1"
VerticalAlignment="Center"
Style="{StaticResource FormTextBox}"
Margin="5,0,5,0"/>
</Grid>
</Border>
<Border BorderBrush="Red" BorderThickness="5">
<Grid ShowGridLines="True" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="TextBlockColumn"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock/>
<TextBox Grid.Column="1"
VerticalAlignment="Center"
Style="{StaticResource FormTextBox}"
Margin="5,0,5,0"/>
</Grid>
</Border>
</StackPanel>
</Grid>
This code produces:
In reality though, if you're going for maximum usability, you'll want to create your own control for the TextBlock/TextBox, and then you could just put it in an ItemsControl of some kind for dynamic content.

WPF - Columns don't hide properly when GridSplitter is moved

I'm trying to hide a column in a Grid with a GridSplitter when a button is clicked (the button sets the visibility of all items in the third column to collapsed). If I don't move the GridSplitter it works properly and the third column disappear, but if I move the GridSplitter the content disappear but the others columns don't resize to fill the empty space.
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="25"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="a" Width="*"/>
<ColumnDefinition x:Name="b" Width="3"/>
<ColumnDefinition x:Name="c" Width="Auto" MaxWidth="600"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Grid.Row="0" HorizontalAlignment="Stretch" Background="Green">
<Image Source="te/Dante.png" Height="Auto" Margin="0,128,2,71"/>
</Border>
<Button Grid.Column="0" Grid.Row="0" Width="30" Height="30" Margin="0,10,10,0" HorizontalAlignment="Right" VerticalAlignment="Top" Click="Button_Click"></Button>
<GridSplitter Width="5" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" ResizeDirection="Columns" HorizontalAlignment="Left" Background="White" BorderBrush="Black" BorderThickness="1,0" ResizeBehavior="PreviousAndCurrent"/>
<WrapPanel x:Name="wpC" Grid.Column="2" Grid.Row="0" Grid.RowSpan="2" MinWidth="300" HorizontalAlignment="Stretch" Background="Aqua" Panel.ZIndex="-1"></WrapPanel>
</Grid>
Here is an example of my problem (gif):
How can i solve this problem? Possibly respecting MVVM pattern.
The problem is simple, you set GridSplitter ResizeBehavior="PreviousAndCurrent", but previous grid column width is * and as soon as you move splitter its width units will be changed to absolute (so it will not be able to resize when 3d column width is changed).
Simply set GridSplitter ResizeBehavior="PreviousAndNext" to solve the problem. If you do so the splitter will modify width of 3d column, but shouldn't touch first one anymore.
Btw, instead of using button and click event you can utilize ToggleButton (which IsChecked is bound to Visibility of container with content you want to hide), see this answer. Using converters with pure xaml view is better MVVM than the one with some code behind and x:Name.
Right, you have few layout problems, here is a complete solution:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border Background="Green" />
<ToggleButton x:Name="toggleButton"
Width="30"
Height="30"
Margin="0,10,10,0"
HorizontalAlignment="Right"
VerticalAlignment="Top" />
<Grid Grid.Column="1"
Visibility="{Binding IsChecked, ElementName=toggleButton, Converter={StaticResource BooleanToVisibilityConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="300"
MinWidth="300"
MaxWidth="600" />
</Grid.ColumnDefinitions>
<GridSplitter Width="5"
ResizeBehavior="CurrentAndNext" />
<WrapPanel Grid.Column="1"
Background="Aqua" />
</Grid>
</Grid>
No need for code-behind, get converter from here.
Point are: 1) put splitter inside hide-able container 2) setup grid columns to have * and fixed width (splitter doesn't work well with auto columns).
Demo:

Vertical and Horizontal ScrollBars are not working - WPF

I have the following code:
<DockPanel>
<Grid>
<Grid.ColumnDefinitions>
<!--This will make any control in this column of grid take 1/10 of total width-->
<ColumnDefinition Width="1*" />
<!--This will make any control in this column of grid take 4/10 of total width-->
<ColumnDefinition Width="4*" />
<!--This will make any control in this column of grid take 4/10 of total width-->
<ColumnDefinition Width="4*" />
<!--This will make any control in this column of grid take 1/10 of total width-->
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Border BorderBrush="Black" BorderThickness="1" Grid.Row="0" Grid.Column="0">
<StackPanel>
<Image Source="/SAMPLE;component/Images/1_Left.png"/>
<Image Source="/SAMPLE;component/Images/2_Left.png"/>
<Image Source="/SAMPLE;component/Images/3_Left.png"/>
<Image Source="/SAMPLE;component/Images/4_Left.png"/>
</StackPanel>
</Border>
<ScrollViewer Grid.Row="0" Grid.Column="1">
<Canvas Name="Canvas1">
<Image Name="LeftImage"/>
<Image Name="LeftIcon"/>
</Canvas>
</ScrollViewer>
<ScrollViewer Grid.Row="0" Grid.Column="2">
<Canvas Name="Canvas2">
<Image Name="RightImage"/>
<Image Name="RightIcon"/>
</Canvas>
</ScrollViewer>
<Border BorderBrush="Black" BorderThickness="1" Grid.Row="0" Grid.Column="3">
<StackPanel>
<Image Source="/SAMPLE;component/Images/5_Right.png"/>
<Image Source="/SAMPLE;component/Images/6_Right.png"/>
<Image Source="/SAMPLE;component/Images/7_Right.png"/>
<Image Source="/SAMPLE;component/Images/8_Right.png"/>
</StackPanel>
</Border>
</Grid>
</DockPanel>
Even though the "LeftImage" and "RightImage" are having more width and height, the scroll bars are not working. I cannot able to scroll to view the complete image. Any help ?
Thanks
Grid does not support scrolling functionality. If you want to scroll something you need ScrollViewer control. So place your grid within a ScrollViewer insted of DockPanel
<ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
//Your grid
</ScrollViewer>

Custom header with ExpanderView from Toolkit for Windows Phone

I am using ExpanderView from Toolkit for Windows Phone. What I add is line below each Header. What I can't do is stretch line for Headers which are Expandable.
<toolkit:ExpanderView x:Name="ev" HorizontalAlignment="Stretch" Margin="0,0,0,8"
Header="{Binding}"
NonExpandableHeader="{Binding}"
Expander="{Binding}"
ItemsSource="{Binding}"
IsNonExpandable="{Binding HasSingleMessage}">
<toolkit:ExpanderView.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding LastMessageReceived.Header}"
HorizontalAlignment="Stretch"
Foreground="Black"
FontSize="{StaticResource PhoneFontSizeLarge}"
FontFamily="{StaticResource PhoneFontFamilySemiLight}"/>
<Rectangle Height="1" StrokeThickness="0" Fill="Black" Margin="0,8" />
</StackPanel>
</DataTemplate>
</toolkit:ExpanderView.HeaderTemplate>
It's a bit tricky to get to the bottom of this. The bottom line is, I think the only way get what you want (stretch the line) is to include a modified version of the control template in your XAML resources, and make the header stretch to fill its container.
If you check source code at the link, and scroll down to the ExpanderView control template, you will see that the header element is defined like this:
<ListBoxItem x:Name="ExpandableContent" controls:TiltEffect.IsTiltEnabled="True"
Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="41"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ContentControl x:Name="Header"
HorizontalContentAlignment="Stretch"
HorizontalAlignment="Stretch"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
Grid.Row="0" Grid.Column="0"
Grid.ColumnSpan="2"/>
<ContentControl x:Name="Expander"
Margin="11,0,0,0"
Grid.Row="1" Grid.Column="1"
HorizontalContentAlignment="Stretch"
HorizontalAlignment="Stretch"
Content="{TemplateBinding Expander}"
ContentTemplate="{TemplateBinding ExpanderTemplate}"/>
<Grid x:Name="ExpanderPanel" Grid.Row="0" Grid.Column="0"
Grid.RowSpan="2" Grid.ColumnSpan="2" Background="Transparent"/>
</Grid>
</ListBoxItem>
Now, at first glance this looks fine, because the "Header" ContentControl has both HorizontalAlignment and HorizontalContentAlignment set to "Stretch". Unfortunately, the parent ListBoxItem does not define its HorizontalContentAlignment, whose default may not be "Stretch". (I'm uncertain on this point because the MSDN documentation does not list the default -- but the default for the corresponding property is "Left" in WPF and "Center" in Silverlight.)
So I would try copying the default control template into your application resources, and add the appropriate alignments to the "ExpandableContent" ListBoxItem:
<ListBoxItem x:Name="ExpandableContent"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" ...

Xaml, middle column not taking up full width

How do I make my middle column take up the full width available while allowing space for the comment section so that all those comment boxes are nicely aligned to the right:
<DataTemplate x:Key="ActivityStreamItemTemplate">
<StackPanel VerticalAlignment="Top" Margin="5,0,0,0">
<Button Command="{Binding Path=DataContext.LoadSpacesCommand, ElementName=OrganisationList}" CommandParameter="{Binding}" Padding="-5,0,-5,-5" Margin="-7,-12,-7,-7" Height="auto" BorderThickness="0" HorizontalAlignment="Left" VerticalAlignment="Stretch" HorizontalContentAlignment="Left" UseLayoutRounding="True" FontSize="0.01">
<Grid Height="auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="67" />
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<StackPanel Height="auto" Grid.Column="0" Background="Transparent">
<Border Background="Transparent" BorderThickness="0" Width="62" Height="62" HorizontalAlignment="Left" Margin="0,0,0,5">
<Image Source="{Binding created_by.image.link}" Width="62" Height="62"></Image>
</Border>
</StackPanel>
<StackPanel Height="auto" Grid.Column="1">
<TextBlock Text="{Binding type}" HorizontalAlignment="Left" FontSize="30" VerticalAlignment="Center" Margin="0,0,0,5" Foreground="White" />
<TextBlock Text="{Binding ttitle}" HorizontalAlignment="Left" FontSize="15" VerticalAlignment="Center" Margin="0,0,0,5" Foreground="White" TextWrapping="Wrap"/>
<TextBlock Text="{Binding created_by.name}" HorizontalAlignment="Left" FontSize="11" VerticalAlignment="Center" Margin="0,0,0,5" Foreground="White" />
</StackPanel>
<StackPanel Height="60" Grid.Column="2" Margin="10,0,0,0">
<StackPanel.Background>
<ImageBrush Stretch="Fill" ImageSource="/Assets/Icons/CommentsIcon.png"/>
</StackPanel.Background>
<TextBlock Text="{Binding comments.Count}" HorizontalAlignment="Center" FontSize="20" Foreground="Black" TextAlignment="Center" Padding="0,8,0,0"/>
</StackPanel>
</Grid>
</Button>
</StackPanel>
</DataTemplate>
I tried placing horizontal align on the third stackpanel but that actually didn't work.
EDIT: Thanks for the tries but no cigar:
You need to alter the style of the ListBoxItem itself to ensure that the content is stretched across the available width.
Define this style:
<Style x:Key="ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
</Style>
Then the Right alignment of the "Comments" image will work and the central text box will stretch to fill the available space.
You might find that just using a StackPanel with an horizontal orientation works better than a Grid for the item template, especially if the data in columns 0 and 2 are a constant width.
Play around with the space given for the columns, for example:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="67" />
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
This gives the center column 3 times more space than the right column
It's hard to tell exactly what you want because of how you've blurred your image. But I think the key is to make the container of the grid take up all available space, HorizontalAlignment="Stretch".
<DataTemplate>
<StackPanel HorizontalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="67" />
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<!-- items here -->
</Grid>
</StackPanel>
The item you set to have Grid.Column="0" will have width 67dip, the one with Grid.Column="2" will be width 60dip, and the one with Grid.Column="1"will fill up the rest of the space.
dip = device independent pixels - all Windows Phone apps are measured as if the screen is 480x800 and then rendered at the actual resolution of the screen.
Inside a StackPanel you can't do HorizontalAlignment to right while its orientation is LeftToRight, as far as I know. Avoid using it.
The problem stems from using a StackPanel as the top-most UIElement. Use a Grid instead and follow the rest of this advice:
Right align content in ListBox
Which leads to this answer as well:
C# windows phone -Alignment in xaml ListBox.ItemTemplate
Your problem is the Button, if it's not mandatory try deleting it and add a "Tap" Event to the StackPanel, i've tried it and it works.
<DataTemplate x:Key="ActivityStreamItemTemplate">
<StackPanel Tap="...">
// no <Button> here
<Grid>
---
</Grid>
// no </Button> here
</StackPanel>
</DataTemplate>
better option
<DataTemplate x:Key="ActivityStreamItemTemplate">
<Grid Tap="...">
...
</Grid>
</DataTemplate>

Categories