Подтвердить что ты не робот

Проблема производительности виртуализации с большими прокручиваемыми данными SL4

Проблема: Отображение больших объемов данных в прокручиваемой области имеет ужасную производительность и/или пользовательский опыт.

Пробовал: В основном задан DataTemplate в ListBox, чтобы показать сетку заполненных данных с параметром VirtualizationMode, установленным в Recycle, и фиксированной высотой, установленной в ListBox. Что-то вроде приведенного ниже примера.

 <ListBox x:Name="Items"
      TabNavigation="Once"
      VirtualizingStackPanel.VirtualizationMode="Recycling"     
      Height="500">         
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Margin="0,5">
                        <HyperlinkButton Content="Action" Margin="5"/>
                        <ContentControl  
                                cal:View.Model="{Binding}"  
                                VerticalContentAlignment="Stretch" 
                                HorizontalContentAlignment="Stretch"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

ContentControl будет приносить стандартное <Grid> из другого представления, которое форматирует общий макет заполненных элементов, состоящий из 20 статических и 20 привязанных к данным TextBlocks.

Это работает хорошо, и вырезать начальные нагрузки пополам. ОДНАКО, теперь проблема в том, что мне нужно, чтобы высота НЕ была фиксированным размером, поэтому она занимает пространство, доступное в своем родителе, и даже может быть изменено. Благодаря @DanFox я обнаружил, что вам нужно исправить высоту в той или иной форме, чтобы вызвать виртуализацию, или RenderEngine просто думает, что в любом случае у нее бесконечная комната.

Вопрос: Есть ли лучший способ сделать это, или как я могу по крайней мере исправить текущую технику, чтобы обеспечить лучший UX? Я генерирую потенциально сотни этих элементов, поэтому мне нужно повысить производительность виртуализации. Однако мне также необходимо разрешить пользователю изменять размер окна и сохранять возможность прокрутки эффективно.

Любое понимание очень ценится, спасибо и счастливые праздники!

4b9b3361

Ответ 1

Хорошо, так вот, что я закончил делать и должен сказать это огромное улучшение! Мы начали с момента загрузки 77 секунд в точке для этой конкретной части. С некоторым рефакторингом о том, как мы привязывались на спине, мы сбрили около 20 секунд, поэтому проблема все еще была в рендеринге пользовательского интерфейса. Ответ ниже, очевидно, более прост, чем я думал изначально, и теперь мы загружаем гигантские объемы данных за 15-20 секунд (с виртуализацией, конечно), а меньшие нагрузки в основном мгновенно возвращают нас в исходное положение.

Вот что я сделал:

 <!-- This needs to be contained in a parent panel like a grid -->
 <ListBox x:Name="Items" >
                <ListBox.Template>
                    <ControlTemplate> <!-- Broke it out to allow resizing -->
                        <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
                            <ItemsPresenter/> <!-- This little fella does magical things -->            
                        </ScrollViewer>         
                    </ControlTemplate>      
                </ListBox.Template>
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <VirtualizingStackPanel VirtualizingStackPanel.VirtualizationMode="Recycling"/>  <!-- Recycle was a must -->        
                    </ItemsPanelTemplate>       
                </ListBox.ItemsPanel>
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <HyperlinkButton  VerticalAlignment="Top" Margin="5" />
                                <!-- This guy I did need to set a minwidth on to retain nice and predictable scrolling 
 when datacontext was potentially changing -->
                            <ContentControl cal:View.Model="{Binding}" MinWidth="1160"
                                            VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" />
                        </StackPanel>
                    </DataTemplate>         
                </ListBox.ItemTemplate>           
            </ListBox>

И это! Вуаля! Все, что нужно, - вырвать ControlTemplate и применить прямой ItemsPresenter! Теперь он виртуализован, он изменяет размеры, баланс восстановлен во вселенной, и я больше не хочу ударять единорога. После этого я просто лишил визуальные состояния, потому что нам не нужен какой-либо выбор предметов, и что об этом, спасибо за понимание! Извините, что на этот раз я не мог наградить щедростью.

Ответ 2

в соответствии с запросом:-) Мне не хотелось, чтобы я "ответил" до сих пор...

Вы абсолютно правы, должен быть способ получить динамику роста. Установка фиксированной высоты - это быстрая проверка, чтобы убедиться, что виртуализация работает. Вы попали куда-нибудь с профилировщиком производительности или, может быть, с помощью SilverlightSpy? Я сделал один из этих вопросов, удалив рефакторинг привязки к UI/VM, чтобы сделать его более эффективным. Для WinPhone7 SL был хороший ресурс с потенциально хорошим решением, я посмотрю, смогу ли я его выкопать (теория должна перейти на полноценный SL)

Это имеет хорошую точку в кэшировании в зависимости от архитектуры виртуальной машины

Это может быть полезно, поскольку объясняет, что laxy загружается немного больше

И пару советов от команды разработчиков WinPho 7.

Надеюсь, что поможет

Ответ 3

Если это связано только с фиксированной высотой, почему бы вам просто не попробовать:

<Grid>    
<ListBox x:Name="Items"
      TabNavigation="Once"
      VirtualizingStackPanel.VirtualizationMode="Recycling"     
      Height="{Binding Path=Height,RelativeSource={RelativeSource AncestorType=Grid}}">         
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Margin="0,5">
                        <HyperlinkButton Content="Action" Margin="5"/>
                        <ContentControl  
                                cal:View.Model="{Binding}"  
                                VerticalContentAlignment="Stretch" 
                                HorizontalContentAlignment="Stretch"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
</Grid>

Важно, чтобы ListBox попадал в контейнер, который подходит для внешнего пространства, а не для внутреннего пространства, такого как панель стека.

Попробуйте это и скажите, поможет ли это.:)

Ответ 4

Я думаю, что если вы волшебным образом втягиваете элементы управления пользовательского интерфейса в свой шаблон данных, вы побеждаете цель виртуализации. Являются ли эти средства управления (сетки) самими повторно используемыми? Что происходит, когда изменяется {Binding}? Сколько кода выполняется в том, что похоже на приложенное поведение? Возможно, вы сделали то, что сделали. Но если вы просто сделаете прямолинейную таблицу данных (или добавили в нее обычный UserControl), вы узнаете, что все элементы управления в ней будут повторно использованы, и при переключении их datacontext будет минимальный штраф.

Я не уверен в проблеме с Height. Он должен работать (получить конечную высоту в Arrange), но это зависит от того, что он делает под капотом (возможно, он пытается понять все, прежде чем Arrange сделает высоту доступной).

Вы всегда можете создать свой собственный элемент управления виртуализацией. Вывести из Panel, поместить его в ScrollViewer и реализовать IScrollInfo на панели. Тогда вы узнаете ВСЕ причины, почему так оно и есть:)

Ответ 5

Я не уверен, если это будет приемлемым решением для вашего случая использования, но я создал ContentControl с именем DelayedLayoutUpdateContainer, который обертывает сложный макет и помогает повысить производительность. Идея, лежащая в основе DelayedLayoutUpdateContainer, заключается в том, что она регулирует размер своего содержимого только тогда, когда она не была изменена сама по себе на определенный промежуток времени. Размер ContentPresenter внутри ControlTemplate установлен на абсолютные значения. Таким образом, это может иметь такой же эффект, как установка высоты ListBoxes на абсолютное значение.