Whew! Thanks dex. Actually I wasn't going to do anything drastic.
I put the background on first to give it the lowest Z order and then pass over it in the hit testing. Here is the complete code for the sake of public documentation; it draw some scary ghost:
Imports System.Windows
Imports System.Windows.Media
Imports System.Collections.Generic
Imports System.Windows.Input
Namespace WindowHostingVisual
Class Application
<STAThread()> _
Public Shared Sub Main()
Dim whv As New WindowHostingVisual.WindowHostingVisual()
whv.ShowDialog()
End Sub
End Class
Namespace WindowHostingVisual
Public Class WindowHostingVisual
Inherits Window
Private visuals As New List(Of Visual)()
Public Sub New()
Title = "Hosting DrawingVisuals"
Width = 400
Height = 400
Background = Brushes.Aquamarine
Me.WindowStartupLocation = Windows.WindowStartupLocation.CenterScreen
Dim bodyVisual As New DrawingVisual()
Dim eyesVisual As New DrawingVisual()
Dim mouthVisual As New DrawingVisual()
Dim visBackground As New DrawingVisual
Using dc As DrawingContext = bodyVisual.RenderOpen()
dc.DrawGeometry(Brushes.Blue, Nothing, Geometry.Parse("M 240,250 C 200,375 200,250 175,200 C 100,400 100,250 100,200 C 0,350 0,250 30,130 C 75,0 100,0 150,0 C 200,0 250,0 250,150 Z"))
End Using
Using dc As DrawingContext = eyesVisual.RenderOpen()
dc.DrawEllipse(Brushes.Black, New Pen(Brushes.White, 10), New Point(95, 95), 15, 15)
dc.DrawEllipse(Brushes.Black, New Pen(Brushes.White, 10), New Point(170, 105), 15, 15)
End Using
Using dc As DrawingContext = mouthVisual.RenderOpen()
Dim p As New Pen(Brushes.Black, 10)
p.StartLineCap = PenLineCap.Round
p.EndLineCap = PenLineCap.Round
dc.DrawLine(p, New Point(75, 160), New Point(175, 150))
End Using
Using dc As DrawingContext = visBackground.RenderOpen
dc.DrawRectangle(Background, Nothing, New Rect(0, 0, Width, Height))
End Using
visuals.Add(visBackground)
visuals.Add(bodyVisual)
visuals.Add(eyesVisual)
visuals.Add(mouthVisual)
' Bookkeeping:
For Each v As Visual In visuals
AddVisualChild(v)
AddLogicalChild(v)
Next
End Sub
' The two necessary overrides, implemented for the single Visual:
Protected Overrides ReadOnly Property VisualChildrenCount() As Integer
Get
Return visuals.Count
End Get
End Property
Protected Overrides Function GetVisualChild(index As Integer) As Visual
If index < 0 OrElse index >= visuals.Count Then
Throw New ArgumentOutOfRangeException("index")
End If
Return visuals(index)
End Function
Protected Overrides Sub OnMouseLeftButtonDown(e As MouseButtonEventArgs)
' Rotate object clicked on.
MyBase.OnMouseLeftButtonDown(e)
' Retrieve the mouse pointer location relative to the Window
Dim location As Point = e.GetPosition(Me)
' Perform visual hit testing
Dim result As HitTestResult = VisualTreeHelper.HitTest(Me, location)
' If we hit any DrawingVisual, rotate it
If result.VisualHit.[GetType]() = GetType(DrawingVisual) Then
Dim dv As DrawingVisual = TryCast(result.VisualHit, DrawingVisual)
' Don't rotate background.
If dv.Equals(visuals.Item(0)) Then
Exit Sub
End If
If dv.Transform Is Nothing Then
dv.Transform = New RotateTransform()
End If
TryCast(dv.Transform, RotateTransform).Angle += 1
End If
End Sub
End Class
End Namespace
End Namespace
I think my real question is what I am really drawing on. It seems to not be the window object. I don't know what the DrawingContext actually is and where it fits in. I do now know the drawn objects can be accessed through LogicalTreeHelper and VisualTreeHelper. To quote from WPF Unleashed: "However, you can easily traverse both the logical and visual trees using the somewhat
symmetrical System.Windows.LogicalTreeHelper and System.Windows.Media.
VisualTreeHelper classes."