2012-12-14 28 views
5

Tôi có chế độ xem trong ứng dụng của tôi với bảng lớn như dữ liệu để hiển thị. Dữ liệu được hiển thị trong hai UniformGrids lồng nhau. UniformGrids là ItemPanels trong ItemsControls và bị ràng buộc với một số ViewModels. Xem các hình ảnh sau đây và một số ví dụ XAML-Code:Làm thế nào để xử lý hiệu suất trong UniformGrids rất lớn?

view and viewmodel http://img593.imageshack.us/img593/8825/stackoverflowuniformgri.png

<!-- The green boxes --> 
<ItemsControl ItemsSource="{Binding BigCells}"> 

    <ItemsControl.ItemPanel> 
    <PanelTemplate> 
     <UniformGrid /> 
    </PanelTemplate> 
    </ItemsControl.ItemPanel> 

    <ItemsControl.ItemTemplate> 
    <DataTemplate> 

     <!-- The blue boxes --> 
     <ItemsControl ItemsSource="{Binding SmallCells}"> 
     <ItemsControl.ItemPanel> 
      <PanelTemplate> 
      <UniformGrid /> 
      </PanelTemplate> 
     </ItemsControl.ItemPanel> 
     </ItemsControl> 

    </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 

Bây giờ, tôi muốn quan điểm của tôi là thay đổi kích thước nhưng điều này không hoạt động tốt ở tất cả vì bố trí mỗi đơn hộp nhỏ được tính toán.
Điều này có thể ít nhất cho kích thước hộp được thực hiện chỉ một lần, vì nó là bằng nhau cho tất cả các hộp.

Phương pháp hay nhất để hiển thị số lượng điều khiển lớn trong WPF/nơi tôi có thể bắt đầu tối ưu hóa? Từ khóa đã giúp tôi tiếp tục khám phá tối ưu hóa hiệu suất cho UniformGrids trong WPF.

Trả lời

8

Tôi đã gặp sự cố tương tự khi sử dụng nhiều WPF DataGrid cùng một lúc. Nó cuối cùng đã xuống đến hai vấn đề chính:

  • điển Resource
  • Bindings

Resource Từ điển

Những có thể tắc nghẽn lớn nếu bạn sử dụng cơ bản WPF tính năng mà không bị cẩn thận.

Trong trường hợp của tôi, đối với một cuộn logic duy nhất trong ItemsControl được chứa DataGrids của tôi, tôi đã nhận được một cái gì đó giống như 10 triệu gọi để GetValueGetValueWithoutLock trên ResourceDictionary. Hơn 1 giây để xử lý.

này số chính của ResourceDictionary truy cập được gây ra bởi các nguồn khác nhau:

  • động thu hồi tài nguyên: Nếu bạn có ControlTemplates, và tổng quát hơn các nguồn lực được đặt trong một số từ điển tài nguyên, và bạn truy cập chúng qua {DynamicResource ...}, loại bỏ chúng . Có một số tĩnh một nơi nào đó và acces chúng trực tiếp.
  • Tìm nạp kiểu: Nếu bạn không có kiểu dáng trên Hình ảnh khác nhau bạn sử dụng hoặc nếu bạn có Kiểu nhưng không đặt thuộc tính FrameworkElement.OverridesDefaultStyle, WPF sẽ cố gắng tìm kiểu phù hợp với kiểm soát trong tất cả các tài nguyên, kết quả trong rất nhiều quyền truy cập vào từ điển tài nguyên. Để tránh điều đó, hãy nhớ ghi đè tất cả các mẫu điều khiển của các điều khiển của bạn và đặt Style={x:Null} trên mỗi và mọi điều khiển (nếu bạn cần một kiểu cho điều khiển, đặt nó trong điều khiển của bạn và thêm OverridesDefaultStyle = true)
  • Ngụ ý DataTemplates: Mẫu dữ liệu ngầm định rất hữu ích nhưng không có gì nhiều. Khi tìm kiếm DataTemplate để áp dụng, WPF sẽ lại một lần nữa duyệt từ điển tài nguyên của bạn để tìm một số DataTemplate phù hợp với các loại Chế độ xem của bạn. Giải pháp là sử dụng bộ chọn DataTemplate cho mỗi và mọi điều khiển được liên kết với một ViewModel và triển khai cách tối ưu hóa để truy xuất đúng DataTemplate. (Cá nhân tôi có một số lĩnh vực tĩnh cho dataTemplate của tôi mà tôi lấy chỉ một lần từ một resourceDictionary, và một tối ưu hóa DataTemplateSelector trả về chúng khi cần thiết.)

Bindings

Bindings rất hữu ích, nhưng rất tốn kém, bộ nhớ khôn ngoan và hiệu suất khôn ngoan. Trong một môi trường rất hợp lý - từ một quan điểm thực hiện - bạn không thể chỉ sử dụng chúng mà không cần phải cẩn thận. Ví dụ, ràng buộc có thể tạo tối đa 3 WeakReferences cho mỗi đối tượng liên kết, có thể sử dụng sự phản chiếu, vv Cuối cùng tôi đã gỡ bỏ gần như tất cả các trình xử lý sự kiện ràng buộc và cắm vào sự kiện PropertyChexted của DataContext. Khi DataContext thay đổi trên Điều khiển của tôi (bạn có sự kiện DataContextChanged) Tôi kiểm tra xem DataContext có hỗ trợ INotifyPropertyChanged hay không và trong trường hợp đó, tôi đính kèm một trình xử lý sự kiện trên PropertyChanged sự kiện và cập nhật các thuộc tính nếu cần. (Tôi chỉ có một cách ràng buộc, vì vậy đây là lý do tại sao tôi đã chọn cách làm việc này, nhưng có những giải pháp khác).

Điều này có vẻ khó chịu khi không sử dụng các ràng buộc khi sử dụng MVVM và WPF, nhưng thực sự không có cách nào để tối ưu hóa các ràng buộc trong trường hợp của tôi.

Điều cuối cùng: nếu bạn có Freezable đối tượng (chẳng hạn như Brushes chẳng hạn), đừng ngần ngại Freeze nếu bạn có thể.

Đó là một số lời khuyên có thể giúp bạn tối ưu hóa mã của mình. Bạn sẽ cần một profiler (Tôi luôn luôn khuyên dùng dotTrace vì đây là profiler tốt nhất mà tôi từng sử dụng) để theo dõi những gì đang thực sự xảy ra và điều chỉnh theo kết quả của bạn.

Chúc may mắn!

+0

Cảm ơn câu trả lời chi tiết của bạn. Nó đã chứa một số điểm tôi có thể cải thiện trong ứng dụng của tôi (freezables, ghi đè kiểu mặc định). Tôi sẽ kiểm tra điều này và báo cáo sau. –

+0

Chắc chắn, hãy cho tôi biết cách thức hoạt động! Nó thực sự là một bài tập tốt cho tôi khi tôi phải viết một bài đăng trên blog của tôi :) – Sisyphe

+0

Chúng tôi có rất nhiều từ điển tài nguyên cho mọi thứ và ứng dụng vẫn rất nhanh.Cách chúng ta tiếp cận nó là tạo một từ điển tài nguyên cho một tài nguyên (như kiểu, vectơ, cọ vẽ, vv), sau đó từ mỗi khung nhìn, chúng ta chỉ tham khảo các từ điển tài nguyên cần thiết. Vấn đề là bản thân lưới đồng nhất, bởi vì nó không ảo hóa. – adminSoftDK

4

Nếu bạn chắc chắn về tất cả các hộp muốn có cùng kích thước, bạn luôn có thể tự tạo lớp UniformGrid và ghi đè MeasureOverride để chỉ đo một trẻ em. Tôi có thể kết thúc với một số UniformGrids lớn với các điều khiển nội bộ cùng kích thước trong dự án hiện tại của tôi, vì vậy ý ​​tưởng của bạn hấp dẫn tôi và tôi quyết định thử nó thật nhanh:

Tôi đã sử dụng lớp Petzold's UniformGridAlmost làm nguồn cảm hứng UniformGrid thay vì Panel do đó các thuộc tính phụ thuộc hàng và cột không cần phải được sao chép. UniformGrid dường như không công khai số lượng hàng và cột được tính toán cho các lớp dẫn xuất của nó, vì vậy tôi đã mượn một triển khai từ Silverlight's UniformGrid class và đã sử dụng phương thức UpdateComputedValues.

Các MeasureOverride trong lớp học của tôi trông như thế này

protected override Size MeasureOverride(Size sizeAvailable) 
    { 
    if (InternalChildren.Count == 0) 
    { 
     return new Size(0, 0); 
    } 

    UpdateComputedValues(); 

    // Calculate a child size based on uniform rows and columns. 
    Size sizeChild = new Size(sizeAvailable.Width/ComputedColumns, 
           sizeAvailable.Height/ComputedRows); 

    // Assume children will measure to at least a comparable size. 
    UIElement child = InternalChildren[0]; 
    child.Measure(sizeChild); 

    double width = Math.Max(width, child.DesiredSize.Width); 
    double height = Math.Max(height, child.DesiredSize.Height); 
    return new Size(ComputedColumns * width, ComputedRows * height); 
    } 

Vì vậy, đây là kicker, khi tôi thử nghiệm nó trên một ItemsControl với 10000 TextBlocks, phiên bản của tôi về UniformGrid là nhanh hơn, nhưng chỉ có một lượng nhỏ (dưới 1% - Tôi xác nhận rằng cuộc gọi đến MeasureOverride nhanh hơn khoảng 95%). Vì vậy, điều này có lẽ không hữu ích, nhưng tôi nghĩ rằng tôi sẽ chia sẻ kinh nghiệm của tôi. Quan sát của bạn rằng nó không cần phải đo mỗi hộp để xác định bố trí thống nhất trong trường hợp của bạn là đúng, nhưng đo lường đó không phải là những gì làm giảm nó xuống. Tôi chắc chắn đó là bởi vì tất cả những điều khiển đó vẫn cần phải được rút ra, vì vậy chúng sẽ được đo sau này. Bạn sẽ nhận được nhiều hơn từ đề xuất ResourceDictionary/Binding nhiều khả năng nhất.

+0

Tôi vừa thử sử dụng UniformGridAlmost này và trong trường hợp của tôi nhanh hơn gấp 3 lần so với lưới đồng nhất tiêu chuẩn. Vì vậy, thời gian vẽ giảm từ 20 giây xuống còn khoảng 7 giây. – adminSoftDK

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