サードパーティが使用するためのカスタムWPFユーザーコントロールを作成しました。私のコントロールには使い捨てのプライベートメンバーがあり、そのウィンドウ/アプリケーションが閉じられると、そのdisposeメソッドが常に呼び出されるようにします。ただし、UserControlは使い捨てではありません。 IDisposableインターフェイスを実装して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;
ここの興味深いブログ投稿:
http://geekswithblogs.net/cskardon/archive/2008/06/23/dispose-of-a-wpf-usercontrol-ish.aspx
リソースを破棄するために Dispatcher.ShutdownStarted にサブスクライブすることに言及しています。
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
};
}
デストラクタの使用には注意が必要です。これは、GC Finalizerスレッドで呼び出されます。場合によっては、解放したリソースが、作成されたスレッドとは異なるスレッドでリリースされることを好まない場合があります。
次の対話型動作を使用して、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);
}
}
私のシナリオは少し異なりますが、ビュー(つまり、私のユーザーコントロール)がプレゼンターを呼び出していくつかの機能を実行し、クリーンアップを実行する必要があるため、意図は同じです(まあ、WPF PRISMアプリケーションでMVPパターンを実装しています)。
ユーザーコントロールのLoadedイベントで、ParentWindowClosingメソッドをParent windows Closingイベントにフックできることを理解しました。このようにして、ユーザーコントロールは、親ウィンドウが閉じられていることを認識し、それに応じて動作することができます!
私はアンロードがすべてと呼ばれていると考えていますが、4.7では完全に存在します。ただし、.Netの古いバージョンで遊んでいる場合は、ロード方法でこれを試してください。
e.Handled = true;
ロードが処理されるまで、古いバージョンがアンロードされるとは思わない。他の人がまだこの質問をしているのを見て、これを解決策として提案していないので、投稿するだけです。私は.Netに年に数回しか触れておらず、数年前にこれに出くわしました。しかし、ロードが完了するまでアンロードが呼び出されないのと同じくらい簡単なのだろうか。それは私にとってはうまくいくようですが、再び新しい.Netでは、ロードが処理済みとしてマークされていなくても常にアンロードを呼び出すようです。