I am trying to create a custom panel that utilizes absolute positionioning but will re-arrange items so that none of the children overlap. This panel will be used for the ItemsPanel of an ItemsControl. The ItemsControl is bound to data that is displayed as an inset on a map for various cities so the goal is to keep the insets as close to their cities without overlapping any other insets. I have an algorithm to rearrange items (although it's slow and I'm interested in finding a better one so if anyone has suggestions, please let me know) but I've been really struggling with the custom panel.
The latest code can be found below minus the algorithm to keep it shorter. I've also attached an image of what the visual tree looks like under overlappanel.
Problems:
1.) The first issue I ran into is that despite MSDN saying that MeasureOverride should return a height and width of positive infinity if it wants to take up the full space available, I get an error when I try to do so. As a result of this, I'm having an issue where some of my child items are getting clipped because the ItemsPresenter that contains the OverlapPanel is only sizing to the returned MeasureOverride size and clipping some of my insets because after ArrangeOverride, they are moved around and the final size is larger.
2.) I tried using VisualTreeHelper.FindElementInHostCoordinates using the Canvas.Left and Canvas.Top properties of each "ItemsControl" a few levels underneath the "ContentPresenter" of each inset to find other overlapping items within the OverlapPanel subtree. The reason I had to drill down to the ItemsControl is because it's the first item with valid canvas.top and left properties as well as DesiredSize.Width and DesiredSize.Height. However, FindElementInHostCoordinates was returning 0 items no matter what I seemed to try. I even tried using TransformToVisual to translate my coordinates to various levels before using it and I still couldn't get any expected results.
3.) Since I'm using the OverlapPanel as an ItemsPanel for an ItemsControl, I have to drill down into the visual tree a couple levels to get an actual left/top value and width/height to calculate positioning but this wouldn't allow the panel to be used as a generic panel. How could I make this more useable as a whole?
Here's the code I am currently working with. Note that "PreArrange" is what ends up calling "Arrange" on each child element, and sets its left/top properties. I left the code for it out for brevity.:
Protected Overrides Function MeasureOverride(ByVal availableSize As System.Windows.Size) As System.Windows.Size
'Return MyBase.MeasureOverride(availableSize)
Dim infinite As New Size(Double.PositiveInfinity, Double.PositiveInfinity)
Dim maxX, maxY As Double
maxX = 0
maxY = 0
For Each elem As FrameworkElement In Children
elem.Measure(infinite)
Dim ic As ItemsControl = CType(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(elem, 0), 0), ItemsControl)
If (elem.GetValue(Canvas.LeftProperty) + ic.DesiredSize.Width) > maxX Then
maxX = elem.GetValue(Canvas.LeftProperty) + ic.DesiredSize.Width
End If
If (elem.GetValue(Canvas.TopProperty) + ic.DesiredSize.Height) > maxY Then
maxY = elem.GetValue(Canvas.TopProperty) + ic.DesiredSize.Height
End If
Next
Return New Size(maxX, maxY)
End Function
Protected Overrides Function ArrangeOverride(ByVal finalSize As System.Windows.Size) As System.Windows.Size
If Children.Count = 0 Then
Return MyBase.ArrangeOverride(finalSize) 'New Size(Width, Height)
End If
Dim maxX As Double = 0
Dim minX As Double = 0
Dim maxY As Double = 0
Dim minY As Double = 0
For i As Integer = 0 To Children.Count - 1
PreArrange(Children(i), 5, 1, 10)
Dim ic As ItemsControl = CType(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(Children(i), 0), 0), ItemsControl)
Dim x1 As Double = Children(i).GetValue(Canvas.LeftProperty)
Dim x2 As Double = ic.DesiredSize.Width + x1
Dim y1 As Double = Children(i).GetValue(Canvas.TopProperty)
Dim y2 As Double = ic.DesiredSize.Height + y1
If x1 < minX Then minX = x1
If x2 > maxX Then maxX = x2
If y1 < minY Then minY = y1
If y2 > maxY Then maxY = y2
Next
Return New Size(Math.Abs(minX) + maxX, Math.Abs(minY) + maxY)
'Return MyBase.ArrangeOverride(finalSize)
End Function
-
Check out the DragDockPanel here:
http://mightymeaty.members.winisp.net/blacklight.silverlight/
0 comments:
Post a Comment