ComboBox样式导致下拉列表非常慢

时间:2015-12-02 18:44:27

标签: c# wpf xaml combobox

我的ItemsSource组合框中有大约800个项目,当我第一次打开ComboBox时,需要花费很长时间(1-3秒)来显示弹出窗口,就像它生成它一样,但是,如果我禁用了ComboBox的风格几乎立即显示,没有任何减速。我已经尝试了我在网上阅读的所有内容(VirtualizingStack面板无处不在,禁用手写笔支持,触摸支持等等)但我不知道是什么导致了这种减速。下面是我的XAML代码:

<Style x:Key="FocusVisual">
  <Setter Property="Control.Template">
    <Setter.Value>
      <ControlTemplate>
        <Rectangle StrokeDashArray="1 2" StrokeThickness="1" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" Margin="2"/>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>
<Style x:Key="ComboBoxToggleButton" 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" SnapsToDevicePixels="true" Background="White" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="#e0e0e0">
          <Border x:Name="splitBorder" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" SnapsToDevicePixels="true" Margin="0" HorizontalAlignment="Right" BorderThickness="1" BorderBrush="Transparent">
            <Path x:Name="arrow" VerticalAlignment="Center" Margin="0" HorizontalAlignment="Center" Fill="Black" Data="F1 M 0,0 L 2.667,2.66665 L 5.3334,0 L 5.3334,-1.78168 L 2.6667,0.88501 L0,-1.78168 L0,0 Z"/>
          </Border></Border>
          <ControlTemplate.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
              <Setter Property="Fill" TargetName="arrow" Value="#3498db">
            </Setter>
          </Trigger>
            <Trigger Property="IsPressed" Value="True">
              <Setter Property="BorderBrush" TargetName="templateRoot" Value="#3498db">
            </Setter>
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
    <ControlTemplate x:Key="ComboBoxTemplate" 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>
        <VirtualizingStackPanel IsVirtualizing="True" VirtualizationMode="Recycling">
          <Popup x:Name="PART_Popup" AllowsTransparency="false" Grid.ColumnSpan="2"
                       IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                       Margin="1" MaxHeight="160" MinHeight="0" Placement="Bottom" MinWidth="{TemplateBinding ActualWidth}" >
            <Border x:Name="dropDownBorder" BorderBrush="#e0e0e0" BorderThickness="1" Background="White">
              <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>
                  <VirtualizingStackPanel  IsItemsHost="True" Orientation="Vertical" VirtualizationMode="Recycling" IsVirtualizing="True" x:Name="ItemsPresenter"

                                                KeyboardNavigation.DirectionalNavigation="Contained"
                                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
                </VirtualizingStackPanel>
              </Grid>
            </ScrollViewer>
          </Border>
        </Popup>
      </VirtualizingStackPanel>
        <ToggleButton x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}"
                              BorderThickness="{TemplateBinding BorderThickness}"
                              Background="{TemplateBinding Background}" Grid.ColumnSpan="2"
                              IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                              Style="{StaticResource ComboBoxToggleButton}" />
        <ContentPresenter x:Name="contentPresenter"
                                  ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
                                  ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
                                  Content="{TemplateBinding SelectionBoxItem}"
                                  ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
                                  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                  IsHitTestVisible="false" Margin="{TemplateBinding Padding}"
                                  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
      </Grid>
      <ControlTemplate.Triggers>
        <Trigger Property="HasItems" Value="false">
          <Setter Property="Height" TargetName="dropDownBorder" Value="95"/>
        </Trigger>
      </ControlTemplate.Triggers>
    </ControlTemplate>
  </Style>
  <Style TargetType="{x:Type ComboBox}">
    <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
    <Setter Property="Background" Value="White"/>
    <Setter Property="BorderBrush" Value="#e0e0e0"/>
    <Setter Property="Foreground" Value="Black"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden"/>
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
    <Setter Property="ScrollViewer.PanningMode" Value="None"/>
    <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
    <Setter Property="OverridesDefaultStyle" Value="True">
  </Setter>
    <Setter Property="Template" Value="{StaticResource ComboBoxTemplate}"/>
  </Style>
  <Style TargetType="{x:Type ComboBoxItem}">
    <Setter Property="OverridesDefaultStyle" Value="True">
  </Setter>
    <Setter Property="SnapsToDevicePixels" Value="true"/>
    <Setter Property="Width" Value="Auto">
  </Setter>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="ComboBoxItem">
          <Border
                            Name="Border"
                            Padding="2"
                            SnapsToDevicePixels="true">
            <VirtualizingStackPanel IsVirtualizing="True" VirtualizationMode="Recycling">
              <ContentPresenter />
            </VirtualizingStackPanel>
          </Border>
          <ControlTemplate.Triggers>
            <Trigger Property="IsHighlighted" Value="true">
              <Setter TargetName="Border" Property="Background"
                                        Value="#f0f0f0" />
            </Trigger>
            <Trigger Property="IsEnabled" Value="false">
              <Setter Property="Foreground" Value="Gray" />
            </Trigger>
          </ControlTemplate.Triggers>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

