WPF implements ScrollViewer scrolling to the specified control

  • 2021-10-11 18:04:40
  • OfStack

In the development of front-end UI, sometimes, we will encounter such requirements: there are many contents in an ScrollViewer, and we need to be able to locate the specified controls after performing an operation; This is much like clicking a link on an HTML page and navigating to an anchor on the current page.

To implement it, we first need to look at the API provided by ScrollViewer, which does not have a method similar to ScrollToControl; Of its several methods that begin with ScrollTo, the most appropriate one is ScrollToVerticalOffset, which takes one parameter, the vertical offset position. So, the important question is: How do we get the location of the control we want to locate in ScrollViewer?

In this article I wrote before: XAML: Get the position of the element, and how to get the relative position of the element. I suggest you first understand 1, which uses Visual. TransformToVisual method and so on. When you understand this article, it is easy to look back at the rest of this article.

Next, we can realize the above requirements by using the following code:


//  Gets the preceding  ScrollViewer  Current scroll position 
 var currentScrollPosition = ScrollViewer.VerticalOffset;
 var point = new Point(0, currentScrollPosition);

 //  Calculate the target position and scroll 
 var targetPosition = TargetControl.TransformToVisual(ScrollViewer).Transform(point);
 ScrollViewer.ScrollToVerticalOffset(targetPosition.Y);

In addition, because we usually use MVVM mode, we can encapsulate the above code into an Action, and avoid adding the above code in Code-Behind code file.

The newly created Action named ScrollToControlAction defines two dependency properties, ScrollViewer and TargetControl, which represent the specified ScrollViewer to operate on and the control to navigate to, respectively, and then places the above code in its Invoke method. Since Action is not the subject of this article, it will not be explained much here. You can refer to the following code or Demo provided later in this article for further understanding.


namespace ScrollTest
{
 /// <summary>
 ///  In  ScrollViewer  Navigates to the specified control in the 
 ///  Description: Vertical scrolling is currently supported 
 /// </summary>
 public class ScrollToControlAction : TriggerAction<FrameworkElement>
 {
 public static readonly DependencyProperty ScrollViewerProperty =
 DependencyProperty.Register("ScrollViewer", typeof(ScrollViewer), typeof(ScrollToControlAction), new PropertyMetadata(null));

 public static readonly DependencyProperty TargetControlProperty =
 DependencyProperty.Register("TargetControl", typeof(FrameworkElement), typeof(ScrollToControlAction), new PropertyMetadata(null));

 /// <summary>
 ///  Objectives  ScrollViewer
 /// </summary>
 public ScrollViewer ScrollViewer
 {
 get { return (ScrollViewer)GetValue(ScrollViewerProperty); }
 set { SetValue(ScrollViewerProperty, value); }
 }

 /// <summary>
 ///  The control to navigate to 
 /// </summary>
 public FrameworkElement TargetControl
 {
 get { return (FrameworkElement)GetValue(TargetControlProperty); }
 set { SetValue(TargetControlProperty, value); }
 }

 protected override void Invoke(object parameter)
 {
 if (TargetControl == null || ScrollViewer == null)
 {
 throw new ArgumentNullException($"{ScrollViewer} or {TargetControl} cannot be null");
 }

 //  Checks whether the specified control is in the specified  ScrollViewer  Medium 
 // TODO:  Here just specify the nearest  ScrollViewer , and didn't continue to look up 
 var container = TargetControl.FindParent<ScrollViewer>();
 if (container == null || container != ScrollViewer)
 {
 throw new Exception("The TargetControl is not in the target ScrollViewer");
 }

 //  Gets the preceding  ScrollViewer  Current scroll position 
 var currentScrollPosition = ScrollViewer.VerticalOffset;
 var point = new Point(0, currentScrollPosition);

 //  Calculate the target position and scroll 
 var targetPosition = TargetControl.TransformToVisual(ScrollViewer).Transform(point);
 ScrollViewer.ScrollToVerticalOffset(targetPosition.Y);
 }
 }
}

Its use method is as follows:


<Button>
 <i:Interaction.Triggers>
  <i:EventTrigger EventName="Click">
  <local:ScrollToControlAction ScrollViewer="{Binding ElementName=s}" TargetControl="{Binding ElementName=txtSectionC}" />
  </i:EventTrigger>
 </i:Interaction.Triggers>
</Button>

At this point, with Action, we have implemented the requirements proposed in this article in a very flexible way.

Source download


Related articles: