2010-09-30 2 views
3

Мне нужна прокручиваемая поверхность, содержащая два связанных списка. Сначала я использовал ScrollViewer с двумя ListBox внутри, каждый из которых имеет свою прокрутку, поэтому я все равно могу выбрать элемент. Увидев плохое время загрузки, я изменил свой списокBoxes на ItemsControl, но производительность все еще ужасна. Всего у меня в двух списках есть только 110 предметов.Проблема с несколькими элементами управления ItemsControl внутри ScrollViewer

<ScrollViewer Grid.Row="1"> 
    <StackPanel> 
     <Button Style="{StaticResource EmptyNonSelectButtonStyle}" BorderThickness="0" HorizontalContentAlignment="Left" Click="AnyCityButton_Click"> 
      <TextBlock Text="{Binding Resources.CurrentLocationItem, Source={StaticResource LocalizedResources}}" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeLarge}" /> 
     </Button> 
     <TextBlock Text="{Binding Resources.TopTenCitiesHeader, Source={StaticResource LocalizedResources}}" Style="{StaticResource PhoneTextSubtleStyle}" Margin="12,12,12,8" /> 
     <ItemsControl ItemsSource="{Binding TopTenCities}" ItemTemplate="{StaticResource CityDataTemplate}" HorizontalContentAlignment="Stretch" /> 
     <TextBlock Text="{Binding Resources.TopHundredCitiesHeader, Source={StaticResource LocalizedResources}}" Style="{StaticResource PhoneTextSubtleStyle}" Margin="12,12,12,8" /> 
     <ItemsControl ItemsSource="{Binding TopHundredCities}" ItemTemplate="{StaticResource CityDataTemplate}" HorizontalContentAlignment="Stretch" /> 
    </StackPanel> 
</ScrollViewer> 

Что я могу сделать для повышения производительности? Я попытался установить ItemsSource после загрузки страницы, но он все еще некрасивый (пустые списки на несколько секунд), не имеет смысла.

Спасибо.

+0

Быстрые наблюдения ... являются ли списки однородными (они обертывают один и тот же тип элементов?) Если нет, являются ли элементы, полученные из любого общего базового класса (кроме System.Object)? Должны ли содержимое списков быть представлены по-разному в зависимости от того, из какого списка они пришли? – avidgator

+0

Это те же самые типы элементов, и я использую один и тот же шаблон ... но мне нужен этот заголовок между ними, таким образом, разделение на слое пользовательского интерфейса, а не перегруппировка на уровне модели представления. –

ответ

3

Этот ответ превратился в монстра, но сильный удар через него, и я думаю, что вы будете найти ответ.

Нам необходимо каким-то образом использовать VirtualizingStackPanel как ListBox. Нам нужно собрать все элементы для отображения (кнопка, два текстовых блока и два набора городских данных) в один перечислимый какой-либо тип. Настоящий трюк и будет определять один из трех шаблонов, используемых для рендеринга элементов.

Подводя итог, мы должны создать новый тип ItemsControl. Теперь мы можем получить небольшое преимущество, просто согласившись с тем, что хотим создать очень специфичный ItemsControl, который поддерживает только эту задачу. Сначала это «стартер на 10» (ссылка в Великобритании).

Действительно тупой пример создания определенных элементов управления: -

public class SwitchingItemsControl : ItemsControl 
{ 
    public DataTemplate AlternativeItemTemplate { get; set; } 

    protected override void PrepareContainerForItemOverride(DependencyObject element, object item) 
    { 
     ContentPresenter cp = (ContentPresenter)element; 
     if (AlternativeItemTemplate != null && (((int)item) & 1) == 1) 
      cp.ContentTemplate = AlternativeItemTemplate; 
     else 
      cp.ContentTemplate = ItemTemplate; 

     cp.Content = item; 
    } 
} 

Этот контроль предполагает его элементы представляют собой набор целых чисел. У него есть AlternativeItemTemplate, который, если он поставляется, переключается между ними на нечетной/четной основе (обратите внимание, что это грань элемента).

Теперь давайте положить, что использование с VirtualizingStackPanel: -

