在设置依赖项属性之前调用OnVisualChildrenChanged

时间:2014-03-23 15:17:54

标签: wpf xaml custom-controls dependency-properties

我创建了一个自定义控件(CartesianCanvas),它可以在添加任何子元素时存储其中的一些属性。为此,我创建了一个新的集合属性(ItemsInfo)并覆盖了OnVisualChildrenChanged,以便在添加或删除子项时,从集合属性中添加或删除相应的属性。

但是,当我通过XAML向控件添加子项时,OnVisualChildrenChanged将在设置属性之前调用它们,因为所有属性都具有其默认值。在窗口加载后添加子项时不是这种情况。如何在调用OnVisualChildrenChanged时确保已设置子属性?

这是我的代码:

Public ReadOnly Property ItemsInfo As Collection(Of CartesianInfo)
    Get
        Return DirectCast(GetValue(ItemsInfoProperty), Collection(Of CartesianInfo))
    End Get
End Property

Friend Shared ReadOnly ItemsInfoKey As System.Windows.DependencyPropertyKey = System.Windows.DependencyProperty.RegisterReadOnly("ItemsInfo", GetType(Collection(Of CartesianInfo)), GetType(CartesianCanvas), New System.Windows.PropertyMetadata(New Collection(Of CartesianInfo)))
Public Shared ReadOnly ItemsInfoProperty As DependencyProperty = ItemsInfoKey.DependencyProperty

Protected Overrides Sub OnVisualChildrenChanged(visualAdded As DependencyObject, visualRemoved As DependencyObject)

        Try
            If Not visualAdded Is Nothing Then
                If Not GetType(FrameworkElement).IsAssignableFrom(visualAdded.GetType) Then
                    Me.Children.Remove(visualAdded)
                    Throw New Exception("The object added:" & visualAdded.ToString & " was not of type or decended from: FrameworkElement and so was removed from CartesianCanvas")
                Else
                    AddItemInfo(visualAdded)
                End If
            End If
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try


        If Not visualRemoved Is Nothing Then
            RemoveItemInfo(visualRemoved)
        End If
        MyBase.OnVisualChildrenChanged(visualAdded, visualRemoved)

    End Sub

Private Sub AddItemInfo(ByRef item As FrameworkElement)
    Dim itemsInfoCollection As New Collection(Of CartesianInfo)
    itemsInfoCollection = ItemsInfo

    Dim ItemXCoordinate As Double
    Dim ItemYCoordinate As Double
    Dim ItemCalculateFromVerticalCenter As Boolean
    Dim ItemCalculateFromHorizontalCenter As Boolean

    If Double.IsNaN(Canvas.GetLeft(item)) Then
        Canvas.SetLeft(item, 0)
    End If

    If Double.IsNaN(Canvas.GetTop(item)) Then
        Canvas.SetTop(item, 0)
    End If

    ItemXCoordinate = Canvas.GetLeft(item)
    ItemYCoordinate = Canvas.GetTop(item)

    If item.VerticalAlignment = Windows.VerticalAlignment.Center Then
        ItemCalculateFromVerticalCenter = True
    End If
    If item.HorizontalAlignment = Windows.HorizontalAlignment.Center Then
        ItemCalculateFromHorizontalCenter = True
    End If

    ItemsInfo.Add(New CartesianInfo(item, ItemXCoordinate, ItemYCoordinate, ItemCalculateFromVerticalCenter, ItemCalculateFromHorizontalCenter))

    SetValue(ItemsInfoKey, ItemsInfo)

    PositionChild(item)
End Sub

这是我的XAML

<local:CartesianCanvas x:Name="MainCanvas">           
    <Button Width="50" Height="100" Canvas.Top="100" Canvas.Left="20"
            Content="test" Click="Button_Click"/>
</local:CartesianCanvas>

1 个答案:

答案 0 :(得分:1)

WPF非常复杂,特别是在编写自定义控件时,因此我的第一个建议是 - 保留它。将现有的控件或几个已存在的控件复合到UserControl中,而不是从头开始编写所有内容。

WPF中的控件可能包含任何其他控件,并且仅在需要时才加载控件的子控件。这就是你遇到问题的原因。

解决方案是当OnVisualChildrenChanged事件触发时,您应该遍历所有子节点并订阅其Initialized或Loaded事件。加载子项后,将触发事件并调用处理程序。此外,在处理程序中,您不应该忘记取消订阅活动。