WPF:无法将ItemsPresenter设置为使用VirtualizingStackPanel

时间:2017-09-17 20:50:34

标签: wpf

我已经看过一些关于此的帖子,包括在StackOverflow上 - 但我找不到答案。

在WPF中,我为ControlTemplate创建了ComboBox,只需点击视觉工作室中的“编辑模板 - >复制”即可。

现在我的问题是,下拉列表中的项目显示在ItemsPresenter中,而我根本无法弄清楚如何在VirtualizingStackPanel内使用ItemsPresenter。我尝试在控件的样式中设置控件的ItemsPanel,但这没有帮助。

这是ControlTemplate的XAML。这是非常标准的。 ItemsPresenter称为PART_ItemsPresenter。

<ControlTemplate x:Key="ComboBoxControlTemplate" TargetType="{x:Type ComboBox}">
    <Grid x:Name="templateRoot" SnapsToDevicePixels="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"
                              Width="0" />
        </Grid.ColumnDefinitions>
        <Popup
            x:Name="PART_Popup"
            AllowsTransparency="True"
            Grid.ColumnSpan="2"
            IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"
            PopupAnimation="Fade"
            Placement="Bottom">
            <!--<themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">-->
            <Border
                x:Name="DropDownBorder"
                BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}"
                Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
                MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
                <ScrollViewer
                    x:Name="DropDownScrollViewer">
                    <Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
                        <Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top"
                                Width="0">
                            <Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=DropDownBorder}"
                                       Height="{Binding ActualHeight, ElementName=DropDownBorder}"
                                       Width="{Binding ActualWidth, ElementName=DropDownBorder}" />
                        </Canvas>
                        <ItemsPresenter
                            x:Name="PART_ItemsPresenter"
                            KeyboardNavigation.DirectionalNavigation="Contained"
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
                        </ItemsPresenter>
                    </Grid>
                </ScrollViewer>
            </Border>
            <!--</themes:SystemDropShadowChrome>-->
        </Popup>
        <ToggleButton
            x:Name="toggleButton"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}"
            Background="{TemplateBinding Background}"
            Grid.ColumnSpan="2"
            IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
            <ToggleButton.Style>
                <Style TargetType="{x:Type ToggleButton}">
                    <Setter Property="OverridesDefaultStyle" Value="True" />
                    <Setter Property="IsTabStop" Value="False" />
                    <Setter Property="Focusable" Value="False" />
                    <Setter Property="ClickMode" Value="Press" />
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ToggleButton}">
                                <Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}"
                                        BorderThickness="{TemplateBinding BorderThickness}"
                                        Background="{StaticResource ControlBackgroundBrush}"
                                        SnapsToDevicePixels="True">
                                    <Border x:Name="splitBorder" BorderBrush="{TemplateBinding BorderBrush}"
                                            BorderThickness="1" HorizontalAlignment="Right" Margin="0,-1,-1,-1"
                                            Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}">
                                        <Path
                                            x:Name="Arrow"
                                            Data="M0,0C0,0 3.5,4 3.5,4 3.5,4 7,0 7,0 7,0 0,0 0,0z"
                                            Fill="{TemplateBinding BorderBrush}"
                                            HorizontalAlignment="Center"
                                            VerticalAlignment="Center" />
                                    </Border>
                                </Border>
                                <ControlTemplate.Triggers>
                                    <Trigger Property="IsMouseOver" Value="True">
                                        <Setter Property="Background" TargetName="templateRoot"
                                                Value="{StaticResource ControlBackgroundMouseOverBrush}" />
                                        <Setter Property="BorderBrush" TargetName="templateRoot"
                                                Value="{StaticResource ControlBorderMouseOverBrush}" />
                                        <Setter Property="BorderBrush" TargetName="splitBorder"
                                                Value="{StaticResource ControlBorderMouseOverBrush}" />
                                        <Setter Property="Fill" TargetName="Arrow"
                                                Value="{StaticResource ControlBorderMouseOverBrush}" />
                                    </Trigger>
                                    <Trigger Property="IsPressed" Value="True">
                                        <Setter Property="Background" TargetName="templateRoot" Value="#FFC4E5F6" />
                                        <Setter Property="BorderBrush" TargetName="templateRoot" Value="#FF2C628B" />
                                    </Trigger>
                                    <Trigger Property="IsEnabled" Value="False">
                                        <Setter Property="Background" TargetName="templateRoot" Value="#FFF4F4F4" />
                                        <Setter Property="BorderBrush" TargetName="templateRoot" Value="#FFADB2B5" />
                                        <Setter Property="Background" TargetName="splitBorder" Value="#FFF4F4F4" />
                                        <Setter Property="BorderBrush" TargetName="splitBorder" Value="#FFADB2B5" />
                                        <Setter Property="Fill" TargetName="Arrow"
                                                Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ToggleButton.Style>
        </ToggleButton>
        <Border x:Name="Border" Background="{StaticResource ControlBackgroundBrush}"
                Margin="{TemplateBinding BorderThickness}">
            <TextBox x:Name="PART_EditableTextBox"
                     HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                     IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}"
                     Margin="{TemplateBinding Padding}"
                     VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
                <TextBox.Style>
                    <Style TargetType="{x:Type TextBox}">
                        <Setter Property="OverridesDefaultStyle" Value="True" />
                        <Setter Property="AllowDrop" Value="True" />
                        <Setter Property="MinWidth" Value="0" />
                        <Setter Property="MinHeight" Value="0" />
                        <Setter Property="FocusVisualStyle" Value="{x:Null}" />
                        <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" />
                        <Setter Property="Stylus.IsFlicksEnabled" Value="False" />
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type TextBox}">
                                    <ScrollViewer
                                        x:Name="PART_ContentHost"
                                        Template="{Binding PART_ItemsPresenter.Template}"
                                        Background="Transparent"
                                        Focusable="False"
                                        HorizontalScrollBarVisibility="Hidden"
                                        VerticalScrollBarVisibility="Hidden" />
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </TextBox.Style>
            </TextBox>
        </Border>
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="Opacity" TargetName="Border" Value="0.56" />
        </Trigger>
        <Trigger Property="IsKeyboardFocusWithin" Value="True">
            <Setter Property="Foreground" Value="Black" />
        </Trigger>
        <!--<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="True">
            <Setter Property="Margin" TargetName="shadow" Value="0,0,5,5"/>
            <Setter Property="Color" TargetName="shadow" Value="#71000000"/>
        </Trigger>-->
        <Trigger Property="HasItems" Value="False">
            <Setter Property="Height" TargetName="DropDownBorder" Value="95" />
        </Trigger>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsGrouping" Value="True" />
                <Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="False" />
            </MultiTrigger.Conditions>
            <Setter Property="ScrollViewer.CanContentScroll" Value="False" />
        </MultiTrigger>
        <Trigger Property="CanContentScroll" SourceName="DropDownScrollViewer" Value="False">
            <Setter Property="Canvas.Top" TargetName="OpaqueRect"
                    Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}" />
            <Setter Property="Canvas.Left" TargetName="OpaqueRect"
                    Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}" />
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

