2009-12-30 44 views
25

Nguyên mẫu của tôi hiển thị "tài liệu" chứa "trang" là được đại diện bằng hình thu nhỏ. Mỗi tài liệu có thể có bất kỳ số lượng trang nào. Ví dụ: có thể có 1000 tài liệu với 5 trang mỗi tài liệu hoặc 5 tài liệu với 1000 trang mỗi tài liệu hoặc ở một nơi nào đó. Tài liệu không chứa các tài liệu khác. Trong đánh dấu xaml của tôi, tôi có một số ListBox, có số ItemsTemplate tham chiếu đến innerItemsTemplate cũng có một số ListBox. Tôi muốn 2 cấp độ của các mục đã chọn để tôi có thể thực hiện các hoạt động khác nhau trên tài liệu hoặc trang (xóa, hợp nhất, di chuyển đến vị trí mới, v.v.). InnerItemsTemplate ListBox sử dụng số WrapPanel làm ItemsPanelTemplate.Hộp danh sách WPF với Hộp danh sách - Giao diện người dùng ảo và cuộn

Đối với kịch bản mà tôi có một số lượng lớn các tài liệu với một vài trang mỗi (nói, 10000 tài liệu với mỗi 5 trang), các di chuyển làm việc nhờ tuyệt vời để giao diện người dùng Virtualization bởi VirtualizingStackPanel. Tuy nhiên, tôi gặp vấn đề nếu tôi có một số lượng lớn trang. Tài liệu với 1000 trang sẽ chỉ hiển thị khoảng 50 lần (mọi thứ vừa với màn hình) và khi tôi cuộn xuống, phần bên ngoài ListBox sẽ chuyển sang tài liệu tiếp theo, bỏ qua 950 trang hoặc không hiển thị. Cùng với đó, không có VirtualzingWrapPanel để bộ nhớ ứng dụng thực sự tăng lên.

Tôi tự hỏi nếu tôi đang đi về điều này đúng cách, đặc biệt là vì nó là loại khó giải thích! Tôi muốn có thể hiển thị 10000 tài liệu với 1000 trang mỗi (chỉ hiển thị bất kỳ nội dung nào phù hợp trên màn hình), bằng cách sử dụng Giao diện người dùng ảo và cũng cuộn mượt mà.

Làm cách nào để đảm bảo cuộn di chuyển qua tất cả các trang trong tài liệu trước khi nó hiển thị tài liệu tiếp theo và vẫn tiếp tục ảo hóa giao diện người dùng? Thanh cuộn dường như chỉ di chuyển đến tài liệu tiếp theo.

Có vẻ hợp lý khi đại diện cho "tài liệu" và "trang" - với phương pháp hiện tại của tôi khi sử dụng số ListBox trong một số ListBox?

Tôi rất cảm kích mọi ý tưởng của bạn. Cảm ơn bạn.

Trả lời

24

Câu trả lời ở đây là đáng ngạc nhiên:

  • Nếu bạn sử dụng ItemsControl hoặc ListBox bạn sẽ nhận được hành vi mà bạn đang trải qua, nơi nếu bạn sử dụng TreeView thay vào đó, điều khiển sẽ cuộn trơn tru để bạn có thể cuộn qua tài liệu của mình và chuyển sang tài liệu tiếp theo, nhưng nó vẫn sẽ được hiển thị. có thể ảo hóa.

Tôi nghĩ lý do nhóm WPF chọn hành vi này là TreeView thường có các mục lớn hơn khu vực hiển thị, trong khi thường là ListBox thì không.

Trong mọi trường hợp, nó là tầm thường trong WPF để thực hiện một giao diện TreeView và hoạt động như ListBox hoặc ItemsControl bằng cách sửa đổi ItemContainerStyle. Điều này rất đơn giản. Bạn có thể cuộn của riêng bạn hoặc chỉ cần sao chép trên các mẫu thích hợp từ tập tin chủ đề hệ thống.

Vì vậy, bạn sẽ có một cái gì đó như thế này:

<TreeView ItemsSource="{Binding documents}"> 
    <TreeView.ItemsPanel> 
    <ItemsPanelTemplate> 
     <VirtualizingStackPanel /> 
    </ItemsPanelTemplate> 
    </TreeView.ItemsPanel> 
    <TreeView.ItemContainerStyle> 
    <Style TargetType="{x:Type TreeViewItem}"> 
     <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type TreeViewItem}"> 
      <ContentPresenter /> <!-- put your desired container style here with a ContentPresenter inside --> 
      </ControlTemplate> 
     </Setter.Value> 
     </Setter> 
    </Style> 
    </TreeView.ItemContainerStyle> 
    <TreeView.ItemTemplate> 
    <DataTemplate TargetType="{x:Type my:Document}"> 
     <Border BorderThickness="2"> <!-- your document frame will be more complicated than this --> 
     <ItemsControl ItemsSource="{Binding pages}"> 
      ... 
     </ItemsControl> 
     </Border> 
    </DataTemplate> 
    </TreeView.ItemTemplate> 
</TreeView> 

Bắt di chuyển dựa trên điểm ảnh và ListBox kiểu multiselect làm việc cùng nhau

Nếu bạn sử dụng kỹ thuật này để có được di chuyển dựa trên pixel, bên ngoài của bạn ItemsControl trong đó cho thấy các tài liệu không thể là một ListBox (vì ListBox không phải là một phân lớp của TreeView hoặc TreeViewItem). Vì vậy, bạn mất tất cả hỗ trợ đa lựa chọn của ListBox. Theo như tôi có thể nói, không có cách nào để sử dụng hai tính năng này cùng nhau mà không bao gồm một số mã của riêng bạn cho một tính năng này hoặc một tính năng khác.

Nếu bạn cần cả hai bộ chức năng trong cùng một điều khiển, bạn có một số tùy chọn cơ bản:

  1. Thực hiện đa lựa chọn cho mình trong một lớp con của TreeViewItem. Sử dụng TreeViewItem thay vì TreeView cho điều khiển bên ngoài, vì nó cho phép nhiều trẻ em được chọn. Trong khuôn mẫu bên trong ItemsContainerStyle: Thêm một hộp kiểm xung quanh ContentPresenter, mẫu liên kết hộp kiểm để được chọn, và tạo kiểu cho hộp kiểm với mẫu điều khiển để có được giao diện bạn muốn. Sau đó, thêm trình xử lý sự kiện chuột của riêng bạn để xử lý Ctrl-Click và Shift-Click để chọn nhiều.

  2. Tự thực hiện ảo hóa pixel được cuộn trong một lớp con của VirtualizingPanel. Điều này là tương đối đơn giản, vì hầu hết sự phức tạp của VirtualizingStackPanel có liên quan đến việc cuộn và tái chế vùng chứa không phải pixel. Dan Crevier's Blog có một số thông tin hữu ích để hiểu VirtualizingPanel.

+0

Cách tiếp cận này thực sự hiệu quả đối với tôi như xa như ảo hóa giao diện người dùng. Bây giờ tôi chỉ cần có được ListBox như hành vi để chọn các mục (trang hoặc tài liệu trong trường hợp này). Làm thế nào tôi có thể nhận được nhiều và mở rộng các chế độ lựa chọn tương tự như ListBox? –

+0

Ngoài ra, tôi đang thiết lập các ItemsPanelTemplate trong ItemsControl đến một WrapPanel, mà dường như không bọc khi tôi thay đổi kích thước các ứng dụng - nó có vẻ cư xử giống như một stackPanel. Nhìn chung, tôi cảm thấy câu trả lời ở trên bởi Ray giúp tôi đi đúng hướng. –

+0

Tôi rất vui vì tôi tình cờ gặp bài đăng này, nó đã cứu tôi kéo tất cả tóc của tôi ra. – Bijington

0

Vui lòng cho phép tôi làm nổi bật câu trả lời này bằng câu hỏi: Người dùng có phải xem từng hình thu nhỏ trong mọi mục trong danh sách mọi lúc không?