<UserControl x:Class="CustomVirtualizingPanelInSL.MainPage" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:SilverlightApplication1" 
    Width="400" Height="300"> 
    <Grid x:Name="LayoutRoot" Background="White"> 
     <local:SwitchingItemsControl x:Name="itemsControl" > 
      <local:SwitchingItemsControl.Template> 
       <ControlTemplate TargetType="local:SwitchingItemsControl"> 
        <ScrollViewer VerticalScrollBarVisibility="Visible"> 
         <ItemsPresenter /> 
        </ScrollViewer> 
       </ControlTemplate> 
      </local:SwitchingItemsControl.Template> 
      <local:SwitchingItemsControl.ItemTemplate> 
       <DataTemplate> 
        <Border CornerRadius="2" BorderBrush="Blue" BorderThickness="1" Margin="2"> 
         <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding}" /> 
        </Border> 
       </DataTemplate> 
      </local:SwitchingItemsControl.ItemTemplate> 
      <local:SwitchingItemsControl.AlternativeItemTemplate> 
       <DataTemplate> 
        <Border CornerRadius="2" BorderBrush="Red" BorderThickness="1" Margin="2"> 
         <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding}" /> 
        </Border> 
       </DataTemplate> 
      </local:SwitchingItemsControl.AlternativeItemTemplate> 
      <local:SwitchingItemsControl.ItemsPanel> 
       <ItemsPanelTemplate> 
        <VirtualizingStackPanel /> 
       </ItemsPanelTemplate> 
      </local:SwitchingItemsControl.ItemsPanel> 
     </local:SwitchingItemsControl> 
    </Grid> 
</UserControl> 

Обратите внимание на ItemsPanel использующем VirtualizingStackPanel и получает представленный в ScrollViewer.

Теперь мы можем дать ему много содержания: -

namespace SilverlightApplication1 
{ 
    public partial class MainPage : UserControl 
    { 
     public MainPage() 
     { 
      InitializeComponent(); 
      itemsControl.ItemsSource = Enumerable.Range(0, 10000); 
     } 
    } 

}

Если переключиться на стандартный StackPanel это принимает возрастов для загрузки, в то время как он появляется мгновенная с виртуализацией.

Вооруженный этой информацией, вы должны быть в состоянии создать специальный ItemsControl, который обладает свойствами: -

  • ButtonTemplate (DataTemplate)
  • HeaderTemplate (DataTemplate)
  • TopTenHeaderText (String)
  • TopHundredHeaderText (String)
  • TopTenSource (IEnumerable<City>)
  • Ti pHunderedSource (IEnumerable<City>)

Теперь вы можете создать единый перечислимы с некоторыми методами расширения Linq: -

itemsControl.ItemsSource = Enumerable.Repeat((object)null, 1) 
    .Concat(Enumerable.Repeat((object)TopTenHeadeText)) 
    .Concat(TopTenSource.Cast<object>()) 
    .Concat(Enumerable.Repeat((object)TopHundredText)) 
    .Concat(TopHundredSource.Cast<object>()) 

Теперь вам просто нужно переопределить PrepareContainerForItemOverride и выбирать между ButtonTemplate (для первого элемента нуль), HeaderTemplate для элемента типа string или ItemTemplate для элемента типа City.

+0

Отличное решение. Я догадался, что есть способ сделать это как единый список с разными шаблонами. Спасибо, что показал. –

+0

Спасибо вам большое. Это очень помогло мне в создании собственного гибридного элемента управления ListBox. Благодаря! –

0

Не могли бы вы использовать один список/itemscontrol, но разные datatemplates, чтобы получить тот же эффект?

Или вы могли бы использовать контроль поворота вместо этого, помещая верхние 10 sitties в одной оси, топ 100 в другой стержень ..

+0

Я бы предпочел иметь решение для привязки данных, и у меня есть текстовые поля между разделами (например, группировка). Мы действительно оценили точку опоры, но это для всплывающего окна, и чувствую, что это будет переоценивать представление, чтобы обойти перфект. проблема. –

+0

ах, во всплывающем окне это не имело бы смысла.Но вы все равно можете использовать список с разными шаблонами данных, не так ли? сгруппированный коллекционный источник будет работать отлично, но он еще не в Silverlight, не так ли? Что такое «первоклассное» решение, которое они используют для группировки в таких вещах, как концентратор контактов? у них буквы алфавита по-разному оформлены в огромном списке, чтобы разбить контакты по письму ... –

1

Спасибо @AnthonyWJones, ваш ответ был (почти) именно тем, что я искал. Я решил дать свой собственный ответ здесь, чтобы другие читатели знали, как я адаптировал свой ответ на мои нужды.

