父级在 WPF 中已更改

本文关键字:WPF | 更新日期: 2023-09-27 18:07:10

有没有办法在 FrameworkElement 的可视父级更改时收到通知,而无需仅通过使用事件来重写 OnVisualParentChanged 方法?

这是使用"已加载"事件的想法,但这似乎并不安全。

是否有在视觉对象父级更改/更改时最终触发的事件?

父级在 WPF 中已更改

在我的测试中,当父项更改时,加载卸载确实会被触发,因此我会说回复它们是安全的。但是,如前所述,只有当您挂接 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 的一些非常基本的部分。如果它发生了变化,我会感到惊讶,所以我愿意在我为之写这个项目时承担这个风险。

相关文章:
  • 没有找到相关文章