威尼斯人线上娱乐

带有惯性的ScrollViewer,scrollView定时器广告位

29 3月 , 2019  

① 、先看看效果

  寒假过完,在家真心什么都做不了,也许年龄大了,再想之前那么能一心坐下来已经十三分了。回来第壹件事正是改了品种的贰个bug,近来又新增了2个新的法力,为顺序添加了二个音信栏。消息栏有成都百货上千样式,须要是贰个不必要历史记录,能够用鼠标选中国国投息内容的音信栏。笔者第③想到的就是TextBox,笔者个人相比较喜欢美丽的,有点失眠,所以必须把TextBox中的ScrollViewer给改写了,行吗,开首。

世家好!后天又是个喜欢的周二!

几周此前在博客更新一篇Windows
phone应用开发[18]-下拉刷新
博文,有众多少人在腾讯网和博客评论中涉嫌了累累难点.其实在实际项目中自个儿依据那篇博文建议解决难题思路优化了那几个消除方案.为了能够详细系统缓解和验证补充这一个难点.觉得单身开一篇博文来解答.在谈空说有中提到的一些难点.

威尼斯人线上娱乐 1

  本博文分为多少个部分,第①片段将讲述怎么着改写TextBox的布局,第1部分则讲述怎么样改写TextBox中的ScrollViewer样式,第3有个别则是对自定义样式时发生的含糊难点展开缝补。

实现的成效

github地址:
https://github.com/yukuifang/KyReactApp.git

威尼斯人线上娱乐 2

1509697628376.jpg

以前学过的知识点明天一小点补回来,使用中才会知晓有个别许坑等着您,未来总括下近期有关ScrollView的使用的知识点。

在原本的源码中有人提到:

 

  壹 、生成自定义TextBox控件

属性

#11楼 灬番茄2013-10-06 14:53

带有惯性的ScrollViewer,scrollView定时器广告位。@chenkai
p.Y值平昔是你设置的暗中同意值,所以if (p.Y <
-VerticalPullToRefreshDistance)这么些论断一向是进不去的。
自个儿读书了此外一篇下拉刷新的篇章
http://www.cnblogs.com/wuzhsh/archive/2012/09/04/2670307.html,里面涉及ScrollViewer的ManipulationMode属性设为Conrtrol(必需),暗许是System。然后我也在你的源码里添加了那句ElementScrollViewer.ManipulationMode
= ManipulationMode.Control;
才完成了下拉刷新。至于原理却没搞领会,MSDN文书档案里也只是说System比Control的滑动更流畅.

 

  依旧把本次写的新闻框做成用户控件的样式,首先,前台容易的XAML:

威尼斯人线上娱乐,contentContainerStyle

这几个样式会应用到三个内层的始末容器上,全数的子视图都会卷入在剧情容器内。PS:ScrollView组件的习性无法用style来安装。

有人涉嫌下拉时不曾电动刷新功效效果.为了详细表明那一个难题.首先来探望上篇博客中涉及关于下拉刷新源码的完成.找到源码中继承ListBox的类RefreshBox.在此类完毕中重写了OnApplyTemplate方法.在该措施中得以看出:

二、原理

威尼斯人线上娱乐 3威尼斯人线上娱乐 4新闻框基础XAML

多少个已知的滑行或许滑动初叶终结的章程:

onScroll:在滚动进度中, 每帧最多调用二次此函数,
调用的频率能够用scroll伊芙ntThrottle属性来控制.

onMomentumScrollEnd:当一帧轮转完结时调用.

onScrollAnimationEnd :ios上的当滚动动画停止时调用.

   1:  public override void OnApplyTemplate()

   2:  {

   3:      base.OnApplyTemplate();

   4:      if (ElementScrollViewer != null)

   5:      {

   6:             ElementScrollViewer.MouseMove -= viewer_MouseMove;

   7:             ElementScrollViewer.ManipulationCompleted -= viewer_ManipulationCompleted;

   8:      }

   9:      ElementScrollViewer = GetTemplateChild("ScrollViewer") as ScrollViewer;

  10:   

  11:      if (ElementScrollViewer != null)

  12:      {                        

  13:             ElementScrollViewer.MouseMove += viewer_MouseMove;

  14:             ElementScrollViewer.ManipulationCompleted += viewer_ManipulationCompleted;

  15:       }

  16:   

  17:       ElementRelease = GetTemplateChild("ReleaseElement") as UIElement;                                

  18:       ChangeVisualState(false);

  19:   }

  固然效果很简短,然而网上的有的素材涉及的代码量相当惊人,而且效用也不是很特出,滚动的时候从不二个顺滑感。小编那里提供的源码一共120多行,就能兑现上海教室的成效。

 1 <TextBox x:Class="FS.PresentationManagement.Controls.MessageTextBox"
 2          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Background="SkyBlue">
 4     <TextBox.Template>
 5         <ControlTemplate TargetType="{x:Type TextBox}">
 6             <Grid Background="{TemplateBinding Background}">
 7                 <Grid.ColumnDefinitions>
 8                     <ColumnDefinition />
 9                     <ColumnDefinition Width="62" />
