การกำจัดการควบคุมผู้ใช้ WPF


119

ฉันได้สร้างการควบคุมผู้ใช้ WPF แบบกำหนดเองซึ่งมีวัตถุประสงค์เพื่อใช้โดยบุคคลที่สาม การควบคุมของฉันมีสมาชิกส่วนตัวซึ่งเป็นแบบใช้แล้วทิ้งและฉันต้องการให้แน่ใจว่าวิธีการกำจัดจะถูกเรียกใช้เสมอเมื่อปิดหน้าต่าง / แอปพลิเคชันที่มีอยู่ อย่างไรก็ตาม UserControl ไม่สามารถใช้แล้วทิ้ง ฉันลองใช้อินเทอร์เฟซ IDisposable และสมัครรับเหตุการณ์ Unloaded แต่ไม่ได้รับการเรียกเมื่อแอปพลิเคชันโฮสต์ปิด ถ้าเป็นไปได้ฉันไม่ต้องการพึ่งพาผู้บริโภคในการควบคุมของฉันที่จำเรียกวิธีการกำจัดเฉพาะ

 public partial class MyWpfControl : UserControl
 {
     SomeDisposableObject x;

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

     }
 }

ทางออกเดียวที่ฉันพบจนถึงตอนนี้คือสมัครรับเหตุการณ์ ShutdownStarted ของผู้มอบหมายงาน นี่เป็นแนวทางที่สมเหตุสมผลหรือไม่?

this.Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted;

แล้วเหตุการณ์ Unloaded ของการควบคุมผู้ใช้ล่ะ?
akjoshi

2
@akjoshi: MSDN กล่าวว่า: เหตุการณ์ที่ไม่ได้โหลดอาจไม่เกิดขึ้นเลย และอาจถูกทริกเกอร์มากกว่าหนึ่งครั้งนั่นคือเมื่อผู้ใช้เปลี่ยนธีม
Dudu

แม้ว่าคุณจะสามารถใช้อินเทอร์เฟซ IDisposable ในการควบคุมผู้ใช้ของคุณ แต่ก็ไม่มีการรับประกันว่าบุคคลที่สามของคุณจะเรียกใช้วิธีการกำจัดของการนำรูปแบบการกำจัดของคุณไปใช้ หากคุณใช้ทรัพยากรในระบบ (เช่นสตรีมไฟล์) คุณควรพิจารณาใช้โปรแกรมปิดท้าย
Philippe

คำตอบ:


57

โพสต์บล็อกที่น่าสนใจที่นี่:

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

มันกล่าวถึงการสมัครสมาชิกDispatcher การปิดระบบเริ่มต้นเพื่อกำจัดทรัพยากรของคุณ


1
ฉันหวังว่าจะมีวิธีที่สะอาดกว่านี้ แต่ดูเหมือนว่าตอนนี้นี่เป็นวิธีที่ดีที่สุดที่จะทำ
Mark Heath

35
แต่ถ้า UserControl ตายก่อนที่แอปจะตายล่ะ? Dispatcher จะอายก็ต่อเมื่อแอปทำงานใช่ไหม?
Robert Jeppesen

15
เนื่องจากตัวควบคุมหลายตัวใช้คอมโพเนนต์ COM หรือทรัพยากรอื่น ๆ ที่ไม่ได้รับการจัดการซ้ำซึ่งไม่ได้ถูกเข้ารหัสด้วยมุมมองที่จะถูกปล่อยทิ้งไว้อย่างไม่มีกำหนดหรือสรุปบนเธรดพูลเธรดและคาดว่า / ต้องการการจัดสรรที่กำหนด
Neutrino

1
ในแอพ Windows Store ไม่มี ShutdownStarted
Cœur

7
หรือคุณจำเป็นต้องยกเลิกการอ้างอิงตัวจัดการเหตุการณ์หรือคุณต้องหยุดเธรดที่เริ่มต้นในการควบคุมนั้น ...
DanW

40

Dispatcher.ShutdownStartedเหตุการณ์จะเริ่มทำงานเมื่อสิ้นสุดการสมัครเท่านั้น มันคุ้มค่าที่จะเรียกตรรกะการกำจัดเมื่อการควบคุมไม่ได้ใช้งาน โดยเฉพาะอย่างยิ่งมันปลดปล่อยทรัพยากรเมื่อมีการใช้การควบคุมหลายครั้งในระหว่างรันไทม์ของแอปพลิเคชัน ดังนั้นวิธีแก้ปัญหาของioWintจึงเป็นที่ต้องการ นี่คือรหัส:

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
     };
}

ในแอพ Windows Store ไม่มี GetWindow ()
Cœur

ไชโยคำตอบที่ดีที่สุด
Nic

8
Cœur: ในแอป Windows Store คุณไม่ได้ใช้ WPF
Alan Baljeu

3
จะเกิดอะไรขึ้นถ้ามีหน้าต่างเพิ่มเติมที่เกี่ยวข้องและหน้าต่างหลักไม่เคยปิด? หรือการควบคุมของคุณโฮสต์อยู่ในหน้าที่โหลด / ยกเลิกการโหลดหลายครั้ง? ดู: stackoverflow.com/a/14074116/1345207
L.Trabacchin

1
หน้าต่างอาจไม่ได้ปิดบ่อยนัก หากตัวควบคุมเป็นส่วนหนึ่งของรายการหลายรายการจะถูกสร้าง / ทำลายจนกว่าหน้าต่างพาเรนต์จะปิดลง
แพ้

15

