web-dev-qa-db-fra.com

Problèmes de performance WPF ComboBox en liant une grande collection

J'essaie de lier une grande collection à une ComboBox et j'ai rencontré des problèmes de performances lors de l'ouverture de la fenêtre contextuelle de ComboBox. J'ai cherché sur Internet et constaté que l'utilisation de VirtualizingStackPanel en tant que modèle de panneau d'éléments pouvait aider, mais cela n'a aidé que partiellement. Si je lie une grande collection à une zone de liste déroulante, je peux ouvrir le menu contextuel très rapidement, ce n'est pas grave, mais si, après cela, je lie une autre collection à une zone de liste déroulante et que j'essaie d'ouvrir à nouveau ce dernier, cela devient très lent. La même chose se produit si vous ouvrez une fenêtre contextuelle pour une zone de liste déroulante vide, puis liez une grande collection et essayez de la rouvrir. Il faut quelques secondes avant que celle-ci ne s'ouvre.

Voici le XAML:

<ComboBox Name="cbBlah">
    <ComboBox.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </ComboBox.ItemsPanel>
</ComboBox>

et l'exemple de code de liaison pour reproduire le problème:

var list = new List<string>();
for (var i = 0; i < new Random().Next(9000, 10000); i++)
    list.Add(i.ToString());
cbBlah.ItemsSource = list;

J'ai essayé de créer un panneau de pile de virtualisation ressemblant à ceci:

<VirtualizingStackPanel VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" />

mais cela n’aide en rien, VirtualizationMode semble être ignoré, donc la fenêtre contextuelle s’ouvre très rapidement seulement la première fois, puis, chaque fois que les modifications de liaison sont effectuées, elles sont très lentes.

UPDATE: J'ai envisagé de ne pas lier une nouvelle collection à chaque fois, mais de lier une fois ObservableCollection, puis de modifier simplement son contenu. Même chose, dès que le contenu de la collection change, l’ouverture d’un popup prend encore quelques secondes :(

41
Alexey

Selon ce blog: http://vbcity.com/blogs/xtab/archive/2009/12/15/wpf-using-a-virtualizingstackpanel-to-improve-combobox-performance.aspx

Je l'ai testé avec ce code:

<ComboBox Name="cbBlah" ItemsSource="{Binding}">
    <ComboBox.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </ComboBox.ItemsPanel>
</ComboBox>

Cela fonctionne bien pour la première fois et les prochaines fois. Il n'est pas nécessaire de coder ces lignes: 

<VirtualizingStackPanel VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" />

J'espère que ceci vous aide.

91
Miguel

J'ai eu le problème avec la performance lente aussi. Mais j'avais créé une classe qui a hérité de la forme Combobox, j'aimerais donc le faire par programmation. Voici donc cette solution pour les autres googlers.

ItemsPanel = new ItemsPanelTemplate();
var stackPanelTemplate = new FrameworkElementFactory(typeof (VirtualizingStackPanel));
ItemsPanel.VisualTree = stackPanelTemplate;
9
jonas

Je viens de rencontrer ce problème aussi. J'utilise ce code dans une liste déroulante personnalisée avec un modèle de style. Lorsque j'ai exécuté mon code en mode de débogage VS, la virtualisation ne fonctionnait pas correctement. Une fois que je l'ai exécuté en dehors du débogage, je peux changer le contenu de ObservableCollection sans verrouiller l'interface utilisateur. Cela pourrait également être utile si vous définissez une hauteur et une largeur maximales.

<Setter Property="ScrollViewer.CanContentScroll" Value="True"/> 
<Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"/>
<Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling"/>
<Popup>
    <Border/>
    <ScrollViewer>
      <VirtualizingStackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained"/>
    </ScrollViewer> 
  </Grid>
</Popup>
0
Phillip