10                 </Grid.ColumnDefinitions>
11                 <!-- 文本框 -->
12                 <ScrollViewer x:Name="PART_ContentHost">
13                     <!-- 暂时省略 -->
14                 </ScrollViewer>
15                 <!-- 按钮 -->
16                 <Button Name="BTN_Clear" Margin="5" Grid.Column="1" Click="BTN_Clear_Click">
17                     <Button.Template>
18                         <ControlTemplate>
19                             <Image Name="IMG_Clear" Source="../Pic/clear.png"/>
20                             <ControlTemplate.Triggers>
21                                 <Trigger Property="IsMouseOver" Value="True">
22                                     <Setter TargetName="IMG_Clear" Property="Source" Value="../Pic/clear2.png" />
23                                 </Trigger>
24                                 <Trigger Property="Button.IsPressed" Value="True">
25                                     <Setter TargetName="IMG_Clear" Property="Source" Value="../Pic/clear3.png" />
26                                 </Trigger>
27                             </ControlTemplate.Triggers>
28                         </ControlTemplate>
29                     </Button.Template>
30                 </Button>
31                 <Button Name="BTN_Close" Margin="0,-18,-25,0" VerticalAlignment="Top" Width="32" Height="32" Grid.Column="1" Click="BTN_Close_Click">
32                     <Button.Template>
33                         <ControlTemplate>
34                             <Image Name="IMG_Close" Source="../Pic/close.png" />
35                             <ControlTemplate.Triggers>
36                                 <Trigger Property="IsMouseOver" Value="True">
37                                     <Setter TargetName="IMG_Close" Property="Source" Value="../Pic/close2.png" />
38                                 </Trigger>
39                                 <Trigger Property="Button.IsPressed" Value="True">
40                                     <Setter TargetName="IMG_Close" Property="Source" Value="../Pic/close3.png" />
41                                 </Trigger>
42                             </ControlTemplate.Triggers>
43                         </ControlTemplate>
44                     </Button.Template>
45                 </Button>
46             </Grid>
47         </ControlTemplate>
48     </TextBox.Template>
49 </TextBox>

贰 、还有其余的有的风云如下,触摸事件里面有带领event,大家能够再上边包车型大巴措施里面更改一些view操作就能够打字与印刷出来这一个event指点的新闻了

① 、onScrollBeginDrag:多个子view滑动开首扶拖拉机动起头时接触,注意和onMomentumScrollBegin的差异

二 、onScrollEndDrag:3个子view滚动停止拖拽时接触,注意和onMomentumScrollEnd的区分

叁 、onTouchStart:按下显示器时接触

④ 、onTouchMove:移入手指时触发

⑤ 、onTouchEnd:手指离开显示屏触摸停止时接触

⑥ 、onMomentumScrollBegin:当一帧轮转初阶时调用.

⑦ 、onMomentumScrollEnd:当一帧滚动实现时调用.

捌 、onStartShouldSetResponder:触摸开头时是不是成为响应者

九 、onStartShouldSetResponderCapture:幸免子视图在触摸开始时改为应答器

十 、onScrollShouldSetResponder:滚动时是或不是成为响应者

1一 、onResponder格兰特:开始响应时接触

1二 、onResponderRelease:手指释放后,视图成为响应者

1叁 、onResponderReject:响应拒绝

1④ 、onScroll:滚动时接触,会触发多次

先是在OnApplyTemplate()方法中能够见到做了之类几件事:

  本质上大家假诺接管ScrollViewer的轮转逻辑,并且把那个逻辑替换到带有惯性的即可,那么哪些去接管呢?这里的主要性是先屏蔽ScrollViewer的鼠标滚轮事件:

  这一个时候框架大致是,左侧将是贰个ScrollViewer,用来展现音信,左边则是关闭和清理,八个按钮,至于按钮的体制,也曾经进展了改动,每种按钮使用三张图片来表示原本、停靠、按下二种状态,须要专注,上边的XAML中按钮的Source路径是像“../Pic/xxx.png”,那是自身把图纸放到了眼下文件的—>上级目录的—>Pic目录下,所以实际上海高校家在采取的时候要求把这一个天性改成图片所在路径。

③ 、上边就那些办法的依次做个大约的介绍:

第贰在ios上拓展测试,测试的结果如下:

威尼斯人线上娱乐 5

image

由上海体育场地能够见见执行的各类,

先是是按下显示屏时触发onTouchStart,

下一场手指运动触发onTouchMove,会调用3遍依旧频仍,

万一左右滑行,滑动开始拖动触发onScrollBeginDrag,View起先转变,View成为响应者,

下一场onScroll … onTouchMove这多少个会出手多次,

然后手指离开显示器触发onResponderRelease,

接着触摸甘休onTouchEnd

接下来是滑动甘休拖拽时触发onScrollEndDrag,接着就是一帧滚动的上马onMomentumScrollBegin,它的前奏位置和onScrollEndDrag的终结地方重合;

然后是滚动滚动onScroll,

接下来是一帧滚动的终止onMomentumScrollEnd,

说到底偶尔还会滚动下onScroll,那个有时光不出去,作者以为跟有震动一样

我们能够协调测试下哦

A: 添加ScrollViewer 关于MouseMove 和ManipulationComplated 三个事件订阅
【ScrollViewer非空时打消】

B:获取ListBox中ScrollViewer对象

C:获取顶部刷新提醒Element 的引用对象

D:起先化控制顶部刷新提醒VisualState 状态

1 protected override void OnMouseWheel(MouseWheelEventArgs e)
2 {
3      e.Handled = true;
4 }

  后台代码此时也十分不难,只是简短地持续了TextBox控件:

沾满源码:

import React,{Component} from 'react';
import{
   AppRegistry,
   StyleSheet,
   View,
   Image,
    Text,
   ScrollView
}from 'react-native';

let { width,height}  =  require('Dimensions').get('window');

let data = require('../Resource/scrollView/ImageData.json');

let TimerMixin = require('react-timer-mixin');