คุณต้องระวังการใช้ตัวทำลาย สิ่งนี้จะถูกเรียกในเธรด GC Finalizer ในบางกรณีทรัพยากรที่คุณพ้นไปอาจไม่ชอบที่จะเผยแพร่ในเธรดที่แตกต่างจากที่สร้างขึ้น


1
ขอบคุณสำหรับคำเตือนนี้ นี่คือกรณีของฉันเป๊ะ! แอพลิเคชัน: devenv.exe Framework เวอร์ชัน: v4.0.30319 คำอธิบาย: กระบวนการนี้สิ้นสุดลงเนื่องจากมีข้อยกเว้นที่ไม่สามารถจัดการได้ ข้อมูลข้อยกเว้น: System.InvalidOperationException Stack: ที่ MyControl.Finalize () วิธีการแก้ปัญหาของฉันคือการย้ายรหัสจาก Finalizer ไปยัง ShutdownStarted
itsho

10

ฉันใช้พฤติกรรมการโต้ตอบต่อไปนี้เพื่อจัดเตรียมเหตุการณ์การยกเลิกการโหลดให้กับ WPF UserControls คุณสามารถรวมลักษณะการทำงานใน UserControls XAML ดังนั้นคุณสามารถใช้งานได้โดยไม่ต้องวางตรรกะไว้ใน UserControl ทุกรายการ

การประกาศ XAML:

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

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

CodeBehind ตัวจัดการ:

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

รหัสพฤติกรรม:

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

5
ฉันจะตั้งค่าสถานะที่นี่ ... จะเกิดอะไรขึ้นถ้ามีสิ่งอื่นใดที่ยกเลิกการปิดหน้าต่าง (อาจสมัครสมาชิกหลังจากการควบคุมของคุณดังนั้นe.Cancelยังคงเป็นเท็จเมื่อถึงWindowClosingHandlerผู้รับมอบสิทธิ์ของคุณ) การควบคุมของคุณจะ "ยกเลิกการโหลด" และหน้าต่างยังคงเปิดอยู่ ฉันจะทำสิ่งนี้ในงานนี้แน่นอนClosedไม่ใช่ในงานClosingเดียว
Jcl

6

สถานการณ์ของฉันแตกต่างกันเล็กน้อย แต่เจตนาเหมือนกันฉันต้องการทราบเมื่อหน้าต่างหลักที่โฮสต์การควบคุมผู้ใช้ของฉันปิด / ปิดเนื่องจากมุมมอง (เช่นผู้ใช้ของฉัน) ควรเรียกใช้ผู้นำเสนอ oncloseView เพื่อดำเนินการฟังก์ชันบางอย่างและทำการล้างข้อมูล (เรากำลังใช้รูปแบบ MVP บนแอปพลิเคชัน WPF PRISM)

ฉันเพิ่งพบว่าในเหตุการณ์ Loaded ของ usercontrol ฉันสามารถเชื่อมต่อเมธอด ParentWindowClosing ของฉันกับเหตุการณ์การปิดหน้าต่างหลัก วิธีนี้ Usercontrol ของฉันสามารถรับรู้ได้เมื่อหน้าต่างหลักถูกปิดและดำเนินการตามนั้น!


0

ฉันคิดว่าการยกเลิกการโหลดเรียกว่าทั้งหมด แต่มีอยู่ใน 4.7 แต่ถ้าคุณกำลังเล่นกับ. Net เวอร์ชันเก่าให้ลองทำสิ่งนี้ในวิธีการโหลดของคุณ:

e.Handled = true;

ฉันไม่คิดว่าเวอร์ชันเก่าจะยกเลิกการโหลดจนกว่าจะจัดการการโหลด แค่โพสต์เพราะเห็นว่ามีคนอื่นถามคำถามนี้อยู่และยังไม่เห็นข้อเสนอนี้เป็นวิธีแก้ปัญหา ฉันสัมผัส. Net ปีละสองสามครั้งเท่านั้นและเมื่อไม่กี่ปีที่ผ่านมานี้ แต่ฉันสงสัยว่ามันง่ายอย่างที่เรียกว่ายกเลิกการโหลดจนกว่าจะโหลดเสร็จ ดูเหมือนว่ามันจะใช้ได้สำหรับฉัน แต่อีกครั้งใน. Net ที่ใหม่กว่าดูเหมือนว่าจะเรียกใช้การยกเลิกการโหลดเสมอแม้ว่าการโหลดจะไม่ถูกทำเครื่องหมายว่าจัดการก็ตาม


-3

UserControl มี Destructor ทำไมคุณไม่ใช้มันล่ะ?

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

ดูเหมือนจะไม่ได้ผล ฉันแค่ลองวิธีนั้นและมันไม่เคยถูกเรียกเลย
JasonD

9
นั่นไม่ใช่ตัวทำลาย แต่เป็นการปิดท้าย คุณติดตั้ง Finalizer และกำจัดเป็นคู่เสมอมิฉะนั้นคุณจะเสี่ยงต่อการรั่วไหล
หลังไมค์

1
และใน Finalizer คุณควรล้างอ็อบเจ็กต์ที่ไม่มีการจัดการเท่านั้น แต่ไม่ใช่อ็อบเจ็กต์ที่มีการจัดการเนื่องจากตัวสุดท้ายถูกรันในลำดับที่ไม่ระบุในเธรด GC ดังนั้นอ็อบเจ็กต์ที่มีการจัดการจึงอาจถูกสรุปก่อนหน้านี้และ Dispose () อาจมีความสัมพันธ์ของเธรด
Dudu

2
joeduffyblog.com/2005/04/08/…เป็นคำอธิบายที่ดีที่สุดของ Finalize and Dispose ที่ฉันพบ น่าอ่านจริงๆ
dss539
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.