• WPF中如何为ItemsControl添加ScrollViewer并显示ScrollBar


        今天在开发的过程中突然碰到了一个问题,本来的意图是想当ItemsControl中加载的Item达到一定数量时,会出现ScrollViewer并出现垂直的滚动条,但是实际上并不能够达成目标,对于熟手来说这个问题非常简单,但是如果不了解WPF的模板的原理,可能并不清楚这些,这里举出一个例子来论证。

    <Window x:Class="TestItemsControl.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <Style x:Key="ItemsControlStyle1" TargetType="{x:Type ItemsControl}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ItemsControl}">
                            <Border BorderBrush="{TemplateBinding BorderBrush}" 
                                    BorderThickness="{TemplateBinding BorderThickness}" 
                                    Background="{TemplateBinding Background}" 
                                    Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True">
                                    <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Window.Resources>
        <Grid>
            <ItemsControl Width="100" 
                          Height="100" 
                          Background="Teal"
                          Style="{DynamicResource ItemsControlStyle1}">
                    <TextBox Text="1" Foreground="Red" Height="20" TextAlignment="Center"></TextBox>
                    <TextBox Text="1" Foreground="Red" Height="20" TextAlignment="Center"></TextBox>
                  <TextBox Text="2" Foreground="Red" Height="20" TextAlignment="Center"></TextBox>
                  <TextBox Text="3" Foreground="Red" Height="20" TextAlignment="Center"></TextBox>
                  <TextBox Text="4" Foreground="Red" Height="20" TextAlignment="Center"></TextBox>
                  <TextBox Text="5" Foreground="Red" Height="20" TextAlignment="Center"></TextBox>
                  <TextBox Text="6" Foreground="Red" Height="20" TextAlignment="Center"></TextBox>
            </ItemsControl>
        </Grid>
    </Window>
    

      执行上述代码我们会发现不会出现ScrollBar,我们定义了ItemsControl的高度为100,当下面的Item超过了这个高度后多出的部分直接被剪切掉了,通过查看Window.Resources中的模板,那么我们可以很好理解,因为ItemsControl的结构是一个Border里面嵌套了一个ItemsPresenter,根本么有ScrollViewer,所以当然不会出现ScrollBar。这个问题非常好解决,直接修改ItemsControl的模板,在Border里面加上一个ScrollViewer,问题解决。

     <Style x:Key="ItemsControlStyle1" TargetType="{x:Type ItemsControl}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ItemsControl}">
                            <Border BorderBrush="{TemplateBinding BorderBrush}" 
                                    BorderThickness="{TemplateBinding BorderThickness}" 
                                    Background="{TemplateBinding Background}" 
                                    Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True">
                                <ScrollViewer>
                                    <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                </ScrollViewer>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
    

      效果如下所示:

        下面来一步步深入,讨论一些复杂的问题。

        在进行最终的问题解释之前,首先来探讨一下ItemsControl这个控件。

        MSDN的解释是:表示用于呈现的项的集合的控件。

        再看一下它的集成关系:

       System.Object
      System.Windows.Threading.DispatcherObject
        System.Windows.DependencyObject
          System.Windows.Media.Visual
            System.Windows.UIElement
              System.Windows.FrameworkElement
                System.Windows.Controls.Control
                  System.Windows.Controls.ItemsControl
                    System.Windows.Controls.HeaderedItemsControl
                    System.Windows.Controls.Primitives.DataGridCellsPresenter
                    System.Windows.Controls.Primitives.DataGridColumnHeadersPresenter
                    System.Windows.Controls.Primitives.MenuBase
                    System.Windows.Controls.Primitives.Selector
                    System.Windows.Controls.Primitives.StatusBar
                    System.Windows.Controls.Ribbon.RibbonContextualTabGroupItemsControl
                    System.Windows.Controls.Ribbon.RibbonControlGroup
                    System.Windows.Controls.Ribbon.RibbonGallery
                    System.Windows.Controls.Ribbon.RibbonQuickAccessToolBar
                    System.Windows.Controls.Ribbon.RibbonTabHeaderItemsControl
                    System.Windows.Controls.TreeView

        通过这些继承关系,我们可以发现ItemsControl是很多包含Items的集合的控件的基类,比如ListBox还有TreeView等等。

        关于ItemsControl中有几个非常重要的概念需要理解:

       1 Template 这个不用说ItemsControl的模板,用于展现ItemsControl最终由什么构成,即外表呈现。   

    <ItemsControl.Template>
                                    <ControlTemplate TargetType="ItemsControl">
                                        <ScrollViewer x:Name="scrollViewer"
                                                      VerticalScrollBarVisibility="Auto" 
                                                      Padding="5">
                                            <ItemsPresenter ></ItemsPresenter>
                                        </ScrollViewer>
                                    </ControlTemplate>
                                </ItemsControl.Template>
    

      2 ItemsPanel属性,这个非常重要,这个是Items项的父容器,它决定了Items以何种方式去呈现,比如常用的Grid、 StackPanel、WrapPanel、UniformGrid、DockPanel等,甚至可以是自定义的Panel。

     <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <WrapPanel  Width="Auto"
                                                    Height="Auto"
                                                    MaxWidth="500"
                                                    IsItemsHost="True"
                                                    HorizontalAlignment="Left"
                                                    VerticalAlignment="Center">
                                        </WrapPanel>
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
    

      3 ItemTemplate,这个属性表示每个Item将以何种方式呈现,有了这三种属性我们就可以定义我们需要的各种形式的界面。(下面的代码稍稍复杂一些)

    <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <Grid Margin="0,0,10,3"
                                              HorizontalAlignment="Left"
                                              VerticalAlignment="Center">
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="*"></ColumnDefinition>
                                                <ColumnDefinition Width="*"></ColumnDefinition>
                                            </Grid.ColumnDefinitions>
                                            <Grid.RowDefinitions>
                                                <RowDefinition Height="*"></RowDefinition>
                                                <RowDefinition Height="*"></RowDefinition>
                                            </Grid.RowDefinitions>
                                            <Border Name="DutyPerson" 
                                                    Grid.Row="0"  
                                                    Grid.RowSpan="2" 
                                                    Grid.Column="0" 
                                                    Grid.ColumnSpan="2" 
                                                    BorderBrush="#bdbdbd"
                                                    BorderThickness="1"      
                                                    Padding="0"
                                                    Width="70"
                                                    Height="32"
                                                    ContextMenu="{StaticResource SetLeader}">
                                                <StackPanel>
                                                    <TextBox x:Name="dutyPersonTextBox"
                                                             Text="{Binding DutyPersonName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" 
                                                             BorderThickness="0"
                                                             Height="30">
                                                        <TextBox.ToolTip>
                                                            <ToolTip HorizontalOffset="-18"  
                                                                     VerticalOffset="5"
                                                                     BorderBrush="Transparent"
                                                                     Background="Transparent"
                                                                     HasDropShadow="False"
                                                                     Placement="Top"
                                                                     Visibility="{Binding IsLeader,Converter={StaticResource BoolToVisibility}}">
                                                                <Grid Margin="0">
                                                                    <Image x:Name="personToolTipImage" 
                                                                            Stretch="Uniform"
                                                                            RenderOptions.BitmapScalingMode="NearestNeighbor"
                                                                            Width="88"
                                                                            Height="36"
                                                                            VerticalAlignment="Bottom"
                                                                            Source="/AIPAnnouncement;component/ControlViews/Sources/Images/气泡.png">
                                                                    </Image>
                                                                    <TextBlock Text="领导"
                                                                               FontSize="13"
                                                                               HorizontalAlignment="Center"
                                                                               VerticalAlignment="Center">
                                                                    </TextBlock>
                                                                </Grid>
                                                            </ToolTip>
                                                        </TextBox.ToolTip>
                                                        <i:Interaction.Triggers>
                                                            <i:EventTrigger EventName="TextChanged">
                                                                <interactive:ExInvokeCommandAction Command="{Binding DataContext.ModifyDutyPersonCommand,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=my:AnnouncementApp}}"
                                                                                     CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TextBox}}">
                                                                </interactive:ExInvokeCommandAction>
                                                            </i:EventTrigger>
                                                            <i:EventTrigger EventName="GotFocus">
                                                                <interactive:ExInvokeCommandAction Command="{Binding DataContext.TextBoxGotFocus,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=my:AnnouncementApp}}"
                                                                                     CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TextBox}}">
                                                                </interactive:ExInvokeCommandAction>
                                                            </i:EventTrigger>
                                                        </i:Interaction.Triggers>
                                                    </TextBox>
                                                    <Popup x:Name="popup" 
                                                           PlacementTarget="{Binding ElementName=dutyPersonTextBox}"
                                                           Width="{Binding ActualWidth,ElementName=dutyPersonTextBox}"
                                                           IsOpen="{Binding  ElementName=dutyPersonTextBox,Path=IsKeyboardFocused, Mode=OneWay}"
                                                           StaysOpen="True">
                                                        <Grid Background="Red">
                                                            <ListBox x:Name="lb_selecthistorymembers"                          
                                                                     SnapsToDevicePixels="true" 
                                                                     ItemsSource="{Binding DataContext.SpecificHistoryMembers,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=my:AnnouncementApp},Mode=TwoWay}" 
                                                                     HorizontalAlignment="Stretch" 
                                                                     ScrollViewer.HorizontalScrollBarVisibility="Disabled" 
                                                                     Background="#fff"
                                                                     BorderThickness="1">
                                                                <i:Interaction.Triggers>
                                                                    <i:EventTrigger EventName="SelectionChanged">
                                                                        <interactive:ExInvokeCommandAction Command="{Binding DataContext.OnSelectHistoryMembersListBoxSelected,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=my:AnnouncementApp},Mode=TwoWay}"
                                                                                     CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBox}}">
                                                                        </interactive:ExInvokeCommandAction>
                                                                    </i:EventTrigger>
                                                                </i:Interaction.Triggers>
                                                                <ListBox.ItemContainerStyle>
                                                                    <Style TargetType="ListBoxItem">
                                                                        <Setter Property="Template">
                                                                            <Setter.Value>
                                                                                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                                                                    <Border x:Name="Bd" 
                                                                                            Height="Auto" 
                                                                                            Width="Auto"
                                                                                            BorderBrush="{TemplateBinding BorderBrush}"
                                                                                            BorderThickness="0" 
                                                                                            Background="{TemplateBinding Background}"                                                                                         
                                                                                            SnapsToDevicePixels="true">
                                                                                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                                                                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                                                                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                                                                                    </Border>
                                                                                    <ControlTemplate.Triggers>
                                                                                        <Trigger Property="IsEnabled" 
                                                                                                 Value="false">
                                                                                            <Setter Property="Foreground" 
                                                                                                    Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                                                                                        </Trigger>
                                                                                    </ControlTemplate.Triggers>
                                                                                </ControlTemplate>
                                                                            </Setter.Value>
                                                                        </Setter>
                                                                        <Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
                                                                        <Setter Property="VerticalAlignment" Value="Center"></Setter>
                                                                        <Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
                                                                    </Style>
                                                                </ListBox.ItemContainerStyle>
                                                                <ListBox.ItemsPanel>
                                                                    <ItemsPanelTemplate>
                                                                        <StackPanel IsItemsHost="True"
                                                                                    HorizontalAlignment="Left"
                                                                                    VerticalAlignment="Center"
                                                                                    Width="{Binding ActualWidth,ElementName=dutyPersonTextBox}"
                                                                                    >
                                                                        </StackPanel>
                                                                    </ItemsPanelTemplate>
                                                                </ListBox.ItemsPanel>
                                                                <ListBox.ItemTemplate>
                                                                    <DataTemplate>
                                                                        <Border  Name="Border"                                                                                                                        
                                                                                 BorderThickness="0">
                                                                            <Grid Margin="2,1,1,1">
                                                                                <Label x:Name="label"
                                                                                       Content="{Binding SpecificHistoryDutyPersonName}"
                                                                                       HorizontalAlignment="Stretch"
                                                                                       HorizontalContentAlignment="Center"
                                                                                       FontSize="13">
                                                                                </Label>
                                                                            </Grid>
                                                                        </Border>
                                                                        <DataTemplate.Triggers>
                                                                            <Trigger Property="IsMouseOver" 
                                                                                     Value="true">
                                                                                <Setter Property="Background"
                                                                                        Value="#00a3d9"
                                                                                        TargetName="Border">
                                                                                </Setter>
                                                                                <Setter Property="Background"
                                                                                        Value="#f8f3f0"
                                                                                        TargetName="label">
                                                                                </Setter>
                                                                            </Trigger>
                                                                        </DataTemplate.Triggers>
                                                                    </DataTemplate>
                                                                </ListBox.ItemTemplate>
                                                            </ListBox>
                                                        </Grid>
                                                    </Popup>
                                                </StackPanel>                                            
                                            </Border>
                                                                                   
                                            <xui:Button x:Name="deleteAnnouncementItem"
                                                        Grid.Row="0"     
                                                        Grid.Column="1"
                                                        Height="14" 
                                                        Width="14" 
                                                        Opacity="0"
                                                        HorizontalAlignment="Right"
                                                        VerticalAlignment="Top">
                                                        <i:Interaction.Triggers>
                                                            <i:EventTrigger EventName="Click">
                                                                <interactive:ExInvokeCommandAction 
                                                                            Command="{Binding DataContext.DutyPersonDeleteCommand,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=my:AnnouncementApp}}"
                                                                            CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=xui:Button}}">
                                                                </interactive:ExInvokeCommandAction>
                                                            </i:EventTrigger>
                                                        </i:Interaction.Triggers>
                                                <Button.Background>
                                                    <ImageBrush ImageSource="/AIPAnnouncement;Component/ControlViews/Sources/Images/关闭.png">
                                                    </ImageBrush>
                                                </Button.Background>
                                            </xui:Button>
                                        </Grid>
                                        <DataTemplate.Triggers>                                        
                                            <DataTrigger Binding="{Binding IsLeader}" 
                                                         Value="true">                                           
                                                <Setter Property="BorderBrush" 
                                                        Value="#f00" 
                                                        TargetName="DutyPerson">
                                                </Setter>
                                            </DataTrigger>
                                            <Trigger Property="IsMouseOver" 
                                                     Value="true"
                                                     SourceName="DutyPerson">
                                                <Setter Property="BorderThickness" 
                                                        Value="0" 
                                                        TargetName="DutyPerson">
                                                </Setter>                                            
                                            </Trigger>
                                            <Trigger Property="IsMouseOver" 
                                                     Value="true"
                                                     SourceName="deleteAnnouncementItem">                                           
                                                <Setter Property="Opacity" 
                                                        Value="1"
                                                        TargetName="deleteAnnouncementItem">
                                                </Setter>
                                            </Trigger>
                                        </DataTemplate.Triggers>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
    

      这里面的每一个Item定义成了类似于百度的搜索框一样的东西,当我们在文本输入框中输入文字时,会弹出一个Popup,里面是一个ListBox,我们可以从中挑选我们需要的选项,最后加入到TextBox中去,这里做了一个模板,在每一个TextBox下面添加一个Popup,当输入文字时会自动检索当前历史记录中是否存在当前项,这里面的核心是 IsOpen="{Binding  ElementName=dutyPersonTextBox,Path=IsKeyboardFocused,  Mode=OneWay}"  StaysOpen=”true” 这句的意思表示当前的Popup是否打开是取决于dutyPersonTextBox(一个TextBox控件)是否获得鼠标的焦点,这里使用IsKeyboardFocused来表示鼠标是否获取到焦点,后面我们会看一看具体效果的图片。

        刚开始的时候,没有很多思考,当自己定义ItemsControl的ItemsPanel时,给它赋了一个定值,这里就埋下了一个很大的隐患,所以我们在不断的往ItemsControl中添加Item的项时,ItemsControl的高度只会维持在70,因为Items的容器ItemsPanel的高度就决定了ItemsControl的高度,当超过这个高度的时候会自动地去剪裁掉多余的部分,这是WPF的一个基本机理,所以我们在设置ItemsPanel的容器WrapPanel的时候一定要将Height设置为Auto,这样我们就能看到ItemsPanel的高度自动增加,这是其中一个方面,另外一个方面就是当我们必须设置ItemsPanel的高度或者是其父容器的高度为一个固定值假设为FixHeight,这样当随着Item的项的增多,ItemsPanel容器的高度超过FixHeight时,我们就会发现ScrollBar会出现,这些东西都是需要我们去不断地思考和总结的一些结论。     WPF的这种机理在很多的地方都是可以看到的,例如当我们往WrapPanel中添加项目时,为了保证能够使添加的项自动添加到第二行,那么我们必须为WrapPanel设置一个宽度,这样当我们添加项时才会自动跳转到下一行,因为如果我们不设置这个值,默认的高度和宽度都是Auto,这个在使用中必须要十分注意,并且平时多积累,才能真正地学以致用。

          今天就总结这么多,最后看一看最终的效果,其中第三行人员这一行就是使用ItemsControl做出来的效果。

  • 相关阅读:
    设计模式之工厂模式 练习
    c++智能指针(1)
    记录下 UTF6 GBK 转换函数
    ip白名单 通过* ? 检测IP匹配 轻量级
    stl学习记录(2)
    boost 学习(1)
    stl string 小练习
    stl string 使用指定的分隔符分割成数个子字符串
    [open source] skinbuilder发布
    Builder模式实例分析(C语言版)
  • 原文地址:https://www.cnblogs.com/seekdream/p/5553228.html
Copyright © 2020-2023  润新知