Scrollviewer not staying at the bottom when new insert. MVVM .NET Frameworks 4.5 XAML
I have the following xaml.
This works fine, HOWEVER, whenever a new message is inserted into the ItemsControl my scrollviewer doesn't update and stays in plays. This means that new messages can't be seen unless your scroll down manually and I cannot for the life of me figure out why it won't stay in its relative position.
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border x:Name="MessageBorder" MinHeight="40" MinWidth="280" BorderThickness="1"
Background="#EFEBE9" BorderBrush="#BCAAA4"
Margin="10,0,60,10" CornerRadius="4" SnapsToDevicePixels="True" HorizontalAlignment="Left">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition Height="15"/>
</Grid.RowDefinitions>
<TextBlock x:Name="MessageTxtBlock" Grid.Row="1" Margin="7,5,7,0" TextWrapping="Wrap"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Text="{Binding Message}"/>
<TextBlock Grid.Row="2" HorizontalAlignment="Right" VerticalAlignment="Stretch"
Margin="0,0,5,0" FontSize="10" Opacity="0.8"
Text="{Binding Time, StringFormat={}{0:t}}"/>
</Grid>
</Border>
<DataTemplate.Triggers>
omitted for brevity
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ScrollViewer x:Name="MessageScroller" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border x:Name="MessageBorder" MinHeight="40" MinWidth="280" BorderThickness="1"
Background="#EFEBE9" BorderBrush="#BCAAA4"
Margin="10,0,60,10" CornerRadius="4" SnapsToDevicePixels="True" HorizontalAlignment="Left">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition Height="15"/>
</Grid.RowDefinitions>
<TextBlock x:Name="MessageTxtBlock" Grid.Row="1" Margin="7,5,7,0" TextWrapping="Wrap"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Text="{Binding Message}"/>
<TextBlock Grid.Row="2" HorizontalAlignment="Right" VerticalAlignment="Stretch"
Margin="0,0,5,0" FontSize="10" Opacity="0.8"
Text="{Binding Time, StringFormat={}{0:t}}"/>
</Grid>
</Border>
<DataTemplate.Triggers>
omitted for brevity
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ScrollViewer x:Name="MessageScroller" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
3 Replies
You need to code it so that it does.
Rules of WPF:
❌ Avoid the WPF Designer to eliminate a category of confusing bugs
❌ Don't rely on Margin as the primary tool for layouts
❌ Avoid writing UserControls or subclassing to extend a default control -- use Behaviors instead (Microsoft.Xaml.Behaviors.Wpf)
✅ Write XAML by hand and autoformat with "Ctrl K,D" or XAML Styler
✅ Rely upon XAML Hot Reload to design your app's UI at runtime
✅ Use layout controls (Grid, DockPanel, etc) to support proper resizing
✅ Use data binding to eliminate glue code and state synchronization issues
✅ Use collection controls and DataTemplate to dynamically create lists of controls
✅ Learn MVVM to create maintainable apps
✅ Use the Dispatcher to update controls from non-UI threads
✅ WPF's default controls can be easily modernized via $wpfuilibs
✅ Include relevant XAML, code-behind, and ViewModel code for questions when possible
Rules of WPF:
❌ Avoid the WPF Designer to eliminate a category of confusing bugs
❌ Don't rely on Margin as the primary tool for layouts
❌ Avoid writing UserControls or subclassing to extend a default control -- use Behaviors instead (Microsoft.Xaml.Behaviors.Wpf)
✅ Write XAML by hand and autoformat with "Ctrl K,D" or XAML Styler
✅ Rely upon XAML Hot Reload to design your app's UI at runtime
✅ Use layout controls (Grid, DockPanel, etc) to support proper resizing
✅ Use data binding to eliminate glue code and state synchronization issues
✅ Use collection controls and DataTemplate to dynamically create lists of controls
✅ Learn MVVM to create maintainable apps
✅ Use the Dispatcher to update controls from non-UI threads
✅ WPF's default controls can be easily modernized via $wpfuilibs
✅ Include relevant XAML, code-behind, and ViewModel code for questions when possible
The answer to my own question
added this event handler that checks if scroll bar is at the bottom, if it is it enables auto scroll is not it stays
Added the ScrollChanged which fires the event
private void scrollviewer_Messages_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
ScrollViewer scrollViewer = sender as ScrollViewer;
if (e.ExtentHeightChange == 0)
{ // Content unchanged : user scroll event
if (scrollViewer.VerticalOffset == scrollViewer.ScrollableHeight)
{ // Scroll bar is in bottom
// Set auto-scroll mode
AutoScroll = true;
}
else
{ // Scroll bar isn't in bottom
// Unset auto-scroll mode
AutoScroll = false;
}
}
// Content scroll event : auto-scroll eventually
if (AutoScroll && e.ExtentHeightChange != 0)
{ // Content changed and auto-scroll mode set
// Autoscroll
scrollViewer.ScrollToVerticalOffset(scrollViewer.ExtentHeight);
}
}
private void scrollviewer_Messages_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
ScrollViewer scrollViewer = sender as ScrollViewer;
if (e.ExtentHeightChange == 0)
{ // Content unchanged : user scroll event
if (scrollViewer.VerticalOffset == scrollViewer.ScrollableHeight)
{ // Scroll bar is in bottom
// Set auto-scroll mode
AutoScroll = true;
}
else
{ // Scroll bar isn't in bottom
// Unset auto-scroll mode
AutoScroll = false;
}
}
// Content scroll event : auto-scroll eventually
if (AutoScroll && e.ExtentHeightChange != 0)
{ // Content changed and auto-scroll mode set
// Autoscroll
scrollViewer.ScrollToVerticalOffset(scrollViewer.ExtentHeight);
}
}
<ScrollViewer x:Name="MessageScroller" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" ScrollChanged="scrollviewer_Messages_ScrollChanged">
<ItemsPresenter />
</ScrollViewer>
<ScrollViewer x:Name="MessageScroller" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" ScrollChanged="scrollviewer_Messages_ScrollChanged">
<ItemsPresenter />
</ScrollViewer>