以下是我将其添加到表单中的方式..

<ComboBox x:Name="GameCombobox"  Margin="20,0,18,0" Height="25" ItemsSource="{Binding Games, Mode=OneWay, Source={x:Static ui:Ui.Instance}}" SelectedValue="Name" IsEditable="False">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <VirtualizingStackPanel Orientation="Vertical" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling" Height="45" Width="Auto">
                <Label FontFamily="Resources/Fonts/#Lato" FontSize="14px" Foreground="Black"   Content="{Binding Name}"/>
                <VirtualizingStackPanel Orientation="Horizontal" VirtualizingPanel.VirtualizationMode="Recycling" VirtualizingPanel.IsVirtualizing="True" Margin="0 -5 0 0" >
                    <Path Margin="5 5 0 0" Fill="Crimson"  Data="M 25 12 C 11.667228 12 1.25 24.34375 1.25 24.34375 A 1.0001 1.0001 0 0 0 1.25 25.65625 C 1.25 25.65625 11.667228 38 25 38 C 38.332772 38 48.75 25.65625 48.75 25.65625 A 1.0001 1.0001 0 0 0 48.75 24.34375 C 48.75 24.34375 38.332772 12 25 12 z M 25 14 C 27.627272 14 30.141915 14.544587 32.46875 15.375 C 34.032931 17.140338 35 19.450427 35 22 C 35 27.535732 30.535732 32 25 32 C 19.464268 32 15 27.535732 15 22 C 15 19.45074 15.935707 17.139242 17.5 15.375 C 19.834652 14.538846 22.362198 14 25 14 z M 14.1875 16.84375 C 13.439134 18.407614 13 20.155051 13 22 C 13 28.616268 18.383732 34 25 34 C 31.616268 34 37 28.616268 37 22 C 37 20.163179 36.580282 18.404914 35.84375 16.84375 C 41.492764 19.714987 45.555865 23.87765 46.59375 25 C 44.969234 26.756721 35.970973 36 25 36 C 14.029027 36 5.0307657 26.756721 3.40625 25 C 4.4456392 23.876024 8.5256535 19.715345 14.1875 16.84375 z M 25 17 C 22.238576 17 20 19.238576 20 22 C 20 24.761424 22.238576 27 25 27 C 27.761424 27 30 24.761424 30 22 C 30 19.238576 27.761424 17 25 17 z">
                        <Path.RenderTransform>
                            <ScaleTransform ScaleY="0.3" ScaleX="0.3"/>
                        </Path.RenderTransform>
                    </Path>
                    <Label Margin="-30 0 0 0" FontFamily="Resources/Fonts/#Lato" FontSize="14px" Foreground="#959699"  Content="{Binding Players}"/>
                </VirtualizingStackPanel>
            </VirtualizingStackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
    <ComboBox.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel IsVirtualizing="True" VirtualizationMode="Recycling"></VirtualizingStackPanel>
        </ItemsPanelTemplate>
    </ComboBox.ItemsPanel>
</ComboBox>

1 个答案:

答案 0 :(得分:0)

减速是由于DataTemplate的{​​{1}} ItemsTemplate非常“{1}}造成的。即使你使虚拟化工作,你可能会发现ComboBox这么大的滚动速度很慢,因为所有这些UI对象都将被实例化并呈现,然后在每次实现项目和虚拟化时分别处理。您可以做一些事情来使它“更轻”(更少和更简单的视觉对象),但仍然具有相同的外观。

  1. DataTemplate内的VirtualizingStackPanels完全没必要。如果你想要虚拟化,你不要把这些“放在任何地方”,你把它们放在一个非常具体的位置:DataTemplate用于你正在使用的ItemsPanelTemplate(在这种情况下是ItemsControl
  2. 使用简单的ComboBox代替TextBlock(这也可以消除您对负边距的需求)
  3. 您定义的非常复杂的Label绘图正在为ComboBox中的每个项目实例化并重新渲染。不要将其放在Path内,而是将该复杂DataTemplate的{​​{1}}作为静态资源创建,并将其用作简单的填充VisualBrush。由于Brushes是“freezable”,因此可以重用它们,Path只会被实例化并呈现一次。我的猜测是Rectangle可能是你所看到的绝大部分糟糕表现的罪魁祸首(VisualStudio中的分析会告诉你究竟是什么东西需要这么长时间才能进行布局/渲染)。只渲染一次而不是800次,应该会有很多帮助。
  4. 您的VisualBrush(在某处定义为StaticResource):

    Path

    然后你的ComboBox:

    Path

    您可能需要使用一些边距,以及VisualBrush的ViewBox和ViewPort的大小以及矩形的宽度/高度,以使其看起来与您之前的模板完全相同。我没有方便的XAML渲染器来确切了解你的用途。