Membuang Kontrol Pengguna WPF

119

Saya telah membuat kontrol pengguna WPF khusus yang dimaksudkan untuk digunakan oleh pihak ketiga. Kontrol saya memiliki anggota pribadi yang dapat dibuang, dan saya ingin memastikan bahwa metode pembuangannya akan selalu dipanggil setelah jendela / aplikasi yang memuatnya ditutup. Namun, UserControl tidak dapat dibuang. Saya mencoba mengimplementasikan antarmuka IDisposable dan berlangganan event Unloaded tetapi tidak ada yang dipanggil ketika aplikasi host ditutup. Jika memungkinkan, saya tidak ingin bergantung pada konsumen dalam kendali saya mengingat untuk memanggil metode Buang tertentu.

 public partial class MyWpfControl : UserControl
 {
     SomeDisposableObject x;

     // where does this code go?
     void Somewhere() 
     {
         if (x != null)
         {
             x.Dispose();
             x = null;
         }

     }
 }

Satu-satunya solusi yang saya temukan sejauh ini adalah berlangganan acara Dispatcher ShutdownStarted. Apakah ini pendekatan yang masuk akal?

this.Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted;
Mark Heath
sumber
Bagaimana dengan acara Bongkar kontrol pengguna?
akjoshi
2
@akjoshi: MSDN mengatakan bahwa: Acara yang dibongkar mungkin tidak dimunculkan sama sekali. Dan itu mungkin juga dipicu lebih dari sekali, yaitu saat pengguna mengubah tema.
Dudu
Meskipun Anda dapat mengimplementasikan antarmuka IDisposable pada kontrol pengguna Anda, tidak ada jaminan bahwa pihak ketiga Anda akan memanggil metode pembuangan implementasi pola Buang Anda. Jika Anda berpegang pada sumber daya asli (misalnya aliran file), Anda harus mempertimbangkan untuk menggunakan finalizer.
Philippe

Jawaban:

57

Posting blog yang menarik di sini:

http://geekswithblogs.net/cskardon/archive/2008/06/23/dispose-of-a-wpf-usercontrol-ish.aspx

Itu menyebutkan berlangganan Dispatcher.ShutdownStarted untuk membuang sumber daya Anda.

Ray Booysen
sumber
1
Saya berharap akan ada cara yang lebih bersih dari ini, tetapi sepertinya untuk saat ini inilah yang terbaik untuk melakukannya.
Mark Heath
35
Tetapi bagaimana jika UserControl mati sebelum aplikasi mati? Dispatcher hanya akan diam ketika aplikasi melakukannya, bukan?
Robert Jeppesen
15
Karena banyak kontrol menggunakan kembali komponen COM atau sumber daya tidak terkelola lainnya yang tidak dikodekan dengan maksud untuk dibiarkan berkeliaran tanpa batas, atau diselesaikan pada thread pool thread, dan mengharapkan / memerlukan deallocation deterministik.
Neutrino
1
Di Aplikasi Windows Store, ShutdownStarted tidak ada.
Cœur
7
Atau Anda perlu dereferensi penangan acara, atau Anda perlu menghentikan utas yang dimulai dalam kontrol itu, ...
DanW
40

Dispatcher.ShutdownStartedacara dipecat hanya pada akhir aplikasi. Layak untuk memanggil logika disposing hanya ketika kontrol tidak lagi digunakan. Secara khusus ia membebaskan sumber daya ketika kontrol digunakan berkali-kali selama runtime aplikasi. Jadi solusi ioWint lebih disukai. Berikut kodenya:

public MyWpfControl()
{
     InitializeComponent();
     Loaded += (s, e) => { // only at this point the control is ready
         Window.GetWindow(this) // get the parent window
               .Closing += (s1, e1) => Somewhere(); //disposing logic here
     };
}
Ilia Barahovski
sumber
Di Aplikasi Windows Store, GetWindow () tidak ada.
Cœur
Bravo, jawaban terbaik.
Nic
8
Cœur: Di Aplikasi Windows Store, Anda tidak menggunakan WPF
Alan Baljeu
3
bagaimana jika ada lebih banyak jendela yang terlibat dan yang utama tidak pernah ditutup? Atau kontrol Anda dihosting di halaman yang dimuat / dibongkar beberapa kali? lihat: stackoverflow.com/a/14074116/1345207
L. Trabacchin
1
Jendela mungkin tidak terlalu sering menutup. Jika kontrol adalah bagian dari item daftar, banyak yang akan dibuat / dimusnahkan hingga jendela induknya dapat ditutup.
HILANG
15

Anda harus berhati-hati menggunakan destruktor. Ini akan dipanggil di utas GC Finalizer. Dalam beberapa kasus, sumber daya yang mungkin tidak disukai oleh pembebasan Anda pada utas yang berbeda dari tempat pembuatannya.

Ade Miller
sumber
1
Terima kasih atas peringatan ini. ini kasus saya tepatnya! Aplikasi: Kerangka devenv.exe Versi: v4.0.30319 Deskripsi: Proses dihentikan karena pengecualian tidak tertangani. Info Pengecualian: System.InvalidOperationException Stack: di MyControl.Finalize () solusi saya adalah memindahkan kode dari finalizer ke ShutdownStarted
itsho
10

Saya menggunakan Perilaku Interaktivitas berikut untuk menyediakan acara pembongkaran ke WPF UserControls. Anda dapat menyertakan perilaku di UserControls XAML. Jadi, Anda dapat memiliki fungsionalitas tanpa menempatkan logika di setiap UserControl.

Deklarasi XAML:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

<i:Interaction.Behaviors>
    <behaviors:UserControlSupportsUnloadingEventBehavior UserControlClosing="UserControlClosingHandler" />
</i:Interaction.Behaviors>

Penangan CodeBehind:

private void UserControlClosingHandler(object sender, EventArgs e)
{
    // to unloading stuff here
}

Kode Perilaku:

/// <summary>
/// This behavior raises an event when the containing window of a <see cref="UserControl"/> is closing.
/// </summary>
public class UserControlSupportsUnloadingEventBehavior : System.Windows.Interactivity.Behavior<UserControl>
{
    protected override void OnAttached()
    {
        AssociatedObject.Loaded += UserControlLoadedHandler;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.Loaded -= UserControlLoadedHandler;
        var window = Window.GetWindow(AssociatedObject);
        if (window != null)
            window.Closing -= WindowClosingHandler;
    }

    /// <summary>
    /// Registers to the containing windows Closing event when the UserControl is loaded.
    /// </summary>
    private void UserControlLoadedHandler(object sender, RoutedEventArgs e)
    {
        var window = Window.GetWindow(AssociatedObject);
        if (window == null)
            throw new Exception(
                "The UserControl {0} is not contained within a Window. The UserControlSupportsUnloadingEventBehavior cannot be used."
                    .FormatWith(AssociatedObject.GetType().Name));

        window.Closing += WindowClosingHandler;
    }

    /// <summary>
    /// The containing window is closing, raise the UserControlClosing event.
    /// </summary>
    private void WindowClosingHandler(object sender, CancelEventArgs e)
    {
        OnUserControlClosing();
    }

    /// <summary>
    /// This event will be raised when the containing window of the associated <see cref="UserControl"/> is closing.
    /// </summary>
    public event EventHandler UserControlClosing;

    protected virtual void OnUserControlClosing()
    {
        var handler = UserControlClosing;
        if (handler != null) 
            handler(this, EventArgs.Empty);
    }
}
alex.enjoy
sumber
5
Saya akan mengibarkan bendera di sini ... bagaimana jika ada hal lain yang membatalkan penutupan jendela (mungkin berlangganan setelah kontrol Anda jadi e.Cancelmasih salah saat mencapai WindowClosingHandlerdelegasi Anda ) ?. Kontrol Anda akan "diturunkan" dan jendela masih terbuka. Saya pasti akan melakukan ini di Closedacara, bukan di Closingsatu acara.
Jcl
6

Skenario saya sedikit berbeda, tetapi maksudnya sama saya ingin tahu kapan jendela induk hosting kontrol pengguna saya ditutup / ditutup sebagai Tampilan (yaitu kontrol pengguna saya) harus memanggil penyaji oncloseView untuk menjalankan beberapa fungsi dan melakukan pembersihan. (nah kami mengimplementasikan pola MVP pada aplikasi WPF PRISM).

Saya baru saja membayangkan bahwa dalam acara Loaded dari usercontrol, saya dapat menghubungkan metode ParentWindowClosing saya ke acara Parent windows Closing. Dengan cara ini, Usercontrol saya bisa waspada ketika jendela Induk ditutup dan bertindak sesuai dengan itu!

ioWint
sumber
0

Saya pikir bongkar disebut semua tetapi sulit ada di 4.7. Tetapi, jika Anda bermain-main dengan versi .Net yang lebih lama, coba lakukan ini dalam metode pemuatan Anda:

e.Handled = true;

Saya tidak berpikir versi lama akan membongkar sampai pemuatan ditangani. Hanya memposting karena saya melihat orang lain masih menanyakan pertanyaan ini, dan belum melihat ini diusulkan sebagai solusi. Saya hanya menyentuh .Net beberapa kali setahun, dan mengalami hal ini beberapa tahun yang lalu. Tapi, saya bertanya-tanya apakah sesederhana membongkar tidak dipanggil sampai pemuatan selesai. Sepertinya ini berfungsi untuk saya, tetapi sekali lagi di .Net yang lebih baru sepertinya selalu memanggil bongkar bahkan jika pemuatan tidak ditandai sebagai ditangani.

Michael T.
sumber
-3

Sebuah UserControl memiliki Destructor, mengapa Anda tidak menggunakannya?

~MyWpfControl()
    {
        // Dispose of any Disposable items here
    }

sumber
Ini sepertinya tidak berhasil. Saya baru saja mencoba pendekatan itu dan tidak pernah dipanggil.
JasonD
9
Itu bukan destruktor, itu finalisator. Anda selalu menerapkan finalizer dan Buang sebagai pasangan jika tidak Anda berisiko bocor.
Mike Post
1
Dan, di finalizer Anda hanya harus membersihkan objek yang tidak dikelola tetapi bukan objek yang dikelola, karena finalizer dijalankan dalam urutan yang tidak ditentukan dalam utas GC sehingga objek yang dikelola dapat diselesaikan lebih awal dan Dispose () mereka mungkin memiliki afinitas utas.
Dudu
2
joeduffyblog.com/2005/04/08/… adalah penjelasan terbaik dari Selesaikan dan Buang yang saya temukan. Ini sangat berharga untuk dibaca.
dss539