class KyScrollView extends Component{
    constructor(props){
        super(props);
        mixins: [TimerMixin]
        this.state = {
            dataList:data.data,
            currentPage:0
        }
    }
    render(){
        return(
            <View style={styles.container}>
                <ScrollView ref='scrollViewRef' contentContainerStyle={styles.scollViewStyle}
                            horizontal={true}
                            showsHorizontalScrollIndicator={false}
                            showsVerticalScrollIndicator={false}
                            pagingEnabled={true}
                            bounces={false}
                            onMomentumScrollEnd={(e)=>this._annimationEnd(e)}
                            onScrollBeginDrag = {()=>this._onBeginDrag()}
                            onScrollEndDrag={()=>this._onEndDrag()}>
                    {this._renderContent()}
                </ScrollView>

                <View style={styles.indicatorContentStyle}>
                    {this._renderPagingIndicator()}

                </View>


            </View>
        )
    }
    componentDidMount(){
        this._startTimer();
    }
    componentWillUnmount(){
        clearInterval(this.timer);
    }
    _onBeginDrag(){
        this._startTimer();
    }
    _onEndDrag(){
        clearInterval(this.timer);
    }
    _startTimer(){
        let maxCount =  this.state.dataList.length;
        let scrollView = this.refs.scrollViewRef;
        let that = this;
        this.timer = setInterval(function () {
            if(that.state.currentPage < maxCount-1){
                that.setState({
                    currentPage : that.state.currentPage + 1
                });
            }else {
                that.setState({
                    currentPage : 0
                });
            }
            scrollView.scrollResponderScrollTo({
                x:that.state.currentPage * width,
                y:0,
                animated:true
            });

        },that.props.duration);

    }
    _renderContent(){
        var items = [];
        for (var i =0 ; i < this.state.dataList.length;i++){
            let imageData = this.state.dataList[i];
            items.push(
                <View key={i}>
                    <Image source={{uri:imageData.img}} style={{height:200,width:width}}></Image>
                </View>
            )
        }
        return items;
    }
    _renderPagingIndicator(){
        var indicators  = [];

        for (var i=0;i<this.state.dataList.length;i++){
            let style = this.state.currentPage == i ? {color:'orange'} : {color:'#FFFFFF'};
            indicators.push(
                <Text key={i} style={[{fontSize:25},style]}>&bull;</Text>
            )
        }
        return indicators;
    }
    _annimationEnd(e){
        let offsetX = e.nativeEvent.contentOffset.x;
        this.setState({
            currentPage:offsetX / width
        })
    }


}

KyScrollView.properties = {
    currentPage:React.PropTypes.int
}
KyScrollView.defaultProps = {
     duration:1000
}

const styles = StyleSheet.create({
    container:{

        // backgroundColor:'red',
        position:'relative'
    },
    scollViewStyle:{
        backgroundColor:'black'
    },
    indicatorContentStyle:{
        height:20,
        backgroundColor:'rgba(0,0,0,0.2)',
        position:'absolute',
        bottom:2,
        left:0,
        right:0,
        flexDirection:'row',
        alignItems:'center'


    }
});

module.exports =KyScrollView;

实际到那里 供给万分说圣元下完毕下拉刷新的原理.从源码中得以看出.
在下拉时会首先触发MouseMove 事件.
MouseMove事件非同平日意义是用来由此下拉的偏离来支配下拉刷新状态[下拉、放手刷新]二种状态切换提醒.
下拉刷新并不是下拉后会马上刷新.而是用户松开后列表回到顶部才起来刷新数据.等用户手势操作离开了荧屏就会自动触发ManipulationComplated
事件.你能够看出在Complated事件中:

  那样一来,ScrollViewer就不会响应滚轮事件了,大家就在此地做小说。首先大家给那几个ScrollViewer添加壹特性能 IsEnableInertia ,用来控制是或不是使用惯性,因为萝卜青菜各有所爱,不要想着强制全数人使用惯性,所以滚轮响应措施成为:

威尼斯人线上娱乐 6威尼斯人线上娱乐 7音讯框基础C#

   1:          private void viewer_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)

   2:          {

   3:              var p = this.TransformToVisual(ElementRelease).Transform(new Point());

   4:              if (p.Y < -VerticalPullToRefreshDistance)

   5:              {

   6:                  if (PullRefresh != null)

   7:                      PullRefresh(this, EventArgs.Empty);

   8:                  isPulling = false;

   9:                  ChangeVisualState(true);

  10:              }

  11:          }
1 protected override void OnMouseWheel(MouseWheelEventArgs e)
2 {
3     if (!IsEnableInertia)
4     {
5         base.OnMouseWheel(e);
6         return;
7     }
8     e.Handled = true;
9 }    
 1 namespace FS.PresentationManagement.Controls
 2 {
 3     /// <summary>
 4     /// 文本消息框控件
 5     /// </summary>
 6     public partial class MessageTextBox : TextBox
 7     {
 8         public MessageTextBox()
 9         {
10             InitializeComponent();        
11         }
12     }
13 }

因此判断ElementRealse也正是下拉刷新顶部提示有些下拉的相距来触发事件PullRefresh来刷新新的数据.
个中VerticalPullToRefreshDistance属性是用来判定当下啦到稍微距离时才触发刷新事件.能够定义控件时预设.在回到上文.来回复为啥在下拉时不曾接触刷新事件?

  控制ScrollViewer的垂直滚动能够应用 ScrollViewer.ScrollToVerticalOffset ,横向也如出一辙。为何不可能用 VerticalOffset ?因为 VerticalOffset 在登记的时候就印证了是只读的:

  此时的意义如图所示:威尼斯人线上娱乐 8  看起来还能够啊,右上角的倒闭按钮由于截图原因不是很清晰,稍后我们能够见到完整版的要好有的。

p.y对象的值为什么一直为90?
那是因为在刚开头定义ElementRealse对象时对顶部Manger Top值正是90,
那为啥在下拉甘休时 这些相应的X值没有跟随滑动操作变化?
其实这几个题材和SCrollView的ManipulationMode属性.aspx)有关系.
首先我们得以在OnApplyTemplate方法能够见见没有设置MainpulationMode属性.aspx)的值.
而MainpilationMode属性在私下认可处境下是安装为System的.也正是钦点系统来处理ListBox的平滑滚动的.ScrollViewer并没有拖到顶部或尾部的事件,而且当ScrollViewer的ManipulationMode为System的时候,是不可能收获到ScrollViewer滚动条的当前地方.也等于无能为力动态在ManipulationComplated
事件来获取ElementRealse距离顶部的距离.那也正是为啥p.y的值平昔是初叶化90
而不趁着滑动操作发生转移的原因.

1 private static readonly DependencyPropertyKey VerticalOffsetPropertyKey = DependencyProperty.RegisterReadOnly(nameof (VerticalOffset), typeof (double), typeof (ScrollViewer), (PropertyMetadata) new FrameworkPropertyMetadata((object) 0.0));
2 
3 public static readonly DependencyProperty VerticalOffsetProperty = ScrollViewer.VerticalOffsetPropertyKey.DependencyProperty;

  二、改造ScrollViewer控件

