亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

在WPF中實現(xiàn)平滑滾動的方法詳解

 更新時間:2022年06月24日 09:55:23   作者:天方  
這篇文章介紹了WPF實現(xiàn)平滑滾動的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

WPF實現(xiàn)滾動條還是比較方便的,只要在控件外圍加上ScrollViewer即可,但美中不足的是:滾動的時候沒有動畫效果。在滾動的時候添加過渡動畫能給我們的軟件增色不少,例如Office 2013的滾動的時候支持動畫看起來就舒服多了。 之前倒是研究過如何實現(xiàn)這個平滑滾動,不過網(wǎng)上的方案大部分大多數(shù)如下:

  • 通過VisualTree找到ScrollViewer

  • 在ScrollChanged事件中添加動畫

這種方案效果并不好,以為我們的滾動很多時候都是一口氣滾動好幾格滾輪的,這個時候上一個動畫還沒有結(jié)束,下一個動畫就來了,反而還出現(xiàn)了卡頓的感覺,并且網(wǎng)上的一些算法大部分還都會導致偏移錯位。

趁著這兩天有點時間,就研究了一下ScorllViewer,從MSDN文檔中看到,它是支持兩種滾動方式的:

物理滾動:

系統(tǒng)默認的滾動方案,控件本身啥都不用干,完全由ScrollViewer來實現(xiàn)滾動。這種方式的好處是簡單,但也正由于簡單,控件本身完全感知不到ScorllViewer的存在,也就無法加以控制了。

邏輯滾動:

將這種方式需要設(shè)置ScrollViewer的CanContentScroll為"True"才能生效,同時需要控件實現(xiàn)IScrollInfo接口。此時ScrollViewer只是將滾動事件通過IScrollInfo接口傳遞給控件,由控件本身自己去實現(xiàn)滾動。同時從IScrollInfo接口中讀取相關(guān)的屬性更新滾動條界面。

也就是說,邏輯滾動才是我們所需要的方案。由于它要求控件實現(xiàn)IScrollInfo接口,自行控制滾動。也就是說我們要實現(xiàn)自己的Panel,并且實現(xiàn)IScrollInfo接口。關(guān)于這個接口,MSDN上有一系列文章介紹過如何實現(xiàn)它:

這個接口實現(xiàn)也不算麻煩,我倒沒有細看這幾篇文章,自己照著最后的一個例子嘗試著弄了一陣子也弄出來了。實際上麻煩的地方不在于實現(xiàn)這個接口,而是實現(xiàn)Panel,我這里為了簡單,直接繼承了WrapPanel類,代碼如下: 

    class MyWrapPanel : WrapPanel, IScrollInfo
    {
        TranslateTransform _transForm;
        public MyWrapPanel()
        {
            _transForm = new TranslateTransform();
            this.RenderTransform = _transForm;
        }

        #region Layout

        Size _screenSize;
        Size _totalSize;

        protected override Size MeasureOverride(Size availableSize)
        {
            _screenSize = availableSize;

            if (Orientation == Orientation.Horizontal)
                availableSize = new Size(availableSize.Width, double.PositiveInfinity);
            else
                availableSize = new Size(double.PositiveInfinity, availableSize.Height);

            _totalSize = base.MeasureOverride(availableSize);
            return _totalSize;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            var size = base.ArrangeOverride(finalSize);
            if (ScrollOwner != null)
            {
                _transForm.Y = -VerticalOffset;
                _transForm.X = -HorizontalOffset;

                ScrollOwner.InvalidateScrollInfo();
            }
            return _screenSize;
        }
        #endregion

        #region IScrollInfo

        public ScrollViewer ScrollOwner { get; set; }
        public bool CanHorizontallyScroll { get; set; }
        public bool CanVerticallyScroll { get; set; }

        public double ExtentHeight { get { return _totalSize.Height; } }
        public double ExtentWidth { get { return _totalSize.Width; } }

        public double HorizontalOffset { get; private set; }
        public double VerticalOffset { get; private set; }

        public double ViewportHeight { get { return _screenSize.Height; } }
        public double ViewportWidth { get { return _screenSize.Width; } }

        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();
        }

        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;
        }


        const double _lineOffset = 30;
        const double _wheelOffset = 90;

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

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

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

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

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

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

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

        public void MouseWheelLeft()
        {
            appendOffset(0, _wheelOffset);
        }

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

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

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

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

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

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

        public void SetHorizontalOffset(double offset)
        {
            this.appendOffset(offset - HorizontalOffset, VerticalOffset);
        }
        #endregion
    }

基本上從代碼中也能看出IScrollInfo接口的交互流程,這里就不多介紹了。

主界面代碼如下: 

<ItemsControl ItemsSource="{Binding}" >
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Border BorderThickness="1" BorderBrush="Black" Margin="8" Width="150" Height="50">
                    <Rectangle Fill="{Binding}"  />
                </Border>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <local:MyWrapPanel />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.Template>
            <ControlTemplate>
                <ScrollViewer CanContentScroll="True">
                    <ItemsPresenter />
                </ScrollViewer>
            </ControlTemplate>
        </ItemsControl.Template>
    </ItemsControl>

需要注意的是,這兒需要設(shè)置<ScrollViewer CanContentScroll="True">,否則使用的不是邏輯滾動。

數(shù)據(jù)源代碼如下:

    var brushes = from property in typeof(Brushes).GetProperties()
                    let value = property.GetValue(null)
                    select value;

    this.DataContext = brushes.Take(100).ToArray();

由于使用了IscrollInfo接口,所有的滾動操作是自己實現(xiàn)的,這里我是通過設(shè)置Panel的RenderTransFrom的X,Y偏移來實現(xiàn)滾動操作的。運行后看上去上和WrapPanel沒有什么區(qū)別,但是由于是自己控制的滾動,加上動畫效果也只是分分鐘的事情了,把上面代碼的RenderTransFrom的X,Y硬切換改成動畫切換即可:

    protected override Size ArrangeOverride(Size finalSize)
    {
        var size = base.ArrangeOverride(finalSize);
        if (ScrollOwner != null)
        {
            var yOffsetAnimation = new DoubleAnimation() { To = -VerticalOffset, Duration = TimeSpan.FromSeconds(0.3) };
            _transForm.BeginAnimation(TranslateTransform.YProperty, yOffsetAnimation);

            var xOffsetAnimation = new DoubleAnimation() { To = -HorizontalOffset, Duration = TimeSpan.FromSeconds(0.3) };
            _transForm.BeginAnimation(TranslateTransform.XProperty, xOffsetAnimation);

            ScrollOwner.InvalidateScrollInfo();
        }
        return _screenSize;
    }

對于其它的Panel,如Grid,DockPanel等,基本上也可以按照這種方式實現(xiàn),IScrollInfo接口處基本上可以保持不變,只需要重寫MeasureOverride和ArrangeOverride兩個函數(shù)即可。一個特殊的控件是StackPanel,由于它本身已經(jīng)實現(xiàn)了IScrollInfo接口,也就是說它本身就有自身的自繪制滾動的方案,并且沒有提供接口在覆蓋自身的自繪制滾動,因此我們需要自己寫一個StackPanel,好在實現(xiàn)StackPanel并不難,由于篇幅有限,這里我懶得繼續(xù)寫了,讀者朋友自己實現(xiàn)吧。至于那些非Panel的控件,實現(xiàn)就更簡單了,也留著讀者朋友自己實現(xiàn)吧。

到此這篇關(guān)于WPF實現(xiàn)平滑滾動的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • C#多線程編程之使用ReaderWriterLock類實現(xiàn)多用戶讀與單用戶寫同步的方法

    C#多線程編程之使用ReaderWriterLock類實現(xiàn)多用戶讀與單用戶寫同步的方法

    這篇文章主要介紹了C#多線程編程之使用ReaderWriterLock類實現(xiàn)多用戶讀與單用戶寫同步的方法,涉及C#多線程操作讀寫鎖定的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-11-11
  • 利用C#/VB.NET實現(xiàn)PPT轉(zhuǎn)換為HTML

    利用C#/VB.NET實現(xiàn)PPT轉(zhuǎn)換為HTML

    利用PowerPoint可以很方便的呈現(xiàn)多媒體信息,且信息形式多媒體化,表現(xiàn)力強。但難免在某些情況下我們會需要將PowerPoint轉(zhuǎn)換為HTML格式,本文就為大家整理了轉(zhuǎn)換方法,希望對大家有所幫助
    2023-05-05
  • C# 合并GriewView相同列的小例子

    C# 合并GriewView相同列的小例子

    C# 合并GriewView相同列的小例子,需要的朋友可以參考一下
    2013-03-03
  • 在C#中如何使用Dapper詳解(譯)

    在C#中如何使用Dapper詳解(譯)

    這篇文章主要給大家介紹了關(guān)于在C#中如何使用Dapper的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起看看吧
    2018-09-09
  • 跳一跳自動跳躍C#代碼實現(xiàn)

    跳一跳自動跳躍C#代碼實現(xiàn)

    這篇文章主要為大家詳細介紹了跳一跳自動跳躍C#代碼實現(xiàn),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • C# log4net日志庫的用法小結(jié)

    C# log4net日志庫的用法小結(jié)

    log4net日志開源庫是用來控制日志文件大小,日志文件個數(shù),滾動式覆蓋,自由控制日志打印等級,今天通過本文給大家介紹C# log4net日志庫的用法小結(jié),感興趣的朋友一起看看吧
    2021-10-10
  • C#從數(shù)據(jù)庫讀取數(shù)據(jù)到DataSet并保存到xml文件的方法

    C#從數(shù)據(jù)庫讀取數(shù)據(jù)到DataSet并保存到xml文件的方法

    這篇文章主要介紹了C#從數(shù)據(jù)庫讀取數(shù)據(jù)到DataSet并保存到xml文件的方法,涉及C#操作DataSet保存到XML文件的技巧,需要的朋友可以參考下
    2015-04-04
  • C#中的預處理器指令詳解

    C#中的預處理器指令詳解

    這篇文章主要介紹了C#中的預處理器指令詳解,本文講解了#define 和 #undef、#if、#elif、#else和#endif、#warning和#error、#region和#endregion、#line、#pragma等預處理器指令,需要的朋友可以參考下
    2015-01-01
  • C#實現(xiàn)對字符串進行大小寫切換的方法

    C#實現(xiàn)對字符串進行大小寫切換的方法

    這篇文章主要介紹了C#實現(xiàn)對字符串進行大小寫切換的方法,涉及C#操作字符串的技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-03-03
  • C#實現(xiàn)的文件批量重命名功能示例

    C#實現(xiàn)的文件批量重命名功能示例

    這篇文章主要介紹了C#實現(xiàn)的文件批量重命名功能,結(jié)合具體實例形式分析了C#針對文件的遍歷、屬性修改相關(guān)操作技巧,需要的朋友可以參考下
    2017-07-07

最新評論