Я наконец нашел решение. В событии UnLoaded
я сканирую Logical/VisualTree
, если экземпляр все еще присутствует или нет.
Поскольку в wpf
нет реального механизма disposing
, я принял это решение. Я открыт для лучшего решения!
FooUserControl
public class FooUserControl : UserControl
{
private IDisposable _Subscription;
private Window _ParentWindow;
public FooUserControl()
{
Loaded += _OnLoaded;
Unloaded += _OnUnloaded;
}
private void _OnLoaded(object sender, RoutedEventArgs e)
{
// avoid multiple subscribing
Loaded -= _OnLoaded;
// add hook to parent window to dispose subscription
_ParentWindow = Window.GetWindow(this);
_ParentWindow.Closed += _ParentWindowOnClosed;
_Subscription = MyObservableInstance.Subscribe(...);
}
private void _OnUnloaded(object sender, RoutedEventArgs e)
{
// look in logical and visual tree if the control has been removed
if (_ParentWindow.FindChildByUid<NLogViewer>(Uid) == null)
{
_Dispose();
}
}
private void _ParentWindowOnClosed(object? sender, EventArgs e)
{
_Dispose();
}
private void _Dispose()
{
_Subscription?.Dispose();
}
}
DependencyObjectExtensions
public static class DependencyObjectExtensions
{
/// <summary>
/// Analyzes both visual and logical tree in order to find all elements of a given
/// type that are descendants of the <paramref name="source"/> item.
/// </summary>
/// <typeparam name="T">The type of the queried items.</typeparam>
/// <param name="source">The root element that marks the source of the search. If the
/// source is already of the requested type, it will not be included in the result.</param>
/// <param name="uid">The UID of the <see cref="UIElement"/></param>
/// <returns>All descendants of <paramref name="source"/> that match the requested type.</returns>
public static T FindChildByUid<T>(this DependencyObject source, string uid) where T : UIElement
{
if (source != null)
{
var childs = GetChildObjects(source);
foreach (DependencyObject child in childs)
{
//analyze if children match the requested type
if (child != null && child is T dependencyObject && dependencyObject.Uid.Equals(uid))
{
return dependencyObject;
}
var descendant = FindChildByUid<T>(child, uid);
if (descendant != null)
return descendant;
}
}
return null;
}
/// <summary>
/// This method is an alternative to WPF's
/// <see cref="VisualTreeHelper.GetChild"/> method, which also
/// supports content elements. Keep in mind that for content elements,
/// this method falls back to the logical tree of the element.
/// </summary>
/// <param name="parent">The item to be processed.</param>
/// <returns>The submitted item's child elements, if available.</returns>
public static IEnumerable<DependencyObject> GetChildObjects(this DependencyObject parent)
{
if (parent == null) yield break;
if (parent is ContentElement || parent is FrameworkElement)
{
//use the logical tree for content / framework elements
foreach (object obj in LogicalTreeHelper.GetChildren(parent))
{
var depObj = obj as DependencyObject;
if (depObj != null) yield return (DependencyObject) obj;
}
}
else
{
//use the visual tree per default
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
yield return VisualTreeHelper.GetChild(parent, i);
}
}
}
}