2011-09-09 30 views
10

phiên bản ngắnListBox ScrollIntoView khi sử dụng CollectionViewSource với GroupDescriptions (ví dụ: IsGrouping == True)

Tôi muốn di chuyển các ListBox mục vào xem khi lựa chọn được thay đổi.

dài phiên bản

Tôi có một ListBox với ItemsSource ràng buộc với một CollectionViewSource với một GroupDescription, theo ví dụ dưới đây.

<Window.Resources> 
    <CollectionViewSource x:Key="AnimalsView" Source="{Binding Source={StaticResource Animals}, Path=AnimalList}"> 
     <CollectionViewSource.GroupDescriptions> 
      <PropertyGroupDescription PropertyName="Category"/> 
     </CollectionViewSource.GroupDescriptions> 
    </CollectionViewSource> 
</Window.Resources> 

<ListBox x:Name="AnimalsListBox"ItemsSource="{Binding Source={StaticResource AnimalsView}}" ItemTemplate="{StaticResource AnimalTemplate}" SelectionChanged="ListBox_SelectionChanged"> 
    <ListBox.GroupStyle> 
     <GroupStyle HeaderTemplate="{StaticResource CategoryTemplate}" /> 
    </ListBox.GroupStyle> 
</ListBox> 

Có sự kiện SelectionChanged trong tệp sau mã.

public List<Animal> Animals { get; set; } 

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 
{ 
    ListBox control = (ListBox)sender; 
    control.ScrollIntoView(control.SelectedItem); 
} 

Hiện tại. Nếu tôi đặt AnimalsListBox.SelectedItem thành một mục hiện không hiển thị, tôi muốn nó cuộn trong chế độ xem. Đây là nơi nó trở nên phức tạp, vì ListBox là các nhóm (thuộc tính IsGroupedtrue) cuộc gọi đến ScrollIntoView không thành công.

System.Windows.Controls.ListBox qua Phản xạ. Lưu ý base.IsGrouping trong số OnBringItemIntoView.

public void ScrollIntoView(object item) 
{ 
    if (base.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 
    { 
     this.OnBringItemIntoView(item); 
    } 
    else 
    { 
     base.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(this.OnBringItemIntoView), item); 
    } 
} 

private object OnBringItemIntoView(object arg) 
{ 
    FrameworkElement element = base.ItemContainerGenerator.ContainerFromItem(arg) as FrameworkElement; 
    if (element != null) 
    { 
     element.BringIntoView(); 
    } 
    else if (!base.IsGrouping && base.Items.Contains(arg)) 
    { 
     VirtualizingPanel itemsHost = base.ItemsHost as VirtualizingPanel; 
     if (itemsHost != null) 
     { 
      itemsHost.BringIndexIntoView(base.Items.IndexOf(arg)); 
     } 
    } 
    return null; 
} 

Câu hỏi

  1. bất cứ ai có thể giải thích lý do tại sao nó không làm việc khi sử dụng nhóm?
    • ItemContainerGenerator.ContainerFromItem luôn trả về null, mặc dù trạng thái của nó cho biết tất cả các vùng chứa đã được tạo.
  2. Làm cách nào để có thể cuộn cuộn vào chế độ xem khi sử dụng nhóm?

Trả lời

8

Tôi đã tìm được giải pháp cho vấn đề của mình. Tôi đã chắc chắn rằng tôi không phải là người đầu tiên nhấn vấn đề này vì vậy tôi tiếp tục tìm kiếm StackOverflow cho các giải pháp và tôi stumbled khi câu trả lời này của David about how ItemContainerGenerator works with a grouped list.

Giải pháp của David là trì hoãn truy cập ItemContainerGenerator cho đến sau quy trình hiển thị.

Tôi đã triển khai giải pháp này, với một vài thay đổi mà tôi sẽ trình bày chi tiết sau.

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 
{ 
    ListBox control = (ListBox)sender; 

    if (control.IsGrouping) 
    { 
     if (control.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 
       Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(DelayedBringIntoView)); 
     else 
       control.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged; 
    } 
    else 
     control.ScrollIntoView(control.SelectedItem); 
} 

private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e) 
{ 
    if (ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) 
     return; 

    ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged; 
    Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(DelayedBringIntoView)); 
} 

private void DelayedBringIntoView() 
{ 
    var item = ItemContainerGenerator.ContainerFromItem(SelectedItem) as ListBoxItem; 
    if (item != null) 
     item.BringIntoView(); 
} 

Thay đổi:

  • Chỉ sử dụng cách tiếp cận ItemContainerGenerator khi nó IsGroupingtrue, nếu không tiếp tục sử dụng mặc định ScrollIntoView.
  • Kiểm tra xem ItemContainerGenerator đã sẵn sàng chưa, nếu có gửi đi hành động, nếu không hãy nghe trạng thái ItemContainerGenerator để thay đổi .. Điều này quan trọng vì nếu nó sẵn sàng thì sự kiện StatusChanged sẽ không bao giờ kích hoạt.
+0

Bạn nên thay đổi câu trả lời cho đúng câu trả lời, chứ không phải câu trả lời ở trên. – Valentein

+0

@Valentein: Tôi đã thay đổi câu trả lời đã đánh dấu. TUYÊN BỐ như [crazyarabian] (http://stackoverflow.com/a/7375646/73025) lời khuyên đã giúp tôi trong việc chẩn đoán vấn đề nó sẽ tốt lên bỏ phiếu ** cả hai ** câu trả lời nếu bạn sử dụng giải pháp cuối cùng của tôi. – Dennis

+0

Sử dụng .NET 4.5.1 và MVVM bạn có thể sử dụng một hành vi để thực hiện việc này. Các hành vi hoạt động trong cả hai kịch bản khi nó cháy muộn. – Kelly

3
  1. Các out of the box VirtualizingStackPanel không hỗ trợ ảo hóa quan điểm bộ sưu tập nhóm. Khi một bộ sưu tập được nhóm lại được hiển thị trong một ItemsControl, mỗi nhóm như một tổng thể là một mục trái ngược với từng mục trong bộ sưu tập dẫn đến việc di chuyển "giật" tới từng tiêu đề nhóm chứ không phải từng mục.

  2. Có thể bạn sẽ cần phải cuộn VirtualizingStackPanel hoặc ItemContainerGenerator của riêng mình để theo dõi các vùng chứa được hiển thị trong một nhóm. Nghe có vẻ vô lý, nhưng ảo hóa mặc định với nhóm trong WPF là thiếu để nói rằng ít nhất.

+0

Đó là những gì tôi nghĩ, tuy nhiên tôi đã hy vọng rằng tôi sẽ không phải viết các bảng ảo hóa có thể phức tạp. ... Vì mỗi nhóm là một mục, đó là lý do tại sao 'ItemContainerGenerator' luôn trả về' null' khi truyền 'SelectedItem'? – Dennis

+1

Tôi tin là có. Bạn nên kiểm tra blog Bea Stollnitz. Cô ấy có rất nhiều bài viết hay về cách nhóm và ảo hóa: http://www.beacosta.com/ – sellmeadog

+0

Cảm ơn. Tôi đã đọc một số bài viết của Bea Stollnitz trên WPF và CollectionViewSource và Grouping - Tôi thực sự đã sử dụng các ví dụ nhóm của mình trong câu hỏi của tôi. – Dennis

Các vấn đề liên quan