我已经看过一些关于此的帖子,包括在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>
答案 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>
您是否使用Standard
或Recycling
虚拟化模式取决于您。 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}" ...