2009-10-11 41 views
20

Tôi tương đối mới với WPF, và một số thứ với nó khá lạ với tôi. Đối với một, không giống như Windows Forms, hệ thống phân cấp điều khiển WPF không hỗ trợ IDisposable. Trong Windows Forms, nếu một điều khiển người dùng sử dụng bất kỳ tài nguyên được quản lý nào, rất dễ dàng để dọn sạch tài nguyên bằng cách ghi đè phương thức Vứt bỏ mà mọi điều khiển được thực hiện.Dọn dẹp đúng cách các điều khiển người dùng WPF

Trong WPF, câu chuyện không đơn giản như vậy. Tôi đã tìm kiếm điều này trong vài giờ và gặp hai chủ đề cơ bản:

Chủ đề đầu tiên là Microsoft nêu rõ rằng WPF không triển khai IDisposable vì các điều khiển WPF không có tài nguyên không được quản lý. Trong khi điều đó có thể đúng, họ dường như đã hoàn toàn bỏ lỡ thực tế là các phần mở rộng của người dùng đến phân cấp lớp WPF của họ thực sự có thể sử dụng các tài nguyên được quản lý (trực tiếp hoặc gián tiếp thông qua một mô hình). Bằng cách không triển khai IDisposable, Microsoft đã gỡ bỏ một cách hiệu quả cơ chế được bảo đảm duy nhất mà tài nguyên không được quản lý được sử dụng bởi một điều khiển WPF hoặc cửa sổ tùy chỉnh có thể được dọn dẹp.

Thứ hai, tôi đã tìm thấy một vài tham chiếu đến Dispatcher.ShutdownStarted. Tôi đã cố gắng sử dụng sự kiện ShutdownStarted, nhưng nó dường như không kích hoạt mọi điều khiển. Tôi có một bó của WPF UserControl mà tôi đã thực hiện một xử lý cho ShutdownStarted, và nó không bao giờ được gọi. Tôi không chắc chắn nếu nó chỉ hoạt động cho Windows, hoặc có lẽ là lớp ứng dụng WPF. Tuy nhiên nó không phải là đúng cách bắn, và tôi đang bị rò rỉ các đối tượng PerformanceCounter mở mỗi khi ứng dụng đóng lại.

Có cách nào tốt hơn để dọn sạch tài nguyên không được quản lý hơn sự kiện Dispatcher.ShutdownStarted không? Có một số thủ thuật để thực hiện IDisposable sao cho Dispose sẽ được gọi? Tôi muốn nhiều hơn tránh sử dụng trình hoàn thiện nếu có thể.

Trả lời

12

Tôi sợ rằng Dispatcher.ShutdownStarted thực sự dường như là cơ chế duy nhất mà WPF cung cấp để xử lý tài nguyên trong UserControls. (Xem rất similar question Tôi đã hỏi một lúc trước).

Một cách khác để tiếp cận vấn đề là di chuyển tất cả các tài nguyên dùng một lần của bạn (nếu có thể) ra khỏi mã phía sau và vào các lớp riêng biệt (chẳng hạn như ViewModel khi sử dụng mẫu MVVM). Sau đó, ở một mức độ cao hơn, bạn có thể xử lý cửa sổ chính của bạn đóng cửa và thông báo cho tất cả các ViewModels thông qua một lớp Messenger.

Tôi rất ngạc nhiên khi bạn không nhận được sự kiện Dispatcher.ShutdownStarted. Là UserControls của bạn gắn liền với cửa sổ cấp cao nhất vào thời điểm đó?

+4

+1 để di chuyển tài nguyên dùng một lần ra khỏi codebehind. Một trong những điểm học tập chính của WPF là giảm thiểu hệ thống mã hóa để tận dụng lợi thế của sức mạnh và tính biểu cảm của kiến ​​trúc dữ liệu. Đó là một điều khó khăn để tìm hiểu (đường cong học tập giống như leo lên một vách đá), nhưng bổ ích khi bạn "nhận" chế độ suy nghĩ của WPF. –

+0

Tất cả các tài nguyên dùng một lần đều thực sự trong ViewModel, là bản thân IDisposable. Tôi thực sự bối rối về lý do tại sao sự kiện Dispatcher.ShutdownStarted không kích hoạt. Kiểm soát truy cập hiệu suất (và ViewModel liên quan của nó) thực sự được gắn vào biểu đồ WPF khi nó được nhúng vào trong một trong một . – jrista

+1

@Greg D: Tôi thường nhận được mô hình WPF. Tôi bắt đầu sử dụng MVVM ngay sau khi tôi đã nắm bắt được những điều cơ bản của WPF, và CodeBehind của tôi là khá nhiều như trần như nó được (chỉ đơn giản là constructor mặc định và các cuộc gọi của nó để InitializeComponent). Khả năng composability và databinding của WPF là tuyệt vời hơn, và tôi sẽ không bao giờ quay trở lại các hình thức cửa sổ nếu tôi có sự lựa chọn. – jrista

9

Giao diện IDisposable (gần như) không có ý nghĩa trong WPF, vì cơ chế khác với Winforms. Trong WPF, bạn phải ghi nhớ cây trực quan và logic: đó là nền tảng.
Vì vậy, bất kỳ đối tượng trực quan nào cũng thường là con của một số đối tượng khác. Cơ sở của cơ chế xây dựng WPF là đính kèm đối tượng trực quan theo thứ bậc, sau đó tách ra và phá hủy khi chúng không hữu ích.

Tôi nghĩ bạn có thể kiểm tra phương pháp OnVisualParentChanged được hiển thị từ UIElement: phương pháp này được gọi khi đối tượng trực quan được đính kèm và khi được tách ra. Đó có thể là nơi thích hợp để xử lý các đối tượng không được quản lý (ổ cắm, tệp, v.v.).

+0

Cảm ơn lời khuyên về OnVisualParentChanged. Tôi sẽ chơi với điều đó và xem liệu nó có giúp giải quyết vấn đề của tôi không. – jrista

0

Trong khi những người khác đã cung cấp cho bạn thông tin thực sự hữu ích về vấn đề này, có một chút thông tin mà bạn có thể không có mà sẽ giải thích rất nhiều về lý do tại sao không có IDisposable. Về cơ bản, WPF (và Silverlight) sử dụng rất nhiều các WeakReferences - điều này cho phép bạn tham chiếu một đối tượng mà GC vẫn có thể thu thập.

+0

Cảm ơn sự thấu hiểu Pete. Tôi tò mò nếu bạn có một số liên kết giải thích điều này chi tiết hơn? Tôi tò mò về cách sử dụng WeakReferences nặng không gây ra vấn đề gì. Chúng có thể là một công cụ mạnh mẽ trong các tình huống thích hợp ... nhưng tôi không thể tưởng tượng chúng được sử dụng như thế nào trong WPF. – jrista

5

tôi đang tìm kiếm này quá và sau khi kiểm tra tùy chọn differents tôi thực hiện các giải pháp của Venezia

protected override void OnVisualParentChanged(DependencyObject oldParent) 
    { 
     if (oldParent != null) 
     { 
      MyOwnDisposeMethod(); //Release all resources here 
     } 

     base.OnVisualParentChanged(oldParent); 
    } 

tôi nhận ra rằng khi gọi cha mẹ Children.Clear() Phương pháp và đã ghi thêm vào trẻ em, DependencyObject có một giá trị. Nhưng khi cha mẹ thêm một mục (Children.Add(CustomControl)) và trẻ em trống rỗng DependencyObject là null.

+0

Tôi đã thay đổi nó thành (nếu == null) để nếu tôi di chuyển điều khiển đến một vùng chứa khác, nó sẽ không tự hủy – Sean

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