Eigene Linien in Grid Panel zeichnen
Das Grid ist das Mächtigste und am Häufigsten gebrauchte Panel in WPF.
Mit der Eigenschaft „ShowGridLines“ lassen sich Hilfslinien anzeigen.
<Grid ShowGridLines="True">

Diese Linien dienen nur als Hilfestellung für den Entwickler, der so die Position der Spalten und Reihen erkennen kann.
Leider kann man nicht selber bestimmen wie diese Linien aussehen. Oder etwas doch?
Was passiert eigentlich im Grid wenn ShowGridLines=True gesetzt wird?
WPF-Controls können sich selbst zeichnen, in dem sie die OnRender Methode überschreiben. Wie dies auch bei OnPaint in WinForms der Fall ist.
Doch beim Grid wird davon nicht Gebrauch gemacht um die Linien zu zeichnen! Stattdessen wird zu den logischen UI-Elementen in dem Panel ein zusätzliches visuelles Element der „GridLinesRenderer“ hinzugefügt. Dies geschieht nicht per ControlTemplate. (Templates ändern ebenfalls das Aussehen also den VisualTree), sonder programmatisch durch den Aufruf von AddVisualChild(visual) in der Basisklasse.
Der GridLinesRenderer ist von DrawingVisual abgeleitet. UIElement und DrawingVisual haben also den Selben Vorfahr „Visual“. DrawingVisuals sind jedoch wesentlichen performanter als UIElements, da sie direkt auf den „Bildschrim“ (WPF-Subsystem) zeichnen.
Der VisualTree im XamlPad zeigt, dass neben dem logischen Child – einem Button - noch ein visuelles Element - der GridLinesRenderer - in der Hierarchie zu finden ist.

Möglichkeit 1: Einen eigenen GridLinesRenderer schreiben
Meine erste Idee war es einen eigenen GridLinesRenderer zu schreiben. Ganz nach dem Vorbild des in dem Grid zu findenden Renderer.
Das Problem ist, dass es nicht möglich ist den Renderer in den VisualTree des Grid einzufügen. Die Methode die dieses möglich machen würde – AddVisualChild – ist nämlich protected.
Ein eigenes Grid zu erstellen, nur um die GridLinien zeichnen zu können war mir dann aber zu aufwändig.
Es muss einen anderen Weg geben.
Möglichkeit 2: Einen GridLinesAdorner schreiben
Wenn es keine Möglichkeit gibt ein UIElement von Innen zu verändern ohne ein eigenes Control abzuleiten, so muss man es eben von Außen probieren. Das machen sog. Adorner.
Adorner „verzieren“ ein UIElement. Wie, das bestimmt der Adorner. Wichtig ist, dass ein Adorner immer über einem UIElement gezeichnet wird. D.h. der Adorner verdeckt darunter liegende Zeichnungen.
Public Class GridLinesAdorner
Inherits Adorner
Private Grid As System.Windows.Controls.Grid
'Übergabe des zu verzierenden Elements (hier Grid)
Sub New(ByVal grid As System.Windows.Controls.Grid)
MyBase.New(grid)
Me.Grid = grid
End Sub
Private _pen As Pen
'Stift um die Linien zeichnen zu können
Public Property Pen() As Pen
Get
Return _pen
End Get
Set(ByVal value As Pen)
_pen = value
End Set
End Property
'Zeichnen der Linien
Protected Overrides Sub OnRender(ByVal dc As System.Windows.Media.DrawingContext)
If Pen Is Nothing Then Exit Sub
If Grid.ColumnDefinitions.Count > 0 Then
Dim x As Double
For i As Integer = 1 To Grid.ColumnDefinitions.Count - 1
x = Grid.ColumnDefinitions(i).Offset
dc.DrawLine(Pen, New Point(x, 0), New Point(x, Grid.ActualHeight))
Next
End If
If grid.RowDefinitions.Count > 0 Then
Dim y As Double
For i As Integer = 1 To Grid.RowDefinitions.Count - 1
y = Grid.RowDefinitions(i).Offset
dc.DrawLine(Pen, New Point(0, y), New Point(Grid.ActualWidth, y))
Next
End If
End Sub
End Class
Die Methode OnRender führt nun das Zeichnen der Gridlinien durch. Über die Row- und ColumnDefinitions des Grid, kann der Offset der einzelnen Spalten vom Grid abgerufen werden. Diese dienen uns als Koordination um an diesen Stellen die entsprechenden Linien zu zeichnen.
Anwenden des Adorners
Um den Adorner zum Einsatz zu bringen, müssen wir nun noch die Code-Behind Datei anpassen. Leider ist es nicht möglich den Adorner in XAML direkt anzuwenden. (Die Klasse AdornerDecorator ist nur dafür da einen Layer für ein zu verzierendes Element bereitzustellen, worauf ein Adorner angewendet werden kann; aber nicht für den Adorner selbst.)
Class Window1
Private Sub Window1_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
'Layer für das Element holen in dem der Adorner gezeichnet wird
Dim layer As AdornerLayer = AdornerLayer.GetAdornerLayer(Me.Grid)
'Adorner mit Stift erstellen
Dim adorner As New GridLinesAdorner(Grid)
adorner.Pen = New Pen(Brushes.Black, 2)
'Adorner dem Layer hinzufügen
layer.Add(adorner)
End Sub
End Class
Das Ergebnis

Wer noch einen Rahmen um das Grid herum haben möchte, der brauch nur noch das Grid in ein Border zu packen.
Achtung: Wie man sieht wird der Adorner über das Grid gezeichnet. Daher sind die Ränder der im Grid befindlichen Buttons nicht ganz zu sehen.
Hier muss dann noch ein entsprechendes Margin auf die Buttons angewendet werden um den richtigen Abstand zu den Gridinnenseiten zu ermöglichen.