2009-02-02 5 views
99

Ich habe ein benutzerdefiniertes WPF-Benutzersteuerelement erstellt, das von Dritten verwendet werden soll. Meine Kontrolle hat ein privates Mitglied, das wegwerfbar ist, und ich möchte sicherstellen, dass seine Entsorgungsmethode immer aufgerufen wird, sobald das enthaltende Fenster/die Anwendung geschlossen wird. UserControl ist jedoch nicht verfügbar. Ich habe versucht, die IDisposable-Schnittstelle zu implementieren und das Unloaded-Ereignis zu abonnieren, aber beide werden nicht aufgerufen, wenn die Host-Anwendung geschlossen wird. Wenn es möglich ist, möchte ich mich nicht darauf verlassen, dass die Konsumenten meiner Kontrolle sich erinnern, eine bestimmte Dispose-Methode aufzurufen.WPF-Benutzersteuerelemente verteilen

Die einzige Lösung, die ich bisher gefunden habe, ist das ShutdownStarted-Ereignis des Dispatchers zu abonnieren. Ist das ein vernünftiger Ansatz?

this.Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted; 
+0

Was ist mit der Unloaded Fall Benutzerkontrolle? – akjoshi

+2

@akjoshi: MSDN sagt Folgendes: Das entladene Ereignis wird möglicherweise überhaupt nicht ausgelöst.Und es kann auch mehr als einmal ausgelöst werden, wenn der Benutzer das Thema wechselt. – Dudu

+0

Während Sie die IDisposable-Schnittstelle in Ihrem Benutzersteuerelement implementieren können, gibt es keine Garantie dafür, dass Ihre Drittpartei die dispose-Methode Ihrer Dispose-Musterimplementierung aufrufen wird. Wenn Sie an nativen Ressourcen festhalten (z. B. einen Dateistream), sollten Sie einen Finalizer verwenden. – Philippe

Antwort

51

Interessante Blog-Post hier:

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

Es Nennungen zu Dispatcher_ShutDownStarted abonniert Ihre Ressourcen verfügen.

+0

Nun, ich hatte gehofft, dass es einen saubereren Weg als das geben würde, aber es sieht so aus, als wäre es jetzt das Beste, es zu tun. –

+28

Aber was, wenn der UserControl stirbt, bevor die App stirbt? Der Dispatcher wird nur scheuen, wenn die App funktioniert, oder? –

+0

Ganz zustimmen, aber ich verstehe nicht, warum das OP über Kontrollen verfügen muss. Sounds ... odd –

-3

Ein UserControl hat einen Destruktor, warum verwenden Sie das nicht?

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

Dies scheint nicht zu funktionieren. Ich habe es nur versucht und es wird nie aufgerufen. – JasonD

+8

Das ist kein Destruktor, es ist ein Finalizer. Sie implementieren immer einen Finalizer und Dispose als Paar, andernfalls riskieren Sie Lecks. –

+1

Und im Finalizer sollten Sie nur nicht verwaltete Objekte, aber nicht verwaltete Objekte bereinigen, da Finalizer in GC-Threads in nicht angegebener Reihenfolge ausgeführt werden, sodass verwaltete Objekte möglicherweise früher abgeschlossen werden und Dispose() Threadaffinität aufweisen kann. – Dudu

10

Sie müssen vorsichtig mit dem Destruktor sein. Dies wird im GC Finalizer-Thread aufgerufen. In einigen Fällen werden die freigegebenen Ressourcen möglicherweise nicht in einem anderen Thread als dem freigegeben, auf dem sie erstellt wurden.

+1

Vielen Dank für diese Warnung. das war genau mein Fall! _Application: devenv.exe Framework-Version: v4.0.30319 Beschreibung: Der Prozess wurde aufgrund einer unbehandelten Ausnahme beendet. Ausnahme Info: System.InvalidOperationException Stack: bei MyControl.Finalize() _ mein Lösungscode von Finalizerthread in ShutdownStarted – itsho

4

Mein Szenario ist ein wenig anders, aber die Absicht ist die gleiche ich würde gerne wissen, wenn das Elternfenster mein Benutzersteuerelement hosting/closed als Die Ansicht (dh my usercontrol) sollte die Moderatoren oncloseView aufrufen einige Funktionen auszuführen und mach sauber. (Nun, wir implementieren ein MVP-Muster in einer WPF PRISM-Anwendung).

Ich dachte nur, dass ich im Loaded-Ereignis der Benutzersteuerung, kann ich meine ParentWindowClosing-Methode an das Eltern-Windows-Closing-Ereignis anschließen. Auf diese Weise kann meine Benutzerkontrolle erkennen, wenn das Elternfenster geschlossen wird, und entsprechend handeln!

26

Dispatcher.ShutdownStarted Ereignis wird nur am Ende der Anwendung ausgelöst. Es lohnt sich, die Dispositionslogik sofort aufzurufen, wenn die Kontrolle außer Betrieb ist. Insbesondere werden Ressourcen freigegeben, wenn die Steuerung während der Laufzeit der Anwendung mehrmals verwendet wird. So ist ioWint 's Lösung vorzuziehen. Hier ist der Code:

+0

In einem Windows Store App, GetWindow() existiert nicht zu bewegen war. –

+0

Bravo, beste Antwort. – MDDDC

+4

Cœur: In einer Windows Store App verwenden Sie nicht WPF –

8

Ich verwende das folgende Interactivity Behavior, um WPF UserControls ein Entladeereignis bereitzustellen. Sie können das Verhalten in das UserControls XAML aufnehmen. So können Sie die Funktionalität haben, ohne die Logik in jedem einzelnen Benutzersteuerelement zu platzieren.

XAML-Deklaration:

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

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

Codebehind-Handler:

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

Verhalten Code:

/// <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); 
    } 
} 
+3

Ich würde hier eine Flagge zeigen ... was wenn alles andere bricht das Schließen des Fensters (vielleicht abonniert nach Ihrer Kontrolle so 'e.Cancel' ist immer noch falsch wenn es Ihren 'WindowClosingHandler'-Delegierten erreicht. Ihre Kontrolle wäre "entladen" und das Fenster wäre noch geöffnet. Ich würde das definitiv auf dem 'Closed' Event machen, nicht auf dem' Closing'. – Jcl

+0

Oh ja, ich habe diesen Punkt verpasst. Vielen Dank. –