127

我创建了一个供第三方使用的自定义 WPF 用户控件。我的控件有一个一次性的私有成员,我想确保一旦包含的窗口/应用程序关闭,它的 dispose 方法将始终被调用。但是,UserControl 不是一次性的。

我尝试实现 IDisposable 接口并订阅 Unloaded 事件,但在主机应用程序关闭时都没有被调用。MSDN 说可能根本不会引发 Unloaded 事件。它也可能被触发不止一次,即当用户更改主题时。

如果可能的话,我不想依赖我控制的消费者记住调用特定的 Dispose 方法。

 public partial class MyWpfControl : UserControl
 {
     SomeDisposableObject x;

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

     }
 }

到目前为止,我发现的唯一解决方案是订阅 Dispatcher 的 ShutdownStarted 事件。这是一个合理的方法吗?

this.Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted;
4

7 回答 7

60

此处有趣的博客文章:Dispose of a WPF UserControl (ish)

它提到订阅Dispatcher.ShutdownStarted以处置您的资源。

于 2009-02-02T10:24:33.610 回答
45

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
     };
}
于 2012-12-18T09:57:54.350 回答
15

你必须小心使用析构函数。这将在 GC Finalizer 线程上调用。在某些情况下,您释放的资源可能不喜欢在与创建它们的线程不同的线程上释放。

于 2009-07-07T22:31:22.007 回答
9

我使用以下交互行为向 WPF 用户控件提供卸载事件。您可以在 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);
    }
}
于 2014-10-22T09:07:03.900 回答
6

我的场景略有不同,但意图是相同的 (我们正在 WPF PRISM 应用程序上实现 MVP 模式)。

我只是想在用户控件的 Loaded 事件中,我可以将 ParentWindowClosing 方法连接到 Parent windows Closing 事件。这样我的用户控件就可以知道父窗口何时关闭并采取相应的行动!

于 2011-08-03T20:34:33.830 回答
-1

我在想卸载在 4.7 中几乎不存在。但是,如果您正在使用旧版本的 .Net,请尝试在您的加载方法中执行此操作:

e.Handled = true;

我认为在处理加载之前不会卸载旧版本。之所以发帖,是因为我看到其他人仍在问这个问题,并且还没有将其视为解决方案。我一年只接触过几次.Net,几年前就遇到过这种情况。但是,我想知道它是否像在加载完成之前不调用卸载一样简单。似乎它对我有用,但在较新的 .Net 中,即使加载未标记为已处理,它似乎总是调用 unload。

于 2018-05-31T20:04:02.237 回答
-3

UserControl 有一个析构函数,你为什么不使用它呢?

~MyWpfControl()
    {
        // Dispose of any Disposable items here
    }
于 2009-02-02T12:58:38.543 回答