WPF: How to reset / recalculate ScrollViewer “ScrollableHeight”
-
20-09-2019 - |
Question
Background:
I am generating the UI for a settings page. The settings are stored within a Dictionary as the settings will be different for each object in question.
Problem:
The ScrollableHeight
of a ScrollViewer
is not acurate to the size of the content. When the content of the ScrollViewer
changes the ScrollableHeight
is not reset, but appends the height of the new content.
When:
I am generating content within a Grid
, which is a child element within the ScrollViewer
. The content being RowDefinitions
where name-value pairs are displayed as TextBlocks
and TextBoxes
. When a different object is selected in order to edit its properties, the Grid's Children are cleared and the UI to display the properties is regenerated. As I mentioned before in the problem defintion, the generated content's height is appended to the ScrollViewer
's ScrollableHeight
property.
What I have learned:
My first thought was to clear the ScrollViewer
's ScrollableHeight
and for each row added append the height of the row in order to achieve the correct size. The issue is that ScrollableHeight
cannot be set (private setter).
Code:
XAML:
<ScrollViewer Name="svScroller" Grid.Row="0">
<Grid x:Name="gdPropertyGrid" Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35*" />
<ColumnDefinition Width="65*" />
</Grid.ColumnDefinitions>
</Grid>
</ScrollViewer>
C#:
//Get Selected Item
var listBox = ((ListBox)sender);
var provider = listBox.SelectedItem as IProviderConfiguration;
if (provider != null)
{
tbTitle.Text = String.Format("Properties for {0}",provider.Name);
int rowCount = 0;
PropertyGrid.Children.Clear();
//Get properties
foreach (var property in provider.Properties)
{
//Create Grid Row
var rowDef = new RowDefinition() {Height = new GridLength(30)};
PropertyGrid.RowDefinitions.Add(rowDef);
//Create Name Label
var tbPropertyName = new TextBlock {
Text = property.Key,
VerticalAlignment = VerticalAlignment.Center
};
//Create Value input
var tbPropertyValue = new TextBox {
Text = property.Value.ToString(),
VerticalAlignment = VerticalAlignment.Center
};
//Add TextBlock & TextBox Grid
PropertyGrid.Children.Add(tbPropertyName);
PropertyGrid.Children.Add(tbPropertyValue);
//Set Grid.Row Attached property
Grid.SetRow(tbPropertyName, rowCount);
Grid.SetRow(tbPropertyValue, rowCount);
Grid.SetColumn(tbPropertyValue, 1);
rowCount++;
}
}
Solution
That is the expected behavior of ScrollViewer.ScrollableHeight, from MSDN:
Gets a value that represents the vertical size of the content element that can be scrolled.
Perhaps you are looking for ScrollViewer.ViewPortHeight? Or maybe you're looking for the ScrollViewer to stretch until a certain point before scrolling. In that case you'll need to look at another solution.
EDIT
The bug is you do not clear the RowDefinitions, hence the ScrollableHeight always appears to be appended to, since you constantly add new rows! My suggestion is you switch to using another ListBox and go with a Master-Detail pattern.
OTHER TIPS
I agree with @sixlettervariables, from your description of the problem it sounds like you are trying to do something with the wrong property. It's not clear to me precisely what your desired outcome is, I assume however that you want to be able to have different numbers of items in the same scroll viewer and have it expand as needed to display as many as possible at runtime.
ScrollViewer.ViewPortHeight lets you set the visible area and is a read/write property, and scrollbars will (by default) appear automatically when the content is too big to fit in the visible area.