Scroll position within a ScrollViewer? - wpf

I have a form that with a TabControl that contains a number of TabItems. Each of the TabItems contains a ScrollViewer that contains some various contained content.
My problem - if the size of the form and the TabControl and the contained content is such that the ScrollViewer displays its vertical scrollbars, the content is displayed vertically centered within the ScrollViewer. In order to see the top of the content, the user needs to manually scroll to the top.
Oddly enough, when the size is such that the Horizontal scrollbars are displayed, the content is initially aligned to the left, which is what I want.
How do I make these ScrollViewers open with the scrollposition initialized to the top?
<ParentUserControl>
<Grid>
<TabControl>
<TabItem>
<ScrollViewer>
<Grid>
// Assorted Junk
</Grid>
</ScrollViewer>
</TabItem>
<TabItem>
<ScrollViewer>
<ChildUserControl />
</ScrollViewer>
</TabItem>
<TabItem>
<ScrollViewer>
<OtherChildUserControl />
</ScrollViewer>
</TabItem>
</TabControl>
</Grid>
</ParentUserControl>

I think what's happening is the ScrollIntoView event is being fired. This solution worked for me in the past: Stop WPF ScrollViewer automatically scrolling to perceived content

Related

WPF Bottomless DataGrid keeps expanding downward

I've got this Grid which contains a TabControl with a number of TabItem.
Each TabItem loads a UserControl.
<Grid>
<TabControl>
<TabItem>
<local:UsersControl/>
</TabItem>
</TabControl>
</Grid>
In this specific UserControl I placed a Grid with nested a DockPanel, left side for menu, right for content.
On the right side are loaded a Border containing a single UserControl relative to the selection on the menu on the left.
<Grid>
<DockPanel>
<Border>
<UserControl Content="{Binding MainDock, UpdateSourceTrigger=PropertyChanged}"/>
</Border>
</DockPanel>
</Grid>
Finally, the UserControl consists of a StackPanel which contains a TextBlock and a ScrollViewver with inside a DataGrid.
<StackPanel>
<TextBlock/>
<ScrollViewer>
<DataGrid>
<!-- datagrid stuff -->
</DataGrid>
</ScrollViewer>
</StackPanel>
The problem consist in the latest StackPanel being bottomless and expanding well over the bottom of the window application, which never activate the ScrollViewer hiding part of the grid data, when there are enough rows.
Any idea how to solve this?

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 do you control the height of a scrollviewer inside a tabitem?

I am having trouble getting the height of a scrollviewer to adjust to whatever the height is of a tabitem.
XAML:
<TabItem header="Item">
<ScrollViewer Height={Binding ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=TabItem}}>
<Grid>
.
.
.
</Grid>
</ScrollViewer>
</TabItem>
When I do this the scrollviewer is only about 50 pixels high. The content inside displays fine. The viewing area height is limited.
I tried this.
<TabItem header="Item" x:Name="itemname">
<ScrollViewer Height="{Binding ElementName="itemname", Path=ViewportHeight}">
The scrollviewer is not limited but when the content inside the
scrollviewer grows it goes beyond the viewing area and the scrollbar
deos not work.
I have used the first (FindAncestor) example with a StackPanel and it works. Not sure why it won't with a TabItem.
Why not use the HorizontalAlignment / VerticalAlignment properties?
<ScrollViewer HorizontalAlignment="Stretch" VerticalAlignment="Stretech">

DataGrid-like scrolling behaviour

I need to implement control with header and items pane. This control should have scrolling behaviour similar to DataGrid. Header should scroll horizontally along with items, but should be fixed while vertical scrolling. How do I implement such behaviour?
I would use two ScrollViewers, not one
Here's the control layout I would use
<DockPanel>
<ScrollViewer x:Name="HeaderScrollViewer" DockPanel.Dock="Top">
<Grid x:Name="Headers" />
</ScrollViewer>
<ScrollViewer x:Name="ContentScrollViewer">
<Grid x:Name="Content" />
</ScrollViewer>
</DockPanel>
There are a few things to note to make this work correctly.
Hide the ScrollBars on the HeaderScrollViewer. The ScrollViewer is only there for the functionality - the User won't actually interact with it.
When the ContentScrollViewer scrolls horizontally, manually scroll the HeaderScrollViewer the same distance.
Use Grid.IsSharedSizeScope and Grid.SharedSizeGroup to align your header Grid columns with your content Grid columns

Unable to get vertical scroll bars in an WPF TextBlock

I'm presenting text in a wpf TextBlock control (.Net 3.5). The content of the textblock varies depending on what the user selects in a list box. The text wraps, so I don't need an horizontal scroll bar. However, there is often more text than the amount the window can display, so I need a vertical scroll bar.
As I started searching I quickly found that the answer is to wrap the TextBlock in a ScrollViewer. However, It Does Not Work (TM) and I'm hoping someone can help me work out why.
This is the structure of the UI code:
<Window x:Class=..>
<StackPanel>
<ListBox HorizontalAlignment="Stretch"
VerticalAlignment="Top" Height="200"
SelectionChanged="listbox_changed" SelectionMode="Single">
</ListBox>
<Button Click="Select_clicked">Select</Button>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<TextBlock Name="textblock" TextWrapping="Wrap"/>
</ScrollViewer>
</StackPanel>
</Window>
When the user selects an item in the list box, some text associated with this item is presented in the TextBlock. I would have thought that the code as it stands should have been all that's required, but it never provides me with a scroll bar.
Searching and experimenting have given me two clues: the root of the problem might be related to me updating the content of the TextBlock dynamically, and that the TextBlock does not resize itself based on the new content. I found a posting that seemed relevant that said that by setting the Height of the TextBlock to its ActualHeight (after having changed its content), it would work. But it didn't (I can see no effect of this).
Second, if I set the height (during design time) of the ScrollViewer, then I do get a vertical scroll bar. For instance, if I set it to 300 in the xaml above, the result is almost good in that the window as first opened contains a TextBlock with a vertical scroll bar when (and only when) I need it. But if I make the window larger (resizing it with the mouse during runtime), the ScrollViewer does not exploit the new window size and instead keeps its height as per the xaml which of course won't do.
Hopefully, I've just overlooked something obvious..
Thanks!
Because your ScrollViewer is in a StackPanel it will be given as much vertical space as it needs to display it's content.
You would need to use a parent panel that restricts the vertical space, like DockPanel or Grid.
<DockPanel>
<ListBox DockPanel.Dock="Top" HorizontalAlignment="Stretch"
VerticalAlignment="Top" Height="200"
SelectionChanged="listbox_changed" SelectionMode="Single">
</ListBox>
<Button DockPanel.Dock="Top" Click="Select_clicked">Select</Button>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<TextBlock Name="textblock" TextWrapping="Wrap"/>
</ScrollViewer>
</DockPanel>

Resources