zoukankan      html  css  js  c++  java
  • [WPF]解决ListView在没有Items时,水平滚动条不出现的问题

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

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

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

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

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

    第一个步骤很好做。把GridViewHeaderRowPresenterItemsPresenter一起放在ListViewScrollViewer里就是了。(原先只有ItemsPresenterListViewScrollViewer里。)

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

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

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

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

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

           

    左图是原ListViewTemplate,右图是修正后的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里的SelectiveScrollingGridBugBug代码如下。

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

    其中FindVisualParent的代码,没有考虑ParentTemplateParent。其代码如下:

    复制代码
    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的源代码可以从这里下载。希望对你有所帮助。

  • 相关阅读:
    [android]Xutils具体介绍
    2015:WPS笔试-Android开发岗位
    BZOJ1355: [Baltic2009]Radio Transmission
    HDU 1421 搬寝室 (线性dp 贪心预处理)
    【独立开发人员er Cocos2d-x实战 009】Cocos2dx 菜单项CCMenu使用
    Error configuring application listener of
    SVN分支/合并原理及最佳实践
    SVN创建分支/合并分支/切换分支
    如何转载别人的文章
    让div自适应浏览器窗口居中显示
  • 原文地址:https://www.cnblogs.com/candyzkn/p/3488680.html
Copyright © 2011-2022 走看看