2014-11-15 9 views
1

WPF 애플리케이션에 성능 문제가 있습니다. (나는 그것이 새로운 것이 아닐 것으로 생각합니다 ...) 기본적으로 응용 프로그램은 ListBox 및 선택할 수있는 항목이 포함되어 있으며 ContentControl은 선택한 항목의 세부 정보를 표시합니다. 한 유형의 항목에는 하위 항목 목록을 표시하는 ItemsControl이 포함됩니다. 그리고 이것이 문제입니다. ItemsControl을 제거하거나 매우 적은 수의 하위 항목 (< 3) 만 표시하면 빨리 처리됩니다. 그러나 50 개의 하위 항목으로 목록을 탐색하는 것은 너무 느립니다.ItemsControl을 사용하여 UI 캐싱 수행

나에게 일종의 가상화를 제안 할 수도 있지만 여기서는 적용되지 않습니다. 대부분의 경우 모든 항목이 화면에 표시되므로 즉시 표시해야합니다. 눈에 보이지 않는 항목을 가상화 할 필요가 없습니다.

성능 문제를 보여주는 몇 가지 짧은 클래스 (보기 및보기 모델)로 전체 응용 프로그램을 제거 할 수 있습니다. 이 테스트 케이스는 downloaded here 일 수 있습니다. 응용 프로그램을 실행하고 왼쪽의 목록에서 항목을 선택한 다음 위 또는 아래 화살표 키를 누르고 있으면 다른 항목으로 이동할 수 있습니다. 일반적으로 이것은 다른 항목을 즉시 보여 주지만 여기서는 각각 ~ 160ms의 매우 두드러진 시간이 걸립니다. UI 컨트롤의 많은 아니라

<UserControl ...> 
    <ScrollViewer VerticalScrollBarVisibility="Auto"> 
    <ItemsControl ItemsSource="{Binding StackFrameVMs, Mode=OneTime}"> 
     <ItemsControl.ItemTemplate> 
     <DataTemplate> 
      <StackPanel Orientation="Horizontal"> 
      <TextBlock Text="•" Foreground="{Binding ...}"/> 
      <TextBox Margin="4,0,0,0" Text="{Binding ...}"/> 
      <TextBox Margin="12,0,0,0" Text="{Binding ...}"/> 
      </StackPanel> 
     </DataTemplate> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 
    </ScrollViewer> 
</UserControl> 

을, 그러나 나는 그들 모두를 필요

뷰의 핵심은 다음과 같다. 실제로 실제 응용 프로그램에는 몇 가지 컨트롤과 바인딩이 있지만 데모에서는 충분합니다. DataTemplate의 내용을 UserControl으로 이동하고 대신 삽입하면 더 오래 걸립니다.

이전 프로필에서 다른 항목이 선택되고 (세부보기의 DataContext 변경됨) 목록이 변경 될 때마다 ItemsControl이 목록 항목에 대한 모든 컨트롤을 삭제하고 다시 작성한다고 생각합니다. 새 선택한 항목의 유형이 동일하고 동일한 DataTemplate을 사용하므로보기 자체가 재사용되는 경우에도 마찬가지입니다.

ItemsControl은 이전에 만든 항목을 다시 사용하도록 할 수 있습니까? 선택한 항목 중 하나가 더 적게 필요하지만 다음 항목이 다시 필요하면 모든 항목이 포함될 수 있습니까? 아니면 매우 간단한 사용 사례의 성능을 다른 방식으로 향상시킬 수있는 방법이 있습니까?

업데이트 : 자세히이 예제 코드는 내 전체 응용 프로그램의 모양을 아주 잘린 것입니다. 선택한 구조를 단순화 할 수 있다고 생각할 수도 있지만 전체 응용 프로그램이 다음 스크린 샷과 같은 것으로 간주하십시오. ItemsControl 이상의 것 외에도 왼쪽 목록에서 선택할 수있는 다양한 항목 유형에 대한 다양한 상세보기가 있습니다. 그래서 기본적으로 내가 가지고있는 구조가 필요합니다. 단지 더 빨라야합니다.

Screenshot

전체 프로젝트는 만약 당신이 좋아하면, 오픈 소스, 당신은 완벽한 솔루션을 살펴 수 있습니다 : https://github.com/dg9ngf/FieldLog

+0

응용 프로그램을 테스트 한 결과 성능에 문제가 없습니다. 그것은 완전히 반응합니다. –

+0

http://pastebin.com/E3iYjZC4 –

+0

재생할 수 없습니다. 나는 당신이 제공 한 코드로 시도했다. (비록 ScrollViewer가 더 많은 것들을 포함하고 있기 때문에 ScrollViewer가 거기에 있어야하기 때문에 나는 그것을 완전히 사용할 수는 없지만). 귀하의 변화도 여기에서 느립니다. – ygoe

답변

0

나는 해결책을 찾았지만 시동 시간을 희생했습니다.

이 패널로 Grid와 함께 ItemsControl에서 오른쪽 목록을 넣고,이에 모든 항목을로드됩니다 시작하지만 약간의 변화 당신은 단지처럼 보이게 할 수 있습니다 전에 : 이제

<Grid> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="*"/> 
    </Grid.RowDefinitions> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="*"/> 
     <ColumnDefinition Width="*"/> 
    </Grid.ColumnDefinitions> 

    <ListBox Name="LogItemsList" ItemsSource="{Binding LogItems}" SelectedItem="{Binding SelectedItem}"/> 

    <ItemsControl ItemsSource="{Binding LogItems}" Grid.Column="1"> 
     <ItemsControl.ItemsPanel> 
      <ItemsPanelTemplate> 
       <Grid/> 
      </ItemsPanelTemplate> 
     </ItemsControl.ItemsPanel> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate DataType="{x:Type vm:FieldLogExceptionItemViewModel}"> 
       <v:FieldLogExceptionItemView Visibility="{Binding IsVisible, Converter={StaticResource BooleanToVis}}"/> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 

</Grid> 

/// <summary> 
    /// Gets or sets a bindable value that indicates SelectedItem 
    /// </summary> 
    public FieldLogExceptionItemViewModel SelectedItem 
    { 
     get { return (FieldLogExceptionItemViewModel)GetValue(SelectedItemProperty); } 
     set { SetValue(SelectedItemProperty, value); } 
    } 
    public static readonly DependencyProperty SelectedItemProperty = 
     DependencyProperty.Register("SelectedItem", typeof(FieldLogExceptionItemViewModel), typeof(MainViewModel), 
     new PropertyMetadata(null, (d, e) => 
     { 
      var vm = (MainViewModel)d; 
      var val = (FieldLogExceptionItemViewModel)e.NewValue; 
      if(val!=null) 
      { 
       foreach (FieldLogExceptionItemViewModel item in vm.LogItems) 
       { 
        item.IsVisible = (item==val); 
       } 
      } 
     })); 

을 그리고 FieldLogExceptionItemViewModel이 추가 : MainViewModel이 추가

/// <summary> 
    /// Gets or sets a bindable value that indicates IsVisible 
    /// </summary> 
    public bool IsVisible 
    { 
     get { return (bool)GetValue(IsVisibleProperty); } 
     set { SetValue(IsVisibleProperty, value); } 
    } 
    public static readonly DependencyProperty IsVisibleProperty = 
     DependencyProperty.Register("IsVisible", typeof(bool), typeof(FieldLogExceptionItemViewModel), new PropertyMetadata(false)); 

그럼 속도를 높이기 위해

변환기 인 BooleanToVis에 주목하십시오. false를 넘겨 줄 때 시스템 기본 변환기가 Visibility.Collapsed을 사용하고 다시 접힌 항목을 Visibility.Visibile으로 변경 한 후에 UI가 다시로드하기 때문에 새 BooleanToVisibilityConvereter을 구현해야하지만 어떤 것이든지 준비가 필요하므로 우리가 필요로하는 것은 Visibility.Hidden입니다. 시각.

public class BooleanToVis : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     return (bool)value ? Visibility.Visible : Visibility.Hidden; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

<Window.Resources> 
    <v:BooleanToVis x:Key="BooleanToVis"/> 
</Window.Resources> 
+0

흠, 그렇습니다.하지만 가상화를 사용하는 수천 개의 목록 항목 (왼쪽에 있음)을 사용하면 어려울 것입니다. (그 목록은 중요하지 않기 때문에 내 예제에는 포함되지 않습니다.) 사실, 다른 세부 정보보기에서 볼 수 있듯이 컨트롤은 항상 독립적으로 존재할 필요는 없으며 데이터 바인딩은 기존 컨트롤의 데이터를 변경하십시오. 항상 항목을 재생성하는 단일 ItemsControl입니다. 그리고 그것은 WPF에서 매우 느립니다. 또한 이것은 매우 간단한 데모입니다. 위의 업데이트를 참조하십시오. – ygoe

+0

이 시점에서 확장 정보에 추가 정보를 넣거나 observableCollection을 일반 컬렉션으로 변경하는 것이 좋습니다. 관찰 가능 콜렉션에서 항목을 추가/제거 할 때마다 변경 사항을 통지하고 간단한 목록을 사용하고 목록이 최신 인 경우에만보기에 통지합니다. – Bijan

+0

다른 VM에서도 마찬가지입니다. 스타일을 더 많이 사용하고, 캔버스를 사용하고, 모든 변경 사항을 한 번에 알리고, 변환기 및 코드 숨김을 피하십시오 ... 더 많은 팁을 검색 할 수 있습니다. – Bijan

관련 문제