那在具体点?
为啥设置MainpulationMode属性.aspx)为System
后就无法获得ScrollViewer滚动条的岗位?
System和Control区别在于.两者的转换(Transform)形式分歧,当ManipulationMode为System的时候,ScrollViewer的转移格局是MatrixTransform[系统矩阵变换处理滑动],所以无法得到ScaleY可能TranslateY等品质。通过那个MatrixTransform也从不章程直接获得当前ScrollViewer的前后滚动、压缩状态。而置为Control时,变换方式就成了CompositeTransform,通过CompositeTransform就能够取得ScrollViewer的TranslateY值(当到达顶部的时候,TranslateY变为正值,其他时候为负值,当先尾部时,相对值超出ScrollViewer内容长度),然后在ScrollViewer的操作事件ManipulationStarted、ManipulationDetla或ManipulationCompleted中,获取ScrollViewer的更换格局,获得TranslateY值,最后判断是还是不是到达顶部或底层,决定是不是要拓展处理.

  好了,接下去就是怎么在滚轮响应措施中贯彻惯性运动了,也正是一种减速运动。想到那儿,纯熟动画的博友极快就精晓要用WPF的动画片来得以实现了,暗许的卡通都以一回线性的,要有惯性功效就得用缓动函数,WPF的缓动函数有许多,而 CubicEase 非凡适合用来做惯性,它的叙述图如下:

  下边介绍本文的主导,怎么着自定义ScrollViewer控件,当然,大家的靶子也不是把它改成怎么着奇葩,只是想把滚动条变得不错一点而已。假设应用WPF比较多的爱人会精晓,许多控件都是由许多层一层一层地叠加形成可视化树的,ScrollViewer也不例外,未来通过Template属性能够完全自身定义其组织。

能够见见两者之间的本质原理上区别.那也就可见解释为啥. 当ScrollViewer
的ManipulationMode属性.aspx)
暗中同意为System时心慌意乱即时获得下拉ElementRealse
的X的值了.也正是说用当下下拉刷新必须安装ManipulationMode属性.aspx)为Control.
但您测试后发现.
下拉刷新逻辑能够平常触发刷新事件.但是一切滑动进度会鲜明感觉卡了广大[亟需注解的是ListBox不存在虚拟化的标题].没有安装为System系统处理情势平滑流畅.
那什么来消除安装设置ManipulationMode属性.aspx)为Control
滑动会卡顿的难点?
或是有没有二个能够取得System处理滑动一样平滑体验同时又能够看清ScrollViewer当前的职位状态的缓解方案.

威尼斯人线上娱乐 9

  要拓展改建的ScrollViewer控件就位于第②部分XAML代码中的省略部分,小编明天只贴出这有些代码:

通过一番周折在MSDN Blog上找到了一个可见落到实处如上两点消除方案:

  图中,横轴表示时间,纵轴代表运动距离。很明朗,中间的 EaseOut 方式就是大家想要的。到了此间思路就清清楚楚了,我们得以定义1个属性 CurrentVerticalOffset ,大家会在它上边达成动画,在它的值回调函数中调用 ScrollViewer.ScrollToVerticalOffset 来更新ScrollViewer的滚动地方。当然大家还索要三个私有字段 _totalVerticalOffset ,这么些是用来存放ScrollViewer滚动目的地点的,滚轮向下滚动三个单位大家就给它减去一回 e.Delta ,那里的e是滚轮响应措施传进来的参数,每便给它赋值之后,就能够在 CurrentVerticalOffset 上举办动画了: BeginAnimation(CurrentVerticalOffsetProperty,
animation) ,要求特别注意的是,当三个借助属性用了动画片改变后,再对其赋值则不会一蹴而就,原因是在2个动画片到达活动期的终极后,时间线暗中同意会保持其速度,直到其父级的活动期和保持期截止截至。要是想在动画截止后还能手动更改依赖属性的值,则必要把 FillBehavior 设置为Stop。不过尔尔又会产出3个标题,一旦动画结束,那一个依靠属性又会还原开头值,所以还要给这几个动画订阅2个 Completed 事件,在事变响应措施中为 CurrentVerticalOffset 给定指标值,也正是 _totalVerticalOffset 。

威尼斯人线上娱乐 10威尼斯人线上娱乐 11自定义ScrollViewer模版

