ListBox,ScrollViewer和CanContentScroll

时间:2010-06-12 20:25:27

标签: .net wpf

我有带ScrollViewer的ListBox

<ScrollViewer Focusable="False" CanContentScroll="True"                     
                  HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Disabled">            
         <ListBox ItemsSource="{Binding Path=MyItems}" VerticalAlignment="Stretch" Focusable="False">
                <ListBox.ItemContainerStyle>                    
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="OverridesDefaultStyle" Value="true"/>
                    <Setter Property="HorizontalContentAlignment"  Value="Stretch"/>                        
                    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                <Border CornerRadius="3,3,3,3">
                                    <Grid>
                                        <myControls:MyControl/>
                                    </Grid>    
                                </Border>     
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ListBox.ItemContainerStyle>
            <ListBox.Style>
                <Style TargetType="ListBox">
                    <Setter Property="SnapsToDevicePixels" Value="true"/>
                    <Setter Property="OverridesDefaultStyle" Value="true"/>
                    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
                    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
                    <Setter Property="ScrollViewer.CanContentScroll" Value="True"/>        
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="ListBox">                                    
                                <ScrollViewer Focusable="False" CanContentScroll="True">
                                    <Border>                                           
                                        <StackPanel Margin="2"           Orientation="Horizontal" IsItemsHost="True"/>                                                                           
                                    </Border>
                                </ScrollViewer>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ListBox.Style>
        </ListBox>           
    </ScrollViewer>

但CanContentScroll =“True”不起作用。它仍然以物理单位滚动。 我的代码有什么问题?谢谢!

1 个答案:

答案 0 :(得分:3)

问题是ScrollViewer和StackPanel之间的边界,这阻止了ScrollViewer找到StackPanel。 StackPanel实现了IScrollInfo接口以进行逻辑滚动,但是如果ScrollViewer找不到IScrollInfo子节点,则它会回退到物理滚动。

有三种方法可以在ScrollViewer中获取逻辑滚动:

  1. 让ScrollViewer的直接子成为可以进行逻辑滚动的面板(例如StackPanel)
  2. 让ScrollViewer的直接孩子成为提供此类面板的ItemsPresenter
  3. 让ScrollViewer的直接子成为您自己编写的实现IScrollInfo的自定义类
  4. 简单的解决方案

    前两个解决方案是不言自明的,但我想指出,使用ItemsPresenter可能比直接在模板中包含StackPanel更好。这样,解决方案中的其他ListBox可以利用ControlTemplate而不必强制使用相同的面板。换句话说,我会做这样的事情而不是你写的:

    <ListBox>
      <ListBox.ItemContainerStyle>
        ...
      </ListBox.ItemContainerStyle>
    
      <ListBox.ItemsPanel>
        <ItemContainerTemplate>
          <StackPanel Margin="2" Orientation="Horizontal" ... />
        </ItemContainerTemplate>
      </ListBox.ItemsPanel>
    
      <ListBox.Template>
        <ControlTemplate>
          <ScrollViewer Focusable="False" CanContentScroll="True">   
            <ItemsPresenter />
          </ScrollViewer>
        </ControlTemplate>
      </ListBox.Template>
    </ListBox>
    

    高级技术

    如果确实需要在逻辑滚动的StackPanel周围显示边框但是该边框似乎随数据滚动,则必须进行一些额外的管道。当StackPanel进行逻辑滚动时,StackPanel本身会在排列过程中滚动其内容,而ScrollViewer根本不会进行任何实际滚动(它只管理滚动条等)。你会发现StackPanel坚决拒绝滚动任何实际上并不是它的子项之一的东西,所以除非你的边框可以是一个真正的ListBox项,否则你需要做一些假装。

    使其看起来像StackPanel内容一样滚动:

    1. 创建一个“CompoundScrolling”自定义控件来管理它实现IScrollInfo。
    2. 为“CompoundScrolling”控件提供包含Grid的模板。
    3. 在网格中放置一个Border和StackPanel,边框在后面,StackPanel上有合适的边距。
    4. 通过调用StackPanel上的等效方法实现所有IScrollInfo属性和方法(您可以给它一个PART_名称并在OnApplyTemplate中找到它)
    5. 每当IScrollInfo方法被代理时,也会在OnMeasure,OnArrange等中,检查StackPanel的滚动状态并更新Border的位置以匹配。
    6. 检测StackPanel的滚动状态可以通过在0和Items.Count-1上调用ItemContainerGenerator.ContainerFromIndex,然后在StackPanel中检查这些容器的位置来查看它们是否可见。如果是这样,边框的顶部和底部(或左侧和右侧的Orientation = Horizo​​ntal)应该是可见的,否则它们不应该是可见的。当然,其他方面总是可见的。