转载地址:http://www.cnblogs.com/nankezhishi/archive/2010/03/19/FixListViewNotScrollHeaderBug.html

上一篇Blog中指出了ListView在没有Items时,即使Header超出了控件范围,水平滚动条也不会出现的问题。由于篇幅和时间所限,没有给出解决方案。下面就介绍一种解决方案。具体问题请参考上篇文章,这里就不赘述了。

解决这个问题分两个步骤:

1.       把Header放到ScrollViewer中可以Scroll的部分里。

2.       让Header在拖动垂直滚动条时不动。(难点,明明在ScrollViewer里,却不能动。)

第一个步骤很好做。把GridViewHeaderRowPresenter和ItemsPresenter一起放在ListView的ScrollViewer里就是了。(原先只有ItemsPresenter在ListView的ScrollViewer里。)

这里要感谢一下ListView的优秀设计——真正做到了Logic和Visual的分离。只要把GridViewHeaderRowPresenter的XAML剪切到另一个位置就可以了。什么都不用改。我们在实现自己的控件时,也应该能够达到这种易用度。

但是第二个步骤就不那么容易了。直到我在WPF Toolkit里看到了一个振奋人心的类名——SelectiveScrollingGrid。第一感觉就是这正是我想要的功能。今天终于有空研究研究了。

结果发现被忽悠了。这个名字很能蛊惑人心,看名字会以为是,在这个Grid中,可以指定哪些东西会Scroll,哪些东西不会Scroll。其实我应该猜得到了,微软也不傻,做个功能还是很倾向于用简单的方法的。这个SeletiveScrollingGrid没有控制内部的东西Scroll不Scroll,全都Scroll,只是把一部分的Child,用RenderTransform拉回原位,看起来象是没有动一样。

看它的实现方式,这个Grid的名字应该叫RenderBackOnScrollGrid。用RenderTransform和真正的SelectiveScrolling有什么区别呢?请参考错位的RenderTransform动画。我猜在这里也会有潜在的问题的。

好了言归正传,我们来着手解决第二个问题。解决方案如下画所示:

       

左图是原ListView的Template,右图是修正后的ListView Template。文字描述就省了。其中SelectiveScrollingGrid的代码是。


SelectiveScrollingPart

<local:SelectiveScrollingGrid>
    <local:SelectiveScrollingGrid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition/>
    </local:SelectiveScrollingGrid.RowDefinitions>
    <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Grid.Row="1"/>
    <ScrollViewer HorizontalScrollBarVisibility="Hidden"
                  VerticalScrollBarVisibility="Hidden"                     
              local:SelectiveScrollingGrid.SelectiveScrollingOrientation="Horizontal">
        <GridViewHeaderRowPresenter
            Margin="2,0,2,0"
            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
            AllowsColumnReorder="{Binding View.AllowsColumnReorder, RelativeSource={RelativeSource TemplatedParent}}"
            ColumnHeaderContainerStyle="{Binding View.ColumnHeaderContainerStyle, RelativeSource={RelativeSource TemplatedParent}}"
            ColumnHeaderContextMenu="{Binding View.ColumnHeaderContextMenu, RelativeSource={RelativeSource TemplatedParent}}"
            ColumnHeaderStringFormat="{Binding View.ColumnHeaderStringFormat, RelativeSource={RelativeSource TemplatedParent}}"
            ColumnHeaderTemplate="{Binding View.ColumnHeaderTemplate, RelativeSource={RelativeSource TemplatedParent}}"
            ColumnHeaderTemplateSelector="{Binding View.ColumnHeaderTemplateSelector, RelativeSource={RelativeSource TemplatedParent}}"
            ColumnHeaderToolTip="{Binding View.ColumnHeaderToolTip, RelativeSource={RelativeSource TemplatedParent}}"
            Columns="{Binding View.Columns, RelativeSource={RelativeSource TemplatedParent}}"/>
    </ScrollViewer>
</local:SelectiveScrollingGrid>

注意到SelectiveScrollingGrid所在的Namespace了么?叫local,没有错,我没有用WPF Toolkit里的SelectiveScrollingGrid,而是自己又写了一个。

原因很简单,WPF Toolkit里的SelectiveScrollingGrid有Bug。Bug代码如下。

ScrollViewer viewer = DataGridHelper.FindVisualParent<ScrollViewer>(element);

其中FindVisualParent的代码,没有考虑Parent和TemplateParent。其代码如下:


FindVisualParent in WPF Toolkit

public static T FindVisualParent<T>(UIElement element) where T: UIElement
{
    for (UIElement element2 = element; element2 != null; element2 = VisualTreeHelper.GetParent(element2) as UIElement)
    {
        T local = element2 as T;
        if (local != null)
        {
            return local;
        }
    }
    return default(T);
}

在这个FindVisualParent中,只用VisualTreeHelper去向上找Parent。但是有时这个VisualTreeHelper会莫名其妙地返回Null,而Parent属性却不是Null。

所以我做的只不过是想重写一下FindVisualParent的逻辑成。


My FindAncestor

public static T FindAncestor<T>(this DependencyObject element)
    where T : DependencyObject
{
    if (element == null)
    {
        return null;
    }
 
    DependencyObject ancestor = VisualTreeHelper.GetParent(element);     if (element is FrameworkElement)
    {
        ancestor = ancestor ?? ((element as FrameworkElement).Parent ?? (element as FrameworkElement).TemplatedParent);
    }     //While, I expend CSharp compiler could handle 尾递归 sometime.
    return ancestor is T ? ancestor as T : FindAncestor<T>(ancestor);
}

但是不幸的是,所有的相关函数都是static的。虽然SelectiveScrollingGrid不是sealed,但是继承他也改变不了什么。所以只能自己重新写一个。

我承认没有什么产品没有Bug,但是我希望MS能让产品的设计更灵活一点。比如少用Static做工具方法,多用策略模式或模版函数。这样就不必在想要改变一下默认行为的时候,非把整个类甚至Assembly重写了。

又扯远了,看下结果:

水平滚动条正常。再加些数据。测试一下垂直滚动。

一切正常。其实还有一个小Bug,看看大家能发现不?

这个Fix的源代码可以从这里下载。希望对你有所帮助。

[WPF]解决ListView在没有Items时,水平滚动条不出现的问题的更多相关文章

  1. 解决ListView和ScrollView同时使用时滑动的冲突问题

    ScrollView外面包裹了一个ListView,解决其滑动冲突问题: 只需自定义ListView,命名MyListView: public class MyListView extends Lis ...

  2. WPF Bug清单之(13)——应该出现却没有出现的ListView水平滚动条

    转载地址:http://www.cnblogs.com/nankezhishi/archive/2010/03/17/wpfbug13.html 我们知道ListView在内容超出控件本身范围时,默认 ...

  3. 解决Android ListView 和 ScrollView 共存时冲突 问题 方法其一

    转载请注明出处: http://www.goteny.com/articles/2013/11/8.html http://www.cnblogs.com/zjjne/p/3428480.html 当 ...

  4. 解决WPF程序中ListBox ItemsSource变化时不重置ScrollBar的问题

    解决WPF程序中ListBox ItemsSource变化时不重置ScrollBar的问题 当我们改变ListBox的ItemsSource时,会发现这样一个问题:数据源变化时,虽然控件中的内容会跟着 ...

  5. universal image loader在listview/gridview中滚动时重复加载图片的问题及解决方法

    在listview/gridview中使用UIL来display每个item的图片,当图片数量较多需要滑动滚动时会出现卡顿,而且加载过的图片再次上翻后依然会重复加载(显示设置好的加载中图片) 最近在使 ...

  6. WPF解决按钮上被透明控件遮盖时无法点击问题

    原文:WPF解决按钮上被透明控件遮盖时无法点击问题 IsHitTestVisible="False" 在控件上设置如上属性即可,即可让透明控件不触发点击效果

  7. 解决WPF的ScrollViewer在使用触摸屏时,滑到尽头窗口抖动的情况

    原文:解决WPF的ScrollViewer在使用触摸屏时,滑到尽头窗口抖动的情况 wpf的ScrollViewer在触摸条件下 默认在尽头时会有一个窗口一起被拖动的FeedBack,但对用户的交互很不 ...

  8. 解决boostrap-table有水平和垂直滚动条时,滚动条滑到最右边表格标题和内容单元格无法对齐的问题

    问题:boostrap-table有水平和垂直滚动条时,滚动条不高的时候(滚动高度比较大的时候没有问题),滚动条滑到最右边表格标题和内容单元格无法对齐的问题 问题原因:bootstrap-table源 ...

  9. A WPF File ListView and ComboBox

    源码下载: Download FileListView_Version_2.zip Download FileListView_Version_2_Binaries.zip Download File ...

随机推荐

  1. RMAN之一:快速入门

    1.数据导出基础 (1)创建datapump导出文件的目录对象并为相应用户授予权限. 出于安全考虑,不允许oracle用户直接在OS上进行文件的操作,而应通过directory对象指定. SQL> ...

  2. primary key与unique的区别

    定义了 UNIQUE 约束的字段中不能包含重复值,可以为一个或多个字段定义 UNIQUE 约束.因此,UNIQUE 即可以在字段级也可以在表级定义, 在UNIQUED 约束的字段上可以包含空值.ORA ...

  3. VS2010中手动重命名项目

    在visual studio 中重命名项目名称的方法: 1. 重命名项目名称 2. 修改Assembly name 3. 修改Default namespace 4. 在Assembly Inform ...

  4. PHP环境搭配

    电脑上如果有apache,必须先卸载了先,如果有集成的环境,类似于apmserver,也必须先停止先.不然安装的时候,会出现修复和卸载选项,而不是典型安装跟用户自定义安装. apache安装目录 E: ...

  5. 恶补ASP.NET基础【1】委托

    委托(delegate)是一种可以把引用存储为函数的类型. 委托的声明类似于函数,但不带函数体,且要使用delegate关键字,委托的声明指定了一个返回类型和一个参数列表. 在定义了委托之后,就可以声 ...

  6. JSON基础学习

    定义 JSON时轻量级的文本数据交换格式,独立于语言,比xml更小更快更易解析 JSON解析器和JSON库支持不同的编程语言 4个基本规则 1. 并列数据间用 逗号, 2. 映射用冒号表示 3. 并列 ...

  7. eclipse 软件的背景颜色、字体设置

    1.eclipse 背景色设置: Window->Preferences->General->Editors->Text Editors->Backgroud color ...

  8. QT下的几种透明效果(三种方法:调色板,透明度属性,自绘)

    1.窗口整体透明,但是窗体上的控件不透明.    通过设置窗体的背景色来实现,将背景色设置为全透.  QPalette pal = palette();  pal.setColor(QPalette: ...

  9. codevs1219 骑士游历

    题目描述 Description 设有一个n*m的棋盘(2≤n≤50,2≤m≤50),如下图,在棋盘上有一个中国象棋马. 规定: 1)马只能走日字 2)马只能向右跳 问给定起点x1,y1和终点x2,y ...

  10. virtualbox 复制多个虚拟机 (宿主机redhat)

    我用VirtualBox做了一个winxp虚拟镜像. 想实现不重新安装而直接复制几个,也就是同时装载几个虚拟机. 但是直接复制已安装好机子的vdi文件,系统会报uuid已存在的错误. 所以,就需要修改 ...