Windows Phone Mango change, Listbox: How to detect compression(end of
scroll) states
?

  最后还有三个争辨难题,当手动拖动滑块也许当用上下文菜单改变滚动条地点时是无法用动画的,因为此时没有触发 OnMouseWheel ,没提到,那多亏大家想要的,但是只要再一次触发 OnMouseWheel 就不平日了,因为手动触发滚动的时候我们从没给 CurrentVerticalOffset 和 _totalVerticalOffset 赋值( CurrentVerticalOffset 和 _totalVerticalOffset 只在 OnMouseWheel 中赋值),所以在用动画执行滚动操作前要先判断一下是否需求先更新一下它们俩,如何判断?大家得以用3个民用字段 _isRunning 来保证状态,每当动画早先就给它赋值true,甘休则赋值false。那样一来,当 _isRunning = false 时,表明在调用 OnMouseWheel 前,动画已经终止,用户大概已经手动改变了滚动条地点(也恐怕没有,但那并不影响),所以就要给前边俩小兄弟更新一下值了。

  1 <ScrollViewer x:Name="PART_ContentHost">
  2     <ScrollViewer.Template>
  3         <ControlTemplate TargetType="{x:Type ScrollViewer}">
  4             <Grid Background="{Binding Path=ScrollViewerBackground,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TextBox}}}">
  5                 <Grid.ColumnDefinitions>
  6                     <ColumnDefinition />
  7                     <ColumnDefinition Width="Auto"/>
  8                 </Grid.ColumnDefinitions>
  9                 <Grid.RowDefinitions>
 10                     <RowDefinition/>
 11                     <RowDefinition Height="Auto"/>
 12                 </Grid.RowDefinitions>
 13                 <ScrollContentPresenter Margin="5,5,0,5" />
 14                 <ScrollBar Name="PART_VerticalScrollBar" Grid.Column="1" Value="{TemplateBinding VerticalOffset}" Maximum="{TemplateBinding ScrollableHeight}" ViewportSize="{TemplateBinding ViewportHeight}" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}">
 15                     <ScrollBar.Template>
 16                         <ControlTemplate TargetType="{x:Type ScrollBar}">
 17                             <!-- 竖向滚动条宽度 -->
 18                             <Grid Width="10">
 19                                 <Grid.RowDefinitions>
 20                                     <RowDefinition Height="1" />
 21                                     <RowDefinition />
 22                                     <RowDefinition Height="1" />
 23                                 </Grid.RowDefinitions>
 24                                 <Track x:Name="PART_Track" Grid.Row="1" IsDirectionReversed="True">
 25                                     <Track.DecreaseRepeatButton>
 26                                         <!--上空白-->
 27                                         <RepeatButton Command="ScrollBar.PageUpCommand" Opacity="0.5">
 28                                             <RepeatButton.Template>
 29                                                 <ControlTemplate>
 30                                                     <Border Background="{Binding Path=ScrollBarBackground,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TextBox}}}" CornerRadius="5,5,0,0" />
 31                                                 </ControlTemplate>
 32                                             </RepeatButton.Template>
 33                                         </RepeatButton>
 34                                     </Track.DecreaseRepeatButton>
 35                                     <Track.Thumb>
 36                                         <!--滑块-->
 37                                         <Thumb>
 38                                             <Thumb.Template>
 39                                                 <ControlTemplate>
 40                                                     <Border Background="{Binding Path=ScrollBarForeground,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TextBox}}}" CornerRadius="5" />
 41                                                 </ControlTemplate>
 42                                             </Thumb.Template>
 43                                         </Thumb>
 44                                     </Track.Thumb>
 45                                     <Track.IncreaseRepeatButton>
 46                                         <!--下空白-->
 47                                         <RepeatButton Command="ScrollBar.PageDownCommand" Opacity="0.5">
 48                                             <RepeatButton.Template>
 49                                                 <ControlTemplate>
 50                                                     <Border Background="{Binding Path=ScrollBarBackground,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TextBox}}}" CornerRadius="0,0,5,5" />
 51                                                 </ControlTemplate>
 52                                             </RepeatButton.Template>
 53                                         </RepeatButton>
 54                                     </Track.IncreaseRepeatButton>
 55                                 </Track>
 56                             </Grid>
 57                         </ControlTemplate>
 58                     </ScrollBar.Template>
 59                 </ScrollBar>
 60                 <ScrollBar Name="PART_HorizontalScrollBar" Orientation="Horizontal" Grid.Row="1" Value="{TemplateBinding HorizontalOffset}" Maximum="{TemplateBinding ScrollableWidth}" ViewportSize="{TemplateBinding ViewportWidth}" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}">
 61                     <ScrollBar.Template>
 62                         <ControlTemplate TargetType="{x:Type ScrollBar}">
 63                             <!-- 横向滚动条高度 -->
 64                             <Grid Height="10">
 65                                 <Grid.ColumnDefinitions>
 66                                     <ColumnDefinition Width="1" />
 67                                     <ColumnDefinition />
 68                                     <ColumnDefinition Width="1" />
 69                                 </Grid.ColumnDefinitions>
 70                                 <Track x:Name="PART_Track" Grid.Column="1" IsDirectionReversed="False">
 71                                     <Track.DecreaseRepeatButton>
 72                                         <!--左空白-->
 73                                         <RepeatButton Command="ScrollBar.PageLeftCommand" Opacity="0.5">
 74                                             <RepeatButton.Template>
 75                                                 <ControlTemplate>
 76                                                     <Border Background="{Binding Path=ScrollBarBackground,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TextBox}}}" CornerRadius="5,0,0,5" />
 77                                                 </ControlTemplate>
 78                                             </RepeatButton.Template>
 79                                         </RepeatButton>
 80                                     </Track.DecreaseRepeatButton>
 81                                     <Track.Thumb>
 82                                         <!--滑块-->
 83                                         <Thumb>
 84                                             <Thumb.Template>
 85                                                 <ControlTemplate>
 86                                                     <Border Background="{Binding Path=ScrollBarForeground,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TextBox}}}" CornerRadius="5" />
 87                                                 </ControlTemplate>
 88                                             </Thumb.Template>
 89                                         </Thumb>
 90                                     </Track.Thumb>
 91                                     <Track.IncreaseRepeatButton>
 92                                         <!--右空白-->
 93                                         <RepeatButton Command="ScrollBar.PageRightCommand" Opacity="0.5">
 94                                             <RepeatButton.Template>
 95                                                 <ControlTemplate>
 96                                                     <Border Background="{Binding Path=ScrollBarBackground,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TextBox}}}" CornerRadius="0,5,5,0" />
 97                                                 </ControlTemplate>
 98                                             </RepeatButton.Template>
 99                                         </RepeatButton>
100                                     </Track.IncreaseRepeatButton>
101                                 </Track>
102                             </Grid>
103                         </ControlTemplate>
104                     </ScrollBar.Template>
105                 </ScrollBar>
106             </Grid>
107         </ControlTemplate>
108     </ScrollViewer.Template>
109 </ScrollViewer>

第二来说说这一个消除方案的完成.当然大家落到实处List博克斯上平滑处理意识系统ManipulationMode属性.aspx)为system
矩阵处理方式滑动体验很流畅.那什么样来判定在安装为System时取得ScrollViewer的状态呢?
答案是使用VisualState.

  因为周边的惯性滚动以垂直方向居多,所以作者未曾写水平方向的逻辑,但也很简单扩充,有趣味的博友能够下载源代码本身商量。

  对应的后台正视属性:

要兑现应用Visual
State来获取SCrollViewer当前地点.只供给今后Xaml文件添加如下代码[只截取其中Visual
State 全体代码见源码]:    

 

