父级在 WPF 中已更改
本文关键字:WPF | 更新日期: 2023-09-27 18:07:10
有没有办法在 FrameworkElement 的可视父级更改时收到通知,而无需仅通过使用事件来重写 OnVisualParentChanged 方法?
这是使用"已加载"事件的想法,但这似乎并不安全。
是否有在视觉对象父级更改/更改时最终触发的事件?
在我的测试中,当父项更改时,加载卸载确实会被触发,因此我会说回复它们是安全的。但是,如前所述,只有当您挂接 OnVisualParentChanged 时,您才能访问旧的父级(当_Unloaded称为 VisualParent 时已经为空(覆盖OnVisualParentChanged非常简单,如果这是您的关注点。
void _Loaded(object sender, RoutedEventArgs e)
{
DoSmthng();
}
void _Unloaded(object sender, RoutedEventArgs e)
{
DoSmthngElse();
}
protected override void OnVisualParentChanged(DependencyObject oldParent)
{
DoSmthngOther();
// Call base class to perform standard event handling.
base.OnVisualParentChanged(oldParent);
}
有点与OnVisualParentChanged
有关。 在 FrameworkElement.cs 实现中,它确实private void TryFireInitialized()
,这反过来会调用 protected virtual void OnInitialized(EventArgs e)
,所以你可以钩住那里。
这确实取决于您需要了解哪些信息。 据我所知,唯一收到父级正在更改并能够访问旧父级的通知的地方是 OnVisualParentChanged(旧父级作为参数传入(。否则,任何可以挂合您的其他地方将只能访问新的父级,因为它已经更改。
您可能还想调查一些继承 FrameworkElement 的类,并查看它们是否公开了可以帮助您的任何其他属性或方法。
我最近遇到了一种情况,我真的需要知道任意元素何时获得其父元素(我没有创建的元素,所以我无法访问覆盖方法(。我整理了一个肮脏的黑客,虽然它可能不"安全",但确实完全有效。
令人讨厌的是,Visual
实际上确实有一个VisualAncestorChanged
事件!这完全符合我们的需求:当父母改变时被解雇。...唯一的问题是它是internal
,所以你必须进入WPF源代码才能看到它。幸运的是,通过反射,访问修饰符之类的东西变得更像建议。
这是我为与该内部事件挂钩而构建的类。该类公开一个可以正常处理的公共事件,该事件由内部事件触发触发。
''' <summary>
''' Raises an event when the visual parent of a <see cref="Visual"/> changes.
''' </summary>
Public Class VisualAncestorWatcher
Implements IDisposable
Public Sub New(Visual As Visual)
Me.Visual = Visual
Dim HandlerType = InternalVisualAncestorChangedEvent.EventHandlerType
Dim Ctor = HandlerType.GetConstructor({GetType(Object), GetType(IntPtr)})
Dim HandlerMethod = [GetType].GetMethod("InternalVisualAncestorChangedHandler", BindingFlags.NonPublic Or BindingFlags.Instance)
CreatedDelegate = Ctor.Invoke({Me, HandlerMethod.MethodHandle.GetFunctionPointer})
InternalVisualAncestorChangedEvent.GetAddMethod(True).Invoke(Visual, {CreatedDelegate})
End Sub
Public ReadOnly Property Visual As Visual
Private CreatedDelegate As [Delegate]
Private Sub InternalVisualAncestorChangedHandler(sender As Object, e As Object)
RaiseEvent VisualAncestorChanged(sender, New AncestorChangedEventArgs(e))
End Sub
Public Event VisualAncestorChanged As EventHandler(Of AncestorChangedEventArgs)
Private Shared _InternalVisualAncestorChangedEvent As Reflection.EventInfo
Public Shared ReadOnly Property InternalVisualAncestorChangedEvent As Reflection.EventInfo
Get
If _InternalVisualAncestorChangedEvent Is Nothing Then _InternalVisualAncestorChangedEvent = GetType(Visual).GetEvent("VisualAncestorChanged", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)
Return _InternalVisualAncestorChangedEvent
End Get
End Property
#Region "IDisposable"
Private disposedValue As Boolean
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
InternalVisualAncestorChangedEvent.RemoveMethod.Invoke(Visual, {CreatedDelegate})
End If
disposedValue = True
End If
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in 'Dispose(disposing As Boolean)' method
Dispose(disposing:=True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
Public Class AncestorChangedEventArgs
Public Sub New(internalArgs As Object)
Ancestor = internalArgs.Ancestor
OldParent = internalArgs.OldParent
End Sub
Public ReadOnly Property Ancestor As DependencyObject
Public ReadOnly Property OldParent As DependencyObject
End Class
现在,我不得不警告说,这段代码是不安全的,将来可能会被破坏。因为我参与的事件不是公开的,所以Microsoft没有任何要求将其保留在那里。名称可能会在技术上更改,或者整个内部实现可以更改,然后此代码将突然开始抛出错误。
另一方面,我们在这里讨论的是 WPF 的一些非常基本的部分。如果它发生了变化,我会感到惊讶,所以我愿意在我为之写这个项目时承担这个风险。