2015-01-04 3 views
0

ListBox에 오히려 긴 목록을 표시 할 때 속도 문제가 있습니다. 내 ListBox에 약 5000 개의 항목이 있고 항목의 높이가 다를 수 있습니다. 각 항목은 항목이 표시되는 즉시 실행되는 쿼리를 통해 검색되는 정보를 표시합니다. 모든 콘텐츠에 대해이 작업을 한 번에 수행하는 데 너무 많은 시간이 걸리므로이 지연된 쿼리 실행이 필요합니다. 항목의 속성에 처음 액세스 할 때 만들어지는 NotifyTaskCompletion 개체에 ItemsControl을 바인딩하여 지연 쿼리를 수행했습니다. 예를 들어VirtualizingPanel.ScrollUnit = "Pixel"을 사용하여 거대한 ListBox에서 스크롤

는 :

public class ItemViewModel 
{ 
    public NotifyTaskCompletion<ObservableCollection<Content>> QueryResult 
    { 
     get 
     {     
      if(_queryTask == null) 
      { 
       _queryTask = new NotifyTaskCompletion<ObservableCollection<Content>> 
            (Task<ObservableCollection<Content>>.Run(queryFunction)); 
      } 
      return _queryTask; 
     } 
    } 
} 

ItemTemplate:  
+--------------------------------------------------+ 
| TextBlock with header       | 
|             | 
| ItemsControl Source={Binding QueryResult.Result} | 
+--------------------------------------------------+ 

그래서 항목이 표시 될 때마다, 그것은 쿼리를 수행하는 스레드를 시작하고 쿼리가 완료되면 내용이 표시됩니다. 쿼리를 통해 검색되는 정보는 상당히 많을 수 있습니다. 즉, ListBox의 항목이 ListBox 자체보다 클 수 있습니다. 그래서 ListBox에서 VirtualizingPanel.ScrollUnit = "Pixel"을 설정해야 사용자가 전체 항목을 볼 수 있습니다.

+-------------------------------------------+ 
| ListBox, too small for big Item 1   | 
|           | 
| +------------------------------+   | 
| | Header 1      |   | 
| |        |   | 
| | Lot's of information ....... |   | 
| | Lot's of information ....... |   | 
| | Lot's of information ....... |   | 
| | Lot's of information ....... |   | 
+-| Lot's of information ....... |----------+ 
    | Lot's of information ....... | 
    | Lot's of information ....... | 
    | Lot's of information ....... | 
    +------------------------------+ 

    +------------------------------+ 
    | Header 2      | 
    |        | 
    | Not so much information..... | 
    +------------------------------+ 

그러나 스크롤 속도가 상당히 느려집니다. 목록의 중간에 스크롤 막대의 썸을 드래그하면 쿼리 된 정보없이 일부 항목이 표시되는 몇 초 후에 고정됩니다. 그런 다음 항목 중 일부는 쿼리 된 정보를 표시합니다. 그런 다음 ListBox가 또 다른 위치로 점프하는 것으로 보이며 다시 다른 고정이 발생합니다. ListBox가 현재 스크롤 위치 위에있는 모든 항목을 렌더링하는 것처럼 보입니다. 이는 모든 쿼리와 쿼리 된 데이터의 복잡한 렌더링으로 인해 매우 오랜 시간이 걸립니다.

VirtualizingPanel.ScrollUnit = "Unit"으로 설정하면 속도 문제가 없습니다. ListBox는 스크롤되는 항목 만 표시하므로 쿼리되는 정보 만 표시됩니다. 불행히도, "픽셀"설정을 사용해야합니다. 왜냐하면 사용자가 모든 항목을 볼 수 있도록 픽셀 스크롤이 필요할 정도로 너무 큰 항목이 있기 때문입니다.

나는 수백 개의 스레드를 동시에 시작하지 않도록 쿼리 대기열을 만드는 것이 좋겠다는 사실을 알고 있지만 ListBox의 근본적인 문제에 대해서는 변경되지 않을 것이라고 생각합니다. 표시되지 않을 항목을 렌더링합니다.

이 문제를 해결할 방법이 확실하지 않습니다. 누구든지 나를 도울 수 있습니까?

+0

에서 스크롤 왜 최대 높이로 ListBoxItem의에서 목록 상자를 넣지? 그리고 지연 스크롤을 켭니다. – Paparazzi

답변

0

유무 DeferredScrolling과 ListBoxItem의

<Grid> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="*"/> 
    </Grid.RowDefinitions> 
    <ListBox ItemsSource="{Binding Path=DeferedItems}" 
       ScrollViewer.IsDeferredScrollingEnabled="True" 
       ScrollViewer.VerticalScrollBarVisibility="Visible" 
       VirtualizingStackPanel.VirtualizationMode="Standard"> 
     <ListBox.ItemTemplate> 
      <DataTemplate> 
       <StackPanel> 
        <TextBlock Text="{Binding Path=ID}"/> 
        <ListBox ItemsSource="{Binding Path=DefStrings}" Margin="10,0,0,0" 
          MaxHeight="300" 
          ScrollViewer.VerticalScrollBarVisibility="Visible"/> 
        <TextBlock Text="{Binding Path=DT}" Margin="10,0,0,0"/>      
       </StackPanel> 
      </DataTemplate> 
     </ListBox.ItemTemplate> 
    </ListBox> 
</Grid> 

public partial class MainWindow : Window 
{ 
    private List<DeferedItem> deferedItems = new List<DeferedItem>(); 
    public MainWindow() 
    { 
     this.DataContext = this; 
     for (Int32 i = 0; i < 100000; i++) deferedItems.Add(new DeferedItem(i)); 
     InitializeComponent(); 
    } 
    public List<DeferedItem> DeferedItems { get { return deferedItems; } } 

} 
public class DeferedItem 
{ 
    private Int32 id; 
    private DateTime? dt = null; 
    private List<String> defStrings = new List<string>(); 
    public DateTime DT 
    { 
     get 
     { 
      if (dt == null) 
      { 
       System.Threading.Thread.Sleep(1000); 
       dt = DateTime.Now; 
      } 
      return (DateTime)dt; 
     } 
    } 
    public List<String> DefStrings 
    { 
     get 
     { 
      if (defStrings.Count == 0) 
      { 
       for (Int32 i = id; i < id + 1000; i++) defStrings.Add(i.ToString() + " " + DateTime.Now.ToLongTimeString()); 
      } 
      return defStrings; 
     } 
    } 
    public Int32 ID { get { return id; } } 
    public DeferedItem(Int32 ID) { id = ID; } 
} 
+0

감사합니다 Blam, 이것은 확실히 옵션입니다! DeferredScrolling에 대해 몰랐습니다. 두 개의 중첩 된 ScrollViewer를 사용하는 것이 가장 좋은 방법은 아니지만 지금은 귀하의 아이디어와 함께 작동한다는 것이 기쁩니다! :) – sr258