Во-первых, как было предложено, я вытекающих из ItemsControl, и обеспечивая второе свойство «Шаблон», называется HeaderTemplate:

#region HeaderTemplate PROPERTY 

public static readonly DependencyProperty HeaderTemplateProperty = DependencyProperty.Register(
    "HeaderTemplate", 
    typeof(DataTemplate), 
    typeof(ItemsControlWithHeaders), 
    new PropertyMetadata(null, new PropertyChangedCallback(OnHeaderTemplateChanged))); 

public DataTemplate HeaderTemplate 
{ 
    get { return (DataTemplate)this.GetValue(HeaderTemplateProperty); } 
    set { this.SetValue(HeaderTemplateProperty, value); } 
} 

private static void OnHeaderTemplateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) 
{ 
    ItemsControlWithHeaders control = obj as ItemsControlWithHeaders; 
    control.InvalidateArrange(); 
} 

#endregion 

Во-вторых, я переопределение PrepareContainerForItemOverride, чтобы обеспечить свою собственную логику выбора шаблона. То, что я делаю, просто перенаправлять любой «строка» пункт в HeaderTemplate и другие элементы к обычному ItemTemplate:

protected override void PrepareContainerForItemOverride(DependencyObject element, object item) 
{ 
    base.PrepareContainerForItemOverride(element, item); 

    ContentPresenter presenter = element as ContentPresenter; 

    if(presenter != null) 
    { 
    if(item is string) 
    { 
     presenter.ContentTemplate = this.HeaderTemplate; 
    } 
    else 
    { 
     presenter.ContentTemplate = this.ItemTemplate; 
    } 
    } 
} 

Этот контроль может теперь использоваться как это:

<local:ItemsControlWithHeaders Grid.Row="1" ItemsSource="{Binding GroupedCities}" ScrollViewer.VerticalScrollBarVisibility="Auto"> 
     <local:ItemsControlWithHeaders.Template> 
      <ControlTemplate TargetType="local:ItemsControlWithHeaders"> 
       <ScrollViewer> 
        <ItemsPresenter /> 
       </ScrollViewer> 
      </ControlTemplate> 
     </local:ItemsControlWithHeaders.Template> 
     <local:ItemsControlWithHeaders.ItemsPanel> 
      <ItemsPanelTemplate> 
       <VirtualizingStackPanel /> 
      </ItemsPanelTemplate> 
     </local:ItemsControlWithHeaders.ItemsPanel> 
     <local:ItemsControlWithHeaders.HeaderTemplate> 
      <DataTemplate> 
       <TextBlock Text="{Binding}" Style="{StaticResource PhoneTextSubtleStyle}" Foreground="{StaticResource PhoneAccentBrush}" Margin="12,12,12,8" /> 
      </DataTemplate> 
     </local:ItemsControlWithHeaders.HeaderTemplate> 
     <local:ItemsControlWithHeaders.ItemTemplate> 
      <DataTemplate> 
       <Button Style="{StaticResource EmptyNonSelectButtonStyle}" BorderThickness="0" HorizontalContentAlignment="Left" Click="AnyCityButton_Click"> 
        <TextBlock Text="{Binding Name, Mode=OneWay}" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeLarge}" /> 
       </Button> 
      </DataTemplate> 
     </local:ItemsControlWithHeaders.ItemTemplate> 
    </local:ItemsControlWithHeaders> 

Строить источник данных необходимо передать в этот специальный гибридный контроль, LINQ это хорошо, но я выбрал гораздо более конкретное решение, реализованная на мой взгляд-модели:

public IEnumerable<object> GroupedCities 
{ 
    get 
    { 
    yield return new CurrentLocationCityViewModel(); 
    yield return Localized.TopTenCitiesHeader; // string resource 

    foreach(CityViewModel city in this.TopTenCities) 
    { 
     yield return city; 
    } 

    yield return Localized.TopHundredCitiesHeader; // string resource 

    foreach(CityViewModel city in this.TopHundredCities) 
    { 
     yield return city; 
    } 
    } 
} 

I теперь есть общий ItemsControlWithHeaders Я могу повторно использовать не только этот сценарий. Производительность отличная. Единственная проблема, остающаяся для пуриста, подобного мне, заключается в том, что базовый ItemControl жалуется на DEBUG, поскольку тип «объект» не имеет свойства «Имя». Он генерирует сообщение System.Windows.Data Error: BindingExpression path error: 'Name' property not found на выходе отладки, которое можно игнорировать.

Смежные вопросы