WPF Kullanıcı Denetimlerini Atma


119

Üçüncü bir taraf tarafından kullanılması amaçlanan özel bir WPF kullanıcı denetimi oluşturdum. Kontrolümün tek kullanımlık özel bir üyesi var ve elden çıkarma yönteminin her zaman içeren pencere / uygulama kapatıldıktan sonra çağrılacağından emin olmak istiyorum. Ancak UserControl tek kullanımlık değildir. IDisposable arabirimini uygulamayı ve Unloaded olayına abone olmayı denedim, ancak ana uygulama kapandığında ikisi de çağrılmadı. Mümkünse, belirli bir Dispose yöntemini çağırmayı hatırlayan kontrolümün tüketicilerine güvenmek istemiyorum.

 public partial class MyWpfControl : UserControl
 {
     SomeDisposableObject x;

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

     }
 }

Şimdiye kadar bulduğum tek çözüm, Dispatcher'ın ShutdownStarted olayına abone olmak. Bu makul bir yaklaşım mı?

this.Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted;

Kullanıcı kontrolünün Yüklenmemiş olayı ne olacak?
akjoshi

2
@akjoshi: MSDN diyor ki: Yüklenmemiş olay hiç ortaya çıkmayabilir. Ayrıca, kullanıcı temayı değiştirdiğinde birden fazla kez tetiklenebilir.
Dudu

IDisposable arabirimini kullanıcı kontrolünüzde uygulayabilirsiniz, ancak üçüncü tarafınızın Dispose kalıbı uygulamanızın dispose yöntemini çağıracağının garantisi yoktur. Yerel kaynakları (örneğin bir dosya akışı) tutuyorsanız, bir sonlandırıcı kullanmayı düşünmelisiniz.
Philippe

Yanıtlar:


57

İlginç blog yazısı burada:

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

Kaynaklarınızı elden çıkarmak için Dispatcher.ShutdownStarted'a abone olmaktan bahsediyor .


1
Bundan daha temiz bir yol olacağını umuyordum ama şimdilik bunu yapmanın en iyisi bu gibi görünüyor.
Mark Heath

35
Peki ya UserControl uygulama ölmeden önce ölürse? Dispatcher yalnızca uygulama yaptığında utanacak, değil mi?
Robert Jeppesen

15
Çünkü birçok denetim COM bileşenlerini veya süresiz olarak asılı bırakılmak amacıyla kodlanmamış veya bir iş parçacığı havuzu iş parçacığında sonlandırılmış ve deterministik serbest bırakma bekler / gerektirecek şekilde kodlanmamış diğer yönetilmeyen kaynakları yeniden kullanır.
Neutrino

1
Bir Windows Mağazası Uygulamasında, ShutdownStarted mevcut değildir.
Cœur

7
Veya olay işleyicilerine başvurmayı kaldırmanız veya bu denetimde başlatılan iş parçacıklarını durdurmanız gerekir, ...
DanW

40

Dispatcher.ShutdownStartedolay yalnızca uygulamanın sonunda tetiklenir. Tam kontrol kullanımdan çıktığında imha etme mantığını çağırmaya değer. Özellikle, uygulama çalışma zamanı sırasında denetim birçok kez kullanıldığında kaynakları serbest bırakır. Bu yüzden ioWint'in çözümü tercih edilir. İşte kod:

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

Bir Windows Mağazası Uygulamasında GetWindow () mevcut değildir.
Cœur

Bravo, en güzel cevap.
Nic

8
Cœur: Bir Windows Mağazası Uygulamasında, WPF
Alan Baljeu kullanmıyorsunuz

3
Ya daha fazla pencere varsa ve ana pencere asla kapanmazsa? Veya kontrolünüz birden çok kez yüklenen / kaldırılan sayfada barındırılıyor mu? bkz: stackoverflow.com/a/14074116/1345207
L.Trabacchin

1
Pencere çok sık kapanmıyor olabilir. Kontrol, bir liste öğesinin parçasıysa, üst penceresi kapanana kadar çoğu oluşturulacak / yok edilecektir.
KAYIP

15

Yıkıcı kullanırken dikkatli olmalısın. Bu, GC Finalizer iş parçacığında çağrılacaktır. Bazı durumlarda, serbest bıraktığınız kaynaklar üzerinde yaratıldıkları iş parçacığından farklı bir iş parçacığında serbest bırakılmaktan hoşlanmayabilir.


