WPF自定义控件(教程含源码)- 实现可以平滑滚动的Panel 控件

控件需求

WPF 控件提供了 ScrollViewer 来实现滚动视图。ScrollViewer 的 PageDown、PageUp、PageLeft、PageRight 方法可以滚动一个视图大小的尺寸,但是 ScrollViewer 滚动时比较僵硬,没有动画效果。

控件设计方案

平滑滚动控件命名为 RWrapPanel,继承 WrapPanel类和 IScrollInfo 接口。将WrapPanel 放入 ScrollViewer 并将 ScrollViewer 的 CanContentScroll 设置为 True(即按逻辑单元滚动),RWrapPanel 控件内部自己管理滚动逻辑。

最终效果

WPF自定义控件(教程含源码)- 实现可以平滑滚动的Panel 控件_第1张图片

平滑滚动的WrapPanel

 

控件代码

    public class RWrapPanel : WrapPanel, IScrollInfo {
        const double lineOffset = 30;
        const double wheelOffset = 90;

        const double duration = 0.2;

        TranslateTransform transForm;

        public RWrapPanel() {
            transForm = new TranslateTransform();
            this.RenderTransform = transForm;
        }

        #region Layout
        Size screenSize;
        Size totalSize;

        protected override Size MeasureOverride(Size constraint) {
         

            this.screenSize = constraint;


            if (this.Orientation == Orientation.Horizontal) {
                constraint = new Size(double.PositiveInfinity, constraint.Height);
            } else {
                constraint = new Size(constraint.Width, double.PositiveInfinity);
            }

            totalSize = base.MeasureOverride(constraint);

            return totalSize;
        }

        protected override Size ArrangeOverride(Size finalSize) {
            var size = base.ArrangeOverride(finalSize);

            if (ScrollOwner != null) {
                if (this.Orientation == Orientation.Horizontal) {
                    var xOffsetAnimation = new DoubleAnimation() {
                        To = -HorizontalOffset,
                        Duration = TimeSpan.FromSeconds(duration),
                    };
                    transForm.BeginAnimation(TranslateTransform.XProperty, xOffsetAnimation);
                } else {
                    var yOffsetAnimation = new DoubleAnimation() {
                        To = -VerticalOffset,
                        Duration = TimeSpan.FromSeconds(duration),
                    };
                    transForm.BeginAnimation(TranslateTransform.YProperty, yOffsetAnimation);
                }

                ScrollOwner.InvalidateScrollInfo();
            }

            return screenSize;
        }
        #endregion

        /// 
        /// 修正 value 的值, 如果 value 超出 value1~value2 范围,则去范围的端点值
        /// 
        /// 
        /// 
        /// 
        /// 
        double range(double value, double value1, double value2) {
            var min = Math.Min(value1, value2);
            var max = Math.Max(value1, value2);

            value = Math.Max(value, min);
            value = Math.Min(value, max);
            return value;
        }

        void appendOffset(double x, double y) {
            var offset = new Vector(HorizontalOffset + x, VerticalOffset + y);

            offset.Y = range(offset.Y, 0, totalSize.Height - screenSize.Height);
            offset.X = range(offset.X, 0, totalSize.Width - screenSize.Width);

            HorizontalOffset = offset.X;
            VerticalOffset = offset.Y;

            InvalidateArrange();
        }

        #region IScrollInfo
        public bool CanVerticallyScroll { get; set; }
        public bool CanHorizontallyScroll { get; set; }

        public double ExtentWidth => totalSize.Width;

        public double ExtentHeight => totalSize.Height;

        public double ViewportWidth => screenSize.Width;

        public double ViewportHeight => screenSize.Height;

        public double HorizontalOffset { get; private set; }

        public double VerticalOffset { get; private set; }

        public ScrollViewer ScrollOwner { get; set; }

        public void LineDown() {
            appendOffset(0, lineOffset);
        }

        public void LineLeft() {
            appendOffset(-lineOffset, 0);
        }

        public void LineRight() {
            appendOffset(lineOffset, 0);
        }

        public void LineUp() {
            appendOffset(0, -lineOffset);
        }

        public Rect MakeVisible(Visual visual, Rect rectangle) {
            throw new NotImplementedException();
        }

        public void MouseWheelDown() {
            appendOffset(0, wheelOffset);
        }

        public void MouseWheelLeft() {
            appendOffset(-wheelOffset, 0);
        }

        public void MouseWheelRight() {
            appendOffset(wheelOffset, 0);
        }

        public void MouseWheelUp() {
            appendOffset(0, -wheelOffset);
        }

        public void PageDown() {
            appendOffset(0, screenSize.Height);
        }

        public void PageLeft() {
            appendOffset(-screenSize.Width, 0);
        }

        public void PageRight() {
            appendOffset(screenSize.Width, 0);
        }

        public void PageUp() {
            appendOffset(0, -screenSize.Height);
        }

        public void SetHorizontalOffset(double offset) {
            appendOffset(offset - HorizontalOffset, VerticalOffset);
        }

        public void SetVerticalOffset(double offset) {
            appendOffset(HorizontalOffset, offset - VerticalOffset);
        }

        #endregion
    }

 控件前端使用示例


        
            
                
                    
                
                
                    
                
                
                    
                
            
        

        

后台代码

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Left_Click(object sender, RoutedEventArgs e) {
            this.panel.PageLeft();
        }

        private void Right_Click(object sender, RoutedEventArgs e) {
            this.panel.PageRight();
        }
    }

你可能感兴趣的:(WPF自定义控件,wpf)