3 个答案:

答案 0 :(得分:1)

这应该是ComboBox样式中所需的全部内容:

<Setter Property="VirtualizingStackPanel.IsVirtualizing"
        Value="True" />
<Setter Property="VirtualizingStackPanel.VirtualizationMode"
        Value="Recycling" />
<Setter Property="ItemsPanel">
  <Setter.Value>
    <ItemsPanelTemplate>
      <VirtualizingStackPanel
        IsItemsHost="True"
        IsVirtualizing="{Binding RelativeSource={RelativeSource TemplatedParent},
                                 Path=(VirtualizingStackPanel.IsVirtualizing)}"
        VirtualizationMode="{Binding RelativeSource={RelativeSource TemplatedParent},
                                     Path=(VirtualizingStackPanel.VirtualizationMode)}" />
    </ItemsPanelTemplate>
  </Setter.Value>
</Setter>

您是否使用StandardRecycling虚拟化模式取决于您。 Recycling模式往往会导致ComboBox上出现一些奇怪的瑕疵。例如:当您滚动时,您可能会看到多个项目已突出显示&#39;已选中&#39;效果(但一次只能看到一个以上)。

然而,有一个问题来完成这项工作:您需要将PART_ItemsPresenter重命名为其他内容(我将其重命名为ItemsPresenter)。我无法确定为什么,但当您使用名称PART_ItemsPresenter时,您的ItemsPanelTemplate无法应用虚拟化正以某种方式被否定。我可以看到模板正在应用,但是显然有太多的项容器被生成,这使得使用虚拟化面板相当无意义。无论原因是什么,只需给出ItemsPresenter不同的名称即可解决问题。

正如@ mm8指出的那样,您还应该在MaxHeight="{TemplateBinding MaxDropDownHeight}"上设置DropDownBorder,否则您的弹出窗口会尝试增长以容纳所有项目。

最后,我不确定您在下面添加绑定时的意图是什么,但您不能仅在属性路径中添加命名引用来设置绑定源。无论你的意图是什么,这种约束都不会满足它。我只是摆脱它。

<ScrollViewer x:Name="PART_ContentHost"
              Template="{Binding PART_ItemsPresenter.Template}"
              ... />

答案 1 :(得分:0)

您需要做很多事情来支持虚拟化。设置ItemsPanelTemplate是解决方案的一部分:

<Setter Property="VirtualizingPanel.IsVirtualizing" Value="True" />
<Setter Property="ScrollViewer.CanContentScroll" Value="True" />
<Setter Property="ItemsPanel">
    <Setter.Value>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </Setter.Value>
</Setter>

答案 2 :(得分:0)

ItemsPanel设置为VirtualizingStackPanel确实有效:

<Setter Property="ItemsPanel">
    <Setter.Value>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </Setter.Value>
</Setter>

但是,为了让实际的虚拟化工作,您还应该在模板中设置MaxHeight元素的DropDownBorder属性(或者不要注释SystemDropShadowChrome } element):

<Border x:Name="DropDownBorder" MaxHeight="{TemplateBinding MaxDropDownHeight}" ...