威尼斯人线上娱乐 12威尼斯人线上娱乐 13ScrollViewer的后台重视属性

   1:                               <VisualStateManager.VisualStateGroups>

   2:                                  <VisualStateGroup x:Name="ScrollStates">

   3:                                      <VisualStateGroup.Transitions>

   4:                                          <VisualTransition GeneratedDuration="00:00:00.5"/>

   5:                                      </VisualStateGroup.Transitions>

   6:                                      <VisualState x:Name="Scrolling">

   7:                                          <Storyboard>

   8:                                              <DoubleAnimation Storyboard.TargetName="VerticalScrollBar"

   9:                                                                            Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>

  10:                                              <DoubleAnimation Storyboard.TargetName="HorizontalScrollBar" 

  11:                                                                            Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>

  12:                                          </Storyboard>

  13:                                      </VisualState>

  14:                                      <VisualState x:Name="NotScrolling">

  15:                                      </VisualState>

  16:                                  </VisualStateGroup>

  17:                                  <VisualStateGroup x:Name="VerticalCompression">

  18:                                      <VisualState x:Name="NoVerticalCompression"/>

  19:                                      <VisualState x:Name="CompressionTop"/>

  20:                                      <VisualState x:Name="CompressionBottom"/>

  21:                                  </VisualStateGroup>

  22:                                  <VisualStateGroup x:Name="HorizontalCompression">

  23:                                      <VisualState x:Name="NoHorizontalCompression"/>

  24:                                      <VisualState x:Name="CompressionLeft"/>

  25:                                      <VisualState x:Name="CompressionRight"/>

  26:                                  </VisualStateGroup>

  27:                              </VisualStateManager.VisualStateGroups>

三、源码

 1         /// <summary>
 2         /// 滚动框背景
 3         /// </summary>
 4         public Brush ScrollViewerBackground
 5         {
 6             get { return (Brush)GetValue(ScrollViewerBackgroundProperty); }
 7             set { SetValue(ScrollViewerBackgroundProperty, value); }
 8         }
 9         public static readonly DependencyProperty ScrollViewerBackgroundProperty =
10             DependencyProperty.Register("ScrollViewerBackground", typeof(Brush), typeof(MessageTextBox), new PropertyMetadata(Brushes.LightBlue));
11 
12         /// <summary>
13         /// 滚动条前景
14         /// </summary>
15         public Brush ScrollBarForeground
16         {
17             get { return (Brush)GetValue(ScrollBarForegroundProperty); }
18             set { SetValue(ScrollBarForegroundProperty, value); }
19         }
20         public static readonly DependencyProperty ScrollBarForegroundProperty =
21             DependencyProperty.Register("ScrollBarForeground", typeof(Brush), typeof(MessageTextBox), new PropertyMetadata(Brushes.RoyalBlue));
22 
23         /// <summary>
24         /// 滚动条背景
25         /// </summary>
26         public Brush ScrollBarBackground
27         {
28             get { return (Brush)GetValue(ScrollBarBackgroundProperty); }
29             set { SetValue(ScrollBarBackgroundProperty, value); }
30         }
31         public static readonly DependencyProperty ScrollBarBackgroundProperty =
32             DependencyProperty.Register("ScrollBarBackground", typeof(Brush), typeof(MessageTextBox), new PropertyMetadata(Brushes.WhiteSmoke));

在后台代码中添加对ScrollViewer状态的变更事件订阅.

  本文所钻探的控件源码已经在github开源:

  在结构前台界面时,首先,定义了二个Grid做为容器,并把它分为了四份,分别是内容、竖向滚动条、横向滚动条、空白。当中,内容位于0行、0列,使用ScrollContentPresenter来表示将要展现的剧情;竖向滚动条位于0行1列,使用ScrollBar来表示;横向滚动条位于1行0列,使用横向(Orientation=”Horizontal”)的ScrollBar来表示。

   1:              sv = (ScrollViewer)FindElementRecursive(MainListBox, typeof(ScrollViewer));

   2:              if (sv != null)

   3:              {

   5:                  FrameworkElement element = VisualTreeHelper.GetChild(sv, 0) as FrameworkElement;

   6:                  if (element != null)

   7:                  {

   8:                      VisualStateGroup group = FindVisualState(element, "ScrollStates");

   9:                      if (group != null)                    

  10:                          group.CurrentStateChanging += new EventHandler<VisualStateChangedEventArgs>(group_CurrentStateChanging);

  11:                      

  12:                      VisualStateGroup vgroup = FindVisualState(element, "VerticalCompression");

  13:                      VisualStateGroup hgroup = FindVisualState(element, "HorizontalCompression");

  14:                      if (vgroup != null)                    

  15:                          vgroup.CurrentStateChanging += new EventHandler<VisualStateChangedEventArgs>(vgroup_CurrentStateChanging);

  16:                      

  17:                      if (hgroup != null)                    

  18:                          hgroup.CurrentStateChanging += new EventHandler<VisualStateChangedEventArgs>(hgroup_CurrentStateChanging);                    

  19:                  }

  20:              }         

 

  然后,分别自定义ScrollBar的样式。以竖向滚动条为例,自定义ControlTemplate,使用Grid作为容器,把滚动条分为三行,第壹行事提升按钮、第三行事滚动条、第一表现向下按钮。小编那边由于美观考虑,把七个按钮全省略了(实际上我们很少使用按钮来上下滚动,半数以上时候用的鼠标中轮和拖动滑块)。

 

  滚动条是使用的Track控件,它又蕴含多个区域,分别是空间白、滑块、下空白,我们来看个示例图:

从代码逻辑可知.Xaml文件重写了整整ScrollViewer的体裁并添加两组Vistaul
State Group状态的标识.
后代代码通过订阅ScrollViewer垂直和程度滑动的情景起头事件CurrentStateChanging.在事变对应的经过如下格局展开判断当前ScrollViewer的动静:

威尼斯人线上娱乐 14

   1:       private void vgroup_CurrentStateChanging(object sender, VisualStateChangedEventArgs e)

   2:          {

   3:              if (e.NewState.Name == "CompressionTop")

   4:              {

   5:                  #region Goto Top

   6:                  #endregion

   7:              }

   8:              else if (e.NewState.Name == "CompressionBottom")

   9:              {

  10:                  #region Goto Bottom

  11:                  #endregion

  12:              }

  13:              else if (e.NewState.Name == "NoVerticalCompression")

  14:              {

  15:                  #region No Vertical Compression

  16:                  #endregion

  17:              }

  18:          }

 

骨子里以获得VerticalCompression和HorizontalCompression两种VisualStateGroup,能够用来检查和测试ListBox的左右左右样子的压缩状态。那种化解方案的做法是行使VisualState检测ScrollViewer滚动状态,来判断SCrollViewer是到了顶部照旧尾部以及是不是滚动中状态.唯有在滚动甘休时,即NotScrolling状态,检查和测试滚动偏移(Offset),固然偏移加上轮转前岗位超过了控件内容总司长度(非可视长度),就展开刷新大概别的相应的处理。

  Track的DecreaseRepeatButton就是空中白、Thumb则是滑块、IncreaseRepeatButton是下空白,分别对那四个控件举行体制自定义即可改变其外观。供给注解的是竖向滚动条须要把Track的IsDirectionReversed属性设置为True,横向则设置为False,不然会现出尤其想获得的气象(原因嘛,大家看属性名的意趣就知道了)。

事实上基于那么些方案.结合第1个格局有个别改造一下ScrollViewer 的Vistual State
即可达到平滑处理滑动下拉刷新提醒操作.这里就不做过多废话了.

  最终,还有一些要解释一下,大家发现许多控件有接近于“PA奥迪Q3T_***”的称谓,这一个名称请不要随意变更,那是WPF内置的很是名称,比如ScrollViewer的“PATucsonT_ContentHost”名称,便是表示那么些控件是用于装载TextBox的文件内容的,并且经过测试,这些称呼只可以用来ScrollViewer或然Adorner、Decorator控件。假设没有利用这几个特别称称,或许就不恐怕像您想像中那么自动达成工作了。

源码下载[]

  ③ 、订正一些难点

Contact ME
[@chenkaihome]

  为啥把那做为单独的一环来研商吗?因为前边的代码已经能够成功基本的行事了,而且出现的难点关乎也并不是那多少个大。可是总会不爽,因为它就不那么完美,所以,Fix
It!

参考资料:

  难题1:鼠标中轮不可能使ScrollViewer上下滚动

Windows Phone Mango change, Listbox: How to detect compression(end of
scroll) states

   发生那一个标题标因由尤其怪异,假诺不是修改ScrollViewer的Template来完全改变它,而是采纳ScrollViewer.Resources来定义ScrollBar的Style则完全不会时有发生那种题材,可是那不能使的改动各控件的大小和布局。

Listbox – ScrollViewer performance improvement for Mango and how it
impacts your existing
application?

  此外,假诺不是把ScrollViewer的Name设置为“PACR-VT_ContentHost”,而是选拔<TextBlock
Text=”{TemplateBinding Text}” TextWrapping=”{TemplateBinding
TextWrapping}”
/>放置到ScrollViewer体中,就足以健康滚动。可是那时会导致无法选粤语本了,因为TextBlock中的文本是不帮忙选中的,尤其注意到,那时的滚动作效果用相当的低,滚动时画面有显然的木讷现象。同样如若不把ScrollViewer的Name设置为“PA奥迪Q5T_ContentHost”,而用<Decorator
Name=”PART_ContentHost”
/>放置到ScrollViewer体中,固然选中也能支撑,然而依然不可能滚动。

ScrollViewer.ManipulationMode
属性.aspx)

  消除方式:

MatrixTransform

  首先,为ScrollViewer添加Initialized=”PART_ContentHost_Initialized”事件,后台扩充新的习性ScrollViewer以便使用:

威尼斯人线上娱乐 15威尼斯人线上娱乐 16先导化滚动条

 1     /// <summary>
 2         /// 消息体滚动框
 3         /// </summary>
 4         public ScrollViewer ScrollViewer { get; set; }
 5     
 6     // 初始化滚动条
 7         private void PART_ContentHost_Initialized(object sender, EventArgs e)
 8         {
 9             this.ScrollViewer = sender as ScrollViewer;
10         }

  然后,自身实现中轮滚动方法,为ScrollViewer添加MouseWheel=”PALX570T_ContentHost_MouseWheel”事件,添加后台响应代码:

  private void PART_ContentHost_MouseWheel(object sender,
System.Windows.Input.MouseWheelEventArgs e)
  {
    ScrollViewer.ScrollToVerticalOffset(ScrollViewer.VerticalOffset

  • (e.Delta >> 2));
      }

  便能够周密消除鼠标中轮滚动难点。

  难题2:鼠标左键按住拖动不能够使ScrollViewer滚动

   一般的话,大家在其它文字相关软件上,比如记事本、网页等,只要鼠标左键按下拖动选汉语本,假若鼠标超出文本框可展现范围,便会活动向鼠标所在方向滚动文本内容,以落到实处跨页选中的效应。不过与难题1相同,由于改变了ScrollViewer的Template,导致那些通用作用也急需自个儿完毕了。

  化解方法:

  首先,给前台的最上层元素TextBox添加SelectionChanged=”TextBox_SelectionChanged”事件,以追踪选中时鼠标所在地方:

 1         private void TextBox_SelectionChanged(object sender, RoutedEventArgs e)
 2         {
 3             if (ScrollViewer != null && this.SelectedText != "")
 4             {
 5                 var point = System.Windows.Input.Mouse.GetPosition(ScrollViewer);
 6                 // 纵向位移
 7                 double y = point.Y;
 8                 if (y > 0)
 9                 {
10                     y = y - ScrollViewer.ActualHeight;
11                     if (y < 0) y = 0;
12                 }
13                 _ScrollY = y;
14                 // 横向位移
15                 double x = point.X;
16                 if (x > 0)
17                 {
18                     x = x - ScrollViewer.ActualWidth;
19                     if (x < 0) x = 0;
20                 }
21                 _ScrollX = x;
22             }
23         }

  说喜宝下,_ScrollX和_ScrollY是五个分子属性,它们各自用来记录横向、竖向的鼠标位移,以用于决定是不是滚动。只有在超越ScrollViewer的限制时,它们的值才会不为0,当小于0时期表要升高/左滚动,大于0时意味着向下/右滚动,它们的断然值越大,则滚动速度越快。

  现在,滚动量已经能革新了,但滚动触发条件还亟需考虑。首先,横向和竖向滚动相对于前台界面肯定是异步举行的;其次,已经在滚动时要实时遵照滚动量来控制滚动速度;还有,滚动终止条件应该是滚动量为0或然已经滚动到了界限。好了,目的显明,须要添加八个委托来分别处理横向、竖向滚动,还亟需七个异步操作情况来代表滚动是不是甘休,那么,代码增加为:

威尼斯人线上娱乐 17威尼斯人线上娱乐 18滚动委托

 1     // 坚向位移
 2         private double _ScrollY
 3         {
 4             get { return _scrollY; }
 5             set
 6             {
 7                 _scrollY = value;
 8                 // 开启滚动
 9                 if (_scrollY != 0 && (_ScrollYResult == null || _ScrollYResult.IsCompleted))
10                     _ScrollYResult = _ScrollYAction.BeginInvoke(null, null);
11             }
12         }
13         private double _scrollY;
14 
15         // 横向位移
16         private double _ScrollX
17         {
18             get { return _scrollX; }
19             set
20             {
21                 _scrollX = value;
22                 // 开启滚动
23                 if (_scrollX != 0 && (_ScrollXResult == null || _ScrollXResult.IsCompleted))
24                     _ScrollXResult = _ScrollXAction.BeginInvoke(null, null);
25             }
26         }
27         private double _scrollX;
28 
29     // 竖向滚动
30         private Action _ScrollYAction;
31         private IAsyncResult _ScrollYResult;
32     
33         // 横向滚动
34         private Action _ScrollXAction;
35         private IAsyncResult _ScrollXResult;

  也便是说,在_ScrollX和_ScrollY更新的时候,程序会议及展览开1次判断,要是滚动量不为0,而且信托调用没有初始仍然已经收尾的时候,就调用委托,开端开始展览滚动。

  最后,正是编辑滚动委托调用的函数了,分别有多少个函数,在函数内以100ms为一循环往复,不停地进行滚动,当滚动到截至也许滚动量已经为0时跳出循环,退出函数执行。

威尼斯人线上娱乐 19威尼斯人线上娱乐 20滚动函数体

 1     // 竖向
 2     private void ScrollYMethod()
 3         {
 4             double endOffset = 0;
 5             if (_ScrollY < 0)       // 向上滚动
 6                 endOffset = 0;
 7             else                    // 向下滚动
 8                 ScrollViewer.Dispatcher.Invoke((Action)(() => endOffset = ScrollViewer.ScrollableHeight), null);
 9             // 初始位置
10             double offset = 0;
11             ScrollViewer.Dispatcher.Invoke((Action)(() => offset = ScrollViewer.VerticalOffset), null);
12             // 开始滚动
13             while (offset != endOffset && _ScrollY != 0)
14             {
15                 ScrollViewer.Dispatcher.Invoke((Action)(() =>
16                 {
17                     offset = ScrollViewer.VerticalOffset;
18                     ScrollViewer.ScrollToVerticalOffset(ScrollViewer.VerticalOffset + _ScrollY);
19                 }), null);
20                 Thread.Sleep(100);
21             }
22         }
23 
24     // 横向
25     private void ScrollXMethod()
26         {
27             double endOffset = 0;
28             if (_ScrollX < 0)       // 向左滚动
29                 endOffset = 0;
30             else                    // 向右滚动
31                 ScrollViewer.Dispatcher.Invoke((Action)(() => endOffset = ScrollViewer.ScrollableWidth), null);
32             // 初始位置
33             double offset = 0;
34             ScrollViewer.Dispatcher.Invoke((Action)(() => offset = ScrollViewer.HorizontalOffset), null);
35             // 开始滚动
36             while (offset != endOffset && _ScrollX != 0)
37             {
38                 ScrollViewer.Dispatcher.Invoke((Action)(() =>
39                 {
40                     offset = ScrollViewer.HorizontalOffset;
41                     ScrollViewer.ScrollToHorizontalOffset(ScrollViewer.HorizontalOffset + _ScrollX);
42                 }), null);
43                 Thread.Sleep(100);
44             }
45         }

  当然绝不遗忘,把“_ScrollYAction = ScrollYMethod;”,“_ScrollXAction

ScrollXMethod;”那两条委托开端化语句放到PALacrosseT_ContentHost_Initialized事件处理函数中去,不然就白写了。

  至此,难题2也修改达成。

  难题3:自动滚动到底层

  实际上那不是题材,而是一个改革,因为一般的滚动条都不曾这几个效应。在实用中,假若消息是不停地填写到新闻框中,理想中应有是当拖动滚动条时,不会自行把滚动条更新到近日的一条消息,而是锁定到拖动的岗位(因为自己想看的是拖动到的新闻)。其余,假诺想实时看新消息,就需求活动滚动到最底部。

  化解格局:

  当滚动条拖动到最底部时,就打开自动滚动,每来一条新音讯都滚动贰遍到最尾部。借使滚动条不在最底部就毫无自动滚动。完成方式正是为TextBox添加TextChanged=”Text博克斯_TextChanged”事件,以咬定是不是需求滚动:

1         private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
2         {
3             if (this.Text != "" && ScrollViewer != null)
4             {
5                 // 如果已经拖到最底端,则固定住
6                 if (ScrollViewer.ScrollableHeight == ScrollViewer.VerticalOffset)
7                     ScrollViewer.ScrollToBottom();
8             }
9         }

  终于码完字了,多想只贴代码啊。放个图,大家看看啊:

威尼斯人线上娱乐 21

  请无视上边的可怜莲灰横条,这是本身此外一个主次中的GridSplitter。那么些自定义控件除了支持TextBox的兼具属性外,还是能够变动配色(使用公开的属性),其它还有点击清空、关闭按钮的操作完毕都简单,不贴了,感兴趣的下载源代码看看啊。

  源代码:ScrollTest.rar

  转发请表明原址:http://www.cnblogs.com/lekko/archive/2013/02/27/2935022.html 


相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图