How can I get a vertical scrollbar in my ListBox? - wpf

In the example below I have a ListBox with dozens of font names in it.
I would have thought it would automatically have a vertical scrollbar on it so that you can select ANY font, not just the first ones in the list, but it doesn't.
So I added a "ScrollViewer" and that puts a "scrollbar area" on the right but there is no scrollbar in the scrollbar area so that you can scroll (!).
Why isn't a scrollbar automatic and how do I force it to have a scrollbar?
<StackPanel Name="stack1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ScrollViewer>
<ListBox Grid.Row="0" Name="lstFonts" Margin="3" ItemsSource="{x:Static Fonts.SystemFontFamilies}"/>
</ScrollViewer>
</Grid>
</StackPanel>

The problem with your solution is you're putting a scrollbar around a ListBox where you probably want to put it inside the ListBox.
If you want to force a scrollbar in your ListBox, use the ScrollBar.VerticalScrollBarVisibility attached property.
<ListBox
ItemsSource="{Binding}"
ScrollViewer.VerticalScrollBarVisibility="Visible">
</ListBox>
Setting this value to Auto will popup the scrollbar on an as needed basis.

ListBox already contains ScrollViewer. By default the ScrollBar will show up when there is more content than space. But some containers resize themselves to accommodate their contents (e.g. StackPanel), so there is never "more content than space". In such cases, the ListBox is always given as much space as is needed for the content.
In order to calculate the condition of having more content than space, the size should be known. Make sure your ListBox has a constrained size, either by setting the size explicitly on the ListBox element itself, or from the host panel.
In case the host panel is vertical StackPanel and you want VerticalScrollBar you must set the Height on ListBox itself. For other types of containers, e.g. Grid, the ListBox can be constrained by the container. For example, you can change your original code to look like this:
<Grid Name="grid1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" Name="lstFonts" Margin="3"
ItemsSource="{x:Static Fonts.SystemFontFamilies}"/>
</Grid>
</Grid>
Note that it is not just the immediate container that is important. In your example, the immediate container is a Grid, but because that Grid is contained by a StackPanel, the outer StackPanel is expanded to accommodate its immediate child Grid, such that that child can expand to accommodate its child (the ListBox).
If you constrain the height at any point — by setting the height of the ListBox, by setting the height of the inner Grid, or simply by making the outer container a Grid — then a vertical scroll bar will appear automatically any time there are too many list items to fit in the control.

I added a "Height" to my ListBox and it added the scrollbar nicely.

Scroll Bar is added to the List box automatically unless its visibility is set to Hidden. Whenever the size of List Items exceeds the one, which can be shown inside a list box vertical or horizontal list box can be seen during the run time.

In my case the number of items in the ListBox is dynamic so I didn't want to use the Height property. I used MaxHeight instead and it works nicely. The scrollbar appears when it fills the space I've allocated for it.

I was having the same problem, I had a ComboBox followed by a ListBox in a StackPanel and the scroll bar for the ListBox was not showing up. I solved this by putting the two in a DockPanel instead. I set the ComboBox DockPanel.Dock="Top" and let the ListBox fill the remaining space.

XAML ListBox Scroller - Windows 10(UWP)
<Style TargetType="ListBox">
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Visible"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Visible"/>
</Style>

Related

StackPanel interferes with sizing by margin

I have an app with a TabControl. On each TabItem is a DataGrid. The width and height are not set, the sizing is controlled by the margin so it sizes along with the tab.
<TabItem Name="tbRails" Header="Rails">
<DataGrid x:Name="dgRails" Margin="5,30,5,5" ItemSource=...
This is all working fine until I needed to put a CheckBox on one of the tabs. We can only have one child on a TabItem so I added a StackPanel and put in the CheckBox and the DataGrid.
<TabItem Name="tbRails" Header="Rails">
<StackPanel Name="pnlRails" Margin="10">
<CheckBox Name="chkCollapseItems" Content="Collapse Items" Margin="15" Checked="chkCollapseItems_Checked" ... />
<DataGrid x:Name="dgRails" Margin="5,30,5,5" ItemSource=...
After doing this the data grid has no scrollbar and doesn't respond to mouse wheel. I can click on the cell and it gets selected and I can arrow key down until it disappears out of the bottom. The width sizes to the window just like it did before but it appears to be sizing its height to fit the content (about 2600 rows).
Has anyone seen this before and how do we fix it? I can set the height of the grid and it works just fine but it no longer sizes itself to match the parent.
Use a Grid as panel instead of a StackPanel. A stack panel will measure its children with positive infinity, which does not restrict their height. In other words, the DataGrid will be scaled to display all its records and therefore there will be no scroll viewer.
Using a Grid with the RowDefinitions below, the CheckBox will size to fit its content and the DataGrid will get the rest of the available space in the TabItem.When this available space is not enough to display all records, it will automatically display a scrollbar.
<TabItem Name="tbRails" Header="Rails">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" Name="chkCollapseItems" Content="Collapse Items" Margin="15" Checked="chkCollapseItems_Checked" ... />
<DataGrid Grid.Row="1" x:Name="dgRails" Margin="5,30,5,5" ItemSource= ... />
</Grid>
</TabItem>

How to force Grid to shrink an auto sized row that contains ScrollViewer (when needed)?

How would one create this layout?:
The idea is to keep the blue text always just below the green element BUT when there is not enough space, the green element should be placed inside a scrollable container (with vertical scrollbar visible).
I have tried StackPanel and Grid and both failed. The StackPanel does not shrink rows at all. The Grid does the same if the top RowDefinition's height is set to "auto" (the ScrollViewer inside does not show its scrollbar). If it's set to a star, the blue text is moved down to at least half of the space (depends of the bottom row's height setting). The bottom row's height can be either an "auto" or a star with VerticalAlignment of the blue text set to Top BUT the row's height must be kept at least as big as the text's. Of course the whole area (black box) cannot be stretched by its content - it's determined by the size of the window.
How? Please keep the responses to be based on XAML if possible.
Inspired by Eirik's approach of constraining a greedy container by putting it inside other tight container, I've discovered a really simple way to achieve what I wanted. I only needed a container that shrinks its one child first, then (when the first one completely disappears) the second one. And there is such container: the DockPanel. Here goes:
<DockPanel LastChildFill="False">
<DockPanel DockPanel.Dock="Top">
<TextBlock DockPanel.Dock="Bottom" TextWrapping="Wrap" Grid.Row="1">Automatically wrapped text of unknown length.</TextBlock>
<ScrollViewer>
<TextBlock TextWrapping="Wrap">In this case the element is too big to fit inside whole space (the black box) with the blue text below. I want the scrollbar to be shown instead of moving the blue text outside of the black box (and clipped)</TextBlock>
</ScrollViewer>
</DockPanel>
</DockPanel>
As simple as that! :) I hope it helps someone.
<Grid Name="outerGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Canvas>
<Grid MaxWidth="{Binding ElementName=outerGrid, Path=ActualWidth}" MaxHeight="{Binding ElementName=outerGrid, Path=ActualHeight}">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ScrollViewer Width="{Binding ElementName=outerGrid, Path=ActualWidth}">
<TextBlock TextWrapping="Wrap">In this case the element is too big to fit inside whole space (the black box) with the blue text below. I want the scrollbar to be shown instead of moving the blue text outside of the black box (and clipped)</TextBlock>
</ScrollViewer>
<TextBlock TextWrapping="Wrap" Grid.Row="1">Automatically wrapped text of unknown length.</TextBlock>
</Grid>
</Canvas>
</Grid>
The auto height will grow to match the height of the content of that row.
The star height will let that row grow in height to fill the rest of the height of the grid, preventing the ScrollViewer to grow more than what's visible.
Edit: If you put the Grid inside another Grid like the XAML above you should get the behavior you want.
The second row of the outer row acts as a "filler" to fill the rest of the space the outer Grid.
Edit 2: Try the edited XAML above. I've put the inner Grid inside a Canvas (to prevent clipping) and bound the MaxWidth and MaxHeight of the inner Grid to the ActualWidth and ActualHeight of the outer Grid to keep the inner Grid the same size as the outer Grid.
Edit 3: Added binding to the Width of the ScrollViewer to keep it the same width as the rest.

WP7: ScrollViewer viewport size

Windows Phone 7. I have a ScrollViewer inside a StackPanel inside a PivotItem inside a Pivot. Above the ScrollViewer, there are some other controls. My intention is that the ScrollViewer takes the available lower part of the screen (~400px), and its content is scrolled vertically (content height ~800px).
Now, right now there's no vertical scrolling - when I try to drag, the view returns in the previous position, as if the viewport size exactly matches the content size. When I look at the ViewportHeight property, it's ~800px - same as content.
Height of the ScrollViewer is not set ("Auto"); I was assuming it would take exactly the available space. That's obviously not the case. Question - short of setting Height by hand, is there a way to implement the logic of "viewport height is exactly how much vertical space you've got left"?
EDIT: here's the XAML, irrelevant details removed:
<Pivot x:Name="Root">
<ctls:PivotItem>
<ctls:PivotItem.Header>Title</ctls:PivotItem.Header>
<StackPanel>
<!-- More stuff here-->
<ScrollViewer Name="MenuPanel" HorizontalScrollBarVisibility="Disabled">
<Canvas x:Name="Menu" HorizontalAlignment="Left" VerticalAlignment="Top">
</Canvas>
</ScrollViewer>
</StackPanel>
</ctls:PivotItem>
</Pivot>
Width and height of the canvas are set in code.
Two things:
A StackPanel doesn't allow it's children to automatically take up the rest of the space available. Use a Grid, instead, with defined Rows. This allows your ScrollViewer to be in a container which is the exact height remaining vertically.
Your Canvas (inside the ScrollViewer) is aligned to top and left, and without a size defined, is exactly 0 pixels high and 0 pixels wide.
Good luck.
<Pivot x:Name="Root">
<ctls:PivotItem>
<ctls:PivotItem.Header>Title</ctls:PivotItem.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<!-- More stuff here-->
</Grid>
<ScrollViewer
Grid.Row="1"
Name="MenuPanel">
<Canvas x:Name="Menu"
Height="500"
Width="500"/>
</ScrollViewer>
</StackPanel>
</ctls:PivotItem>
</Pivot>
Without seeing your XAML this is assummed - but based on commonly seen issues
The ScrollViewer is actually being assigned all the space it needs to include all it's content items.
Either give it an absolute height or wrap it in a Grid, which will limit it to the available space within the StackPanel.

WPF ListView no scrollbar if height set to auto

Hi i have a ListView that binds to a collection. I set the height of the ListView to auto for it to take up all the space in the region. However there is not scrollbar after i set the height to auto. If i give it a height then the scrollbar would show up.
the markup is pretty much like the following
<Grid>
<StackPanel>
<Expander>
<DataGrid>
<Expander>
<ListView>
I have a hunch that your ListView is inside a panel that allows it to expand vertically without limit.
If you put a ListView inside a StackPanel, for example, the ListView's height can exceed the height of the StackPanel. The ListView has increased its height to show all its items, as far as it's concerned, thus no scrollbar.
However, if you change that StackPanel to a Grid, where controls automatically try to fit themselves inside that area, the ListView will automatically have a scrollbar when it contains more items than it can display.
This will probably be solved most simply by adjusting your layout that contains the ListView.
Instead of setting
<RowDefinition Height="auto"/>
set:
<RowDefinition Height="1*"/>
Why should it show a scrollbar if there is nothing to scroll?
If you want to override any default behavior you can set ScrollViewer.VerticalScrollBarVisibility="Visible" on the ListBox.

WPF - DockPanel Question

I am very new to WPF and am trying to understand the DockPanel control. I am building a Window, and am using a DockPanel in order to dock controls the way I want them. However, is it possible to dock the DockPanel itself, say to the top of the Window? I can't seem to find a way to do this. Ideally I would like to have the DockPanel dock itself to the top of my Window so that when the Window is resized, it grows or shrinks as the window is resized horizontally.
The problem is I cannot find a Doc property on the DockPanel itself. Is this not possible to do?
You would need to place it within another DockPanel if you wished to dock it.
That being said, my experience has been that when a layout gets to the level of complexity you are describing, it's often better to switch to using a Grid control. This gives you a huge amount of control over layout, and makes having dynamically growing rows/columns (with fixed size portions between) easy.
Edit in response to comments:
You can use a Grid that scales as needed. For example, by using star syntax, you can have a grid where the top row is always 1/3rd of the window size, and the bottom row is 2/3rds, like so:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
</Grid>
For details on options for sizing, see GridLength. You can use "Auto" (fit to contents), "*" to scale based on space, or a fixed height (put in a number).
I'm not sure if this what you want but I have a DockPanel with a Menu. When I resize the window the menu resizes with it.
Here is my code:
<Grid>
<DockPanel Height="50" Margin="0" Name="MenuDockPanel" VerticalAlignment="Top" Width="Auto">
<Menu Height="23" Name="MenuPanel" Width="Auto" VerticalAlignment="Top">
<MenuItem Header="_File">
</Menu>
</DockPanel>
<Grid>
Hope this helps

Resources