Nếu câu trả lời cho câu hỏi đó là 'không', thì có thể giới hạn số lượng trang hiển thị trong mẫu mục bên trong (với điều kiện bạn đã cho biết thao tác cuộn hoạt động tốt với 5 trang) và sử dụng mẫu 'mục đã chọn' riêng biệt lớn hơn và hiển thị tất cả các trang cho tài liệu đó?Billy Hollis giải thích làm thế nào để 'pop' một sản phẩm được chọn ra trong một ListBox trên dnrtv episode 115

+0

không người dùng nào không phải xem từng hình thu nhỏ trong mọi mục trong danh sách mọi lúc - chúng chỉ cần có thể cuộn để truy cập các mục khác –

36

Nó có thể đạt được VirtualizingStackPanels di chuyển trơn tru trong WPF 4.0 mà không bị mất ảo nếu bạn đang chuẩn bị để sử dụng phản ánh để truy cập chức năng riêng của VirtualizingStackPanel.Tất cả những gì bạn phải làm là thiết lập thuộc tính IsPixelBased riêng của VirtualizingStackPanel thành true.

Lưu ý rằng trong .Net 4.5, bạn không cần phải thực hiện hack này vì bạn có thể đặt VirtualizingPanel.ScrollUnit = "Pixel".

Để làm cho nó thực sự dễ dàng, đây là một số mã:

public static class PixelBasedScrollingBehavior 
{ 
    public static bool GetIsEnabled(DependencyObject obj) 
    { 
     return (bool)obj.GetValue(IsEnabledProperty); 
    } 

    public static void SetIsEnabled(DependencyObject obj, bool value) 
    { 
     obj.SetValue(IsEnabledProperty, value); 
    } 

    public static readonly DependencyProperty IsEnabledProperty = 
     DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(PixelBasedScrollingBehavior), new UIPropertyMetadata(false, HandleIsEnabledChanged)); 

    private static void HandleIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var vsp = d as VirtualizingStackPanel; 
     if (vsp == null) 
     { 
      return; 
     } 

     var property = typeof(VirtualizingStackPanel).GetProperty("IsPixelBased", 
                    BindingFlags.NonPublic | BindingFlags.Instance); 

     if (property == null) 
     { 
      throw new InvalidOperationException("Pixel-based scrolling behaviour hack no longer works!"); 
     } 

     if ((bool)e.NewValue == true) 
     { 
      property.SetValue(vsp, true, new object[0]); 
     } 
     else 
     { 
      property.SetValue(vsp, false, new object[0]); 
     } 
    } 
} 

Để sử dụng này trên một ListBox, ví dụ, bạn sẽ làm gì:

<ListBox> 
    <ListBox.ItemsPanel> 
     <ItemsPanelTemplate> 
     <VirtualizingStackPanel PixelBasedScrollingBehavior.IsEnabled="True"> 
      </VirtualizingStackPanel> 
     </ItemsPanelTemplate> 
    </ListBox.ItemsPanel> 
</ListBox> 
+19

Điều này thực sự hữu ích, +1. Đối với khách truy cập trong tương lai sử dụng .NET 4.5, bạn cần phải đặt 'VirtualizingPanel.ScrollUnit =" Pixel "' trên 'ListBox' của chính nó, không phải trên' VirtualizingStackPanel' chứa nội dung. –

+0

Ok, điều này không hiệu quả đối với tôi. Không biết tại sao. –

+0

@ViktorLaCroix: bạn đã cài đặt .Net 4.5 trên máy của bạn chưa? Bởi vì đó là một bản cập nhật tại chỗ cho .Net 4.0, tôi nghĩ rằng nó phá vỡ hack này. –

4

này đã làm việc cho tôi. Có vẻ như một vài thuộc tính đơn giản sẽ làm điều đó (.NET 4.5)

<ListBox    
    ItemsSource="{Binding MyItems}" 
    VirtualizingStackPanel.IsVirtualizing="True" 
    VirtualizingStackPanel.ScrollUnit="Pixel"/> 
Các vấn đề liên quan