1
Bu uyarı için teşekkür ederim. bu aynen benim durumumdu! Uygulama: devenv.exe Çerçeve Sürümü: v4.0.30319 Açıklama: İşlenmeyen bir istisna nedeniyle işlem sonlandırıldı. İstisna Bilgisi: System.InvalidOperationException Yığını: MyControl.Finalize () adresinde benim çözümüm, kodu sonlandırıcıdan ShutdownStarted
itsho

10

WPF UserControls'e bir boşaltma olayı sağlamak için aşağıdaki Etkileşim Davranışını kullanıyorum. Davranışı UserControls XAML'ye dahil edebilirsiniz. Böylece, mantığı her bir UserControl'e yerleştirmeden işlevselliğe sahip olabilirsiniz.

XAML bildirimi:

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

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

CodeBehind işleyici:

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

Davranış Kodu:

/// <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
Burada bir bayrak kaldırırım ... ya pencerenin kapanmasını başka bir şey iptal ederse (belki sizin kontrolünüzden sonra abone olunur, bu nedenle temsilcinize e.Cancelulaştığında hala yanlıştır WindowClosingHandler) ?. Kontrolünüz "kaldırılacak" ve pencere hala açık olacaktır. Bunu kesinlikle Closedolayda yaparım , Closingbirde değil .
Jcl

6

Senaryom biraz farklı, ancak amaç aynı, kullanıcı kontrolümü barındıran ana pencerenin ne zaman kapandığını / kapandığını bilmek istiyorum Görünüm (yani, kullanıcı kontrolüm) bazı işlevleri yürütmek ve temizleme gerçekleştirmek için oncloseView sunucularını çağırmalıdır. (WPF PRISM uygulamasında bir MVP modeli uyguluyoruz).

Kullanıcı kontrolünün Loaded olayında, ParentWindowClosing yöntemimi Parent windows Closing olayına bağlayabileceğimi anladım. Bu şekilde, Kullanıcı kontrolüm, Ana pencerenin ne zaman kapatıldığını anlayabilir ve buna göre hareket edebilir!


0

Boşaltmanın tümü olarak adlandırıldığını düşünüyorum ama 4.7'de çok var. Ancak, .Net'in eski sürümleriyle oynuyorsanız, bunu yükleme yönteminizde yapmayı deneyin:

e.Handled = true;

Yükleme işlenene kadar eski sürümlerin kaldırılacağını sanmıyorum. Sadece gönderiyorum çünkü başkalarının hala bu soruyu sorduğunu görüyorum ve bunun bir çözüm olarak önerildiğini görmedim. .Net'e yılda sadece birkaç kez dokunuyorum ve birkaç yıl önce bununla karşılaştım. Ancak, yükleme bitene kadar boşaltmanın çağrılmaması kadar basit olup olmadığını merak ediyorum. Benim için işe yarıyor gibi görünüyor, ancak yine yeni .Net'te, yükleme işlenmiş olarak işaretlenmemiş olsa bile her zaman kaldırmayı çağırıyor gibi görünüyor.


-3

Bir UserControl bir Destructor'a sahiptir, neden onu kullanmıyorsunuz?

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

Bu işe yaramıyor gibi görünüyor. Bu yaklaşımı sadece denedim ve asla çağrılmadı.
JasonD

9
Bu bir yıkıcı değil, bir sonlandırıcı. Her zaman bir sonlandırıcı uygularsınız ve bir çift olarak imha edersiniz, aksi takdirde sızıntı riskini alırsınız.
Mike Post

1
Ve sonlandırıcıda yalnızca yönetilmeyen nesneleri temizlemelisiniz, yönetilen nesneleri temizlememelisiniz, çünkü sonlandırıcılar GC evrelerinde belirtilmemiş sırayla çalıştırılır, bu nedenle yönetilen nesneler daha erken sonlandırılabilir ve Dispose () iş parçacığı benzeşmesine sahip olabilir.
Dudu

2
joeduffyblog.com/2005/04/08/… Bulduğum Sonlandır ve İmha Et'in en iyi açıklaması. Gerçekten okumaya değer.
dss539
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.