.net Framework source code ScrollViewer

This article is a series of analyzing the source code of .net Framework, mainly to tell you Microsoft's idea of ​​​​making ScrollViewer, the analysis is very simple

After reading this article, you can learn how to write a ScrollViewer, how to define an IScrollInfo or add animation to his scrolling

use

The following will tell you how to use ScrollViewer simply. Generally, scrolling can be achieved by placing a ScrollViewer outside the control that needs to be scrolled.

  <ScrollViewer HorizontalScrollBarVisibility="Auto">
    <StackPanel VerticalAlignment="Top" HorizontalAlignment="Left">
      <TextBlock TextWrapping="Wrap" Margin="0,0,0,20">Scrolling is enabled when it is necessary. 
      Resize the window, making it larger and smaller.</TextBlock>
      <Rectangle Fill="Red" Width="500" Height="500"></Rectangle>
    </StackPanel>
  </ScrollViewer>

But not all controls can be scrolled by putting a ScrollViewer outside, because scrolling actually requires the control to do it by itself.

principle

Let me tell you how to scroll.

One of the easiest ways is to set the element transForm.Y's scrolling in this way is the easiest way, but the disadvantage is that other controls cannot do other movements.

There are two scrolling methods in ScrollViewer, physical scrolling and logical scrolling. If physical scrolling is used, scrolling is done by ScrollViewer. How to use logical scrolling, then scrolling is done by the control itself.

So I start with the ScrollViewer receiving input

enter

If you use ScrollViewer for scrolling, then you may encounter a magical requirement, how to scroll under touch. Yes, if using a simple ScrollViewer it is not possible to scroll with touch

Please look at the code, write a simple ScrollViewer with some rectangles in it, you can see that you can scroll with the mouse at this time, but you can't scroll by touch.

    <Grid>
        <ScrollViewer>
            <StackPanel x:Name="HcrkKmqnnfzo"></StackPanel>
        </ScrollViewer>
    </Grid>

Iterate over the colors in the background then add

        public MainWindow()
        {
            InitializeComponent();

            foreach (var temp in typeof(Brushes)
                .GetProperties(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
                .Select(temp => temp.GetValue(null, null)))
            {
                var rectangle = new Rectangle
                {
                    Height = 20,
                    Fill = (Brush)temp
                };

                HcrkKmqnnfzo.Children.Add(rectangle);
            }
        }

Code: WPF ScrollView Code Explanation 1.1-CSDN Download

If you don't have csdn points, try to use my network disk , but if my network disk expires, please let me know

If you need to use scrolling on touch, then you need to set PanningMode, you can set to support vertical drag.

If it is set at this time PanningMode, you will find that the window will shake when dragging. At this time, you need to rewrite OnManipulationBoundaryFeedback in the window, please see the following code. Don't write anything in the function. For details, please see https://stackoverflow.com/a/6918131/6116637

       protected override void OnManipulationBoundaryFeedback(ManipulationBoundaryFeedbackEventArgs e)
        {
        }

Modified code: WPF ScrollView Code Explanation 1.2 - CSDN Download

So how does the mouse scroll receive the scroll?

From the Microsoft source code, you can see that ScrollViewer inherits ContentControl, so you can override OnMouseWheel, please see his code

      protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            if (e.Handled) { return; }
 
            if (!HandlesMouseWheelScrolling)
            {
                return;
            }
 
            if (ScrollInfo != null)
            {
                if (e.Delta < 0) { ScrollInfo.MouseWheelDown(); }
                else { ScrollInfo.MouseWheelUp(); }
            }
 
            e.Handled = true;
        }

In fact, ScrollViewer does not scroll, the actual scrolling is ScrollInfo scrolling.

ScrollInfo

So what is ScrollInfo, in fact it is an interface, the controls placed in ScrollViewer are not actually placed directly in ScrollViewer, the controls are placed ScrollContentPresenter, and ScrollContentPresenter is written in the Style of ScrollViewer, you can see this code in ScrollViewer

[TemplatePart(Name = "PART_ScrollContentPresenter", Type = typeof(ScrollContentPresenter))]

But as you can see from the rubbish Microsoft code, there is no attribute to use this directly, but it is written like this where it is usedGetTemplateChild(ScrollContentPresenterTemplateName) as ScrollContentPresenter;

This write performance is relatively poor.

So how does he assign values ​​to ScrollInfo? In fact, HookupScrollingComponents in this class is to assign a value to ScrollInfo, and the place where HookupScrollingComponents is called is OnApplyTemplate, so you can see that the control is already known when it is initialized.

You can see the logic of HookupScrollingComponents from the garbage Microsoft's source code. The first is to judge CanContentScrollwhether the controls in the element can be scrolled. If the controls in the element can be scrolled, then judge whether the controls in the element are inherited IScrollInfo. If so, um, If not, assign a value to ScrollInfo. If the control inside is not inherited IScrollInfo, then judge whether it is in the list, and if so, take the list ItemsPresenteras ScrollInfo. If you still can't get it, you have to use yourself asScrollInfo

From here, you can see that if CanContentScroll is not set, this class is used directly, that is, physical scrolling is done by this class. If an element is not in a list and does not inherit IScrollInfo then even if it is set to use logical scrolling, it is actually physical scrolling. Physical scrolling means that the element does not know how to scroll, and all movement is beyond the control of the element. Unlike physical scrolling, the logic is that the element controls all scrolling.

physical scrolling

Let me tell you how the physical scrolling is done. The actual scrolling is to use the following code in the layout to make the element layout in the scrolling place, so it looks like the element scrolling

                  Rect childRect = new Rect(child.DesiredSize);
 
                        if (IsScrollClient)
                        {
                            childRect.X = -HorizontalOffset;
                            childRect.Y = -VerticalOffset;
                        }
 
                        //this is needed to stretch the child to arrange space,
                        childRect.Width = Math.Max(childRect.Width, arrangeSize.Width);
                        childRect.Height = Math.Max(childRect.Height, arrangeSize.Height);
 
                        child.Arrange(childRect);

You can see that the HorizontalOffset of the layout setting is reversed as the x movement of the element, so that the element can be moved

But if the element is moved outside the ScrollViewer, how can it be cropped? In fact, it is to use the overridden GetLayoutClip for clipping

 return new RectangleGeometry(new Rect(RenderSize));

It can be known from the code that the actual ScrollViewer does not scroll the elements. The scrolling elements are the elements in the ScrollViewer. The scrolling method generally uses the X and Y of the elements set during the layout to make the elements scroll. I saw StackPanel and several other classes, all use this method, because compared to the way of Translate, this method will not use Translate and will not be unable to move when the user modifies the Translate. In addition, this method is done in the layout and calculated directly. If the Translate is modified, it needs to be recalculated in the layout, so the performance of this method will be relatively high.

touch input

So how does ScrollViewer get input when touched? In fact, Manipulation is used when touching, and the value of PanningMode is judged.

                    if (panningMode == PanningMode.HorizontalOnly)
                    {
                        e.Mode = ManipulationModes.TranslateX;
                    }
                    else if (panningMode == PanningMode.VerticalOnly)
                    {
                        e.Mode = ManipulationModes.TranslateY;
                    }
                    else
                    {
                        e.Mode = ManipulationModes.Translate;
                    }

Therefore, in ManipulationDelta, you can get the moving value, because the value directly obtained is the path the user wants, so the direct setting does not need to be calculated.

But the multiple PanningRatio is needed, if inertia is needed, then just set the inertia.

This is probably the whole source code. A lot of the code is judging boundaries and processing some user input.

At the time of touch, the core code is ManipulateScroll, which passes in the current movement and accumulated movement, and whether it moves horizontally. By judging whether the current movement has a movement and multiplying it by a multiple, and then by setting the values ​​of the HorizontalOffset properties, you can re-layout.

So all the code is actually to get the input, and then pass it to the corresponding ScrollInfo, and do specific business through the method implemented by ScrollInfo.

However, ScrollViewer does not directly pass in ScrollInfo and needs to move, and sends commands

     
        public void ScrollToHorizontalOffset(double offset)
        {
            double validatedOffset = ScrollContentPresenter.ValidateInputOffset(offset, "offset");
 
            // Queue up the scroll command, which tells the content to scroll.
            // Will lead to an update of all offsets (both live and deferred).
            EnqueueCommand(Commands.SetHorizontalOffset, validatedOffset, null);
        }
 

Then in the specific function ExecuteNextCommand, take out the commands one by one and move them

     private bool ExecuteNextCommand()
        {
            IScrollInfo isi = ScrollInfo;
 
            Command cmd = _queue.Fetch();
            switch(cmd.Code)
            {
                case Commands.LineUp:    isi.LineUp();    break;
                case Commands.LineDown:  isi.LineDown();  break;
                case Commands.LineLeft:  isi.LineLeft();  break;
                case Commands.LineRight: isi.LineRight(); break;
                //去掉差不多的代码
                case Commands.Invalid: return false;
            }
            return true;
        }

When inputting, the input may be too fast, and the layout is not immediately laid out. As can be seen from the code, the mobile business is to modify the value in the layout, but the layout modification is not a high priority, but the input priority is very high , you may keep typing during the layout process. So you need to put the input commands in, use a function to take them out one by one, process different commands, and finally lay them out.

See also:

Smooth scrolling in WPF

IScrollInfo in Avalon part I - BenCon's WebLog

IScrollInfo in Avalon part II - BenCon's WebLog

IScrollInfo in Avalon part III - BenCon's WebLog

IScrollInfo tutorial part IV - BenCon's WebLog

Other source code analysis

.net Framework source code ScrollViewer

.net source code analysis – List

One-stop WPF--DependencyProperty 1- Zhou Yongheng- Blog Park

I built my own blog https://lindexi.gitee.io/ Welcome everyone to visit, there are many new blogs in it. I will only put it on csdn or blog garden after I see that the blog is mature, but it will not be updated once it is published

If you see anything you don’t understand on the blog, welcome to communicate. I have built a dotnet vocational and technical college. Welcome everyone to join.

Creative Commons License
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. You are welcome to reprint, use, and republish, but be sure to keep the article's signature Lin Dexi (including the link: http://blog.csdn.net/lindexi_gd ), and must not be used for commercial purposes. Works modified based on this article must be released with the same license. If you have any questions, please contact me .

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325031459&siteId=291194637