Dziś odcinek o klasach Visuals. Czym się one różnią od omówionych już klas Gemoetry oraz jak ich możemy użyć. Visuals są trochę dziwnym tworem w WPF, rezydującym na znacznie niższym poziomie niż większość elementów, z którymi się stykami. Trzeba się zatem mocno napracować aby coś z tej klasy wydobyć. Napiszmy więc kawałek kodu:

DrawingGroup group = FindResource(“drawingObject”) as DrawingGroup;

DrawingVisual visual = new DrawingVisual();

using(var context = visual.RenderOpen())

{

    context.DrawDrawing(group);

}

a w XAMLu dodatkowo

<Window.Resources>

    <DrawingGroup x:Key=”drawingObject”>

        <GeometryDrawing Brush=”Pink” Geometry=”M0 0S 50,50 100,0 l10,30″>

            <GeometryDrawing.Pen>

                <Pen Thickness=”2″ Brush=”Black” DashStyle=”{x:Static DashStyles.DashDot}” DashCap=”Round” />

            GeometryDrawing.Pen>

        GeometryDrawing>

    DrawingGroup>

Window.Resources>

gdy jednak uruchomimy program zobaczymy puste okienko. Niestety nie ma tak prosto. Czego zatem nam brakuje? Po pierwsze musimy zarejestrować nasz obiekt w drzewie logicznym i wizualnym.

AddVisualChild(visual);

AddLogicalChild(visual);

Jednak to nadal nie wszystko 🙁

protected override int VisualChildrenCount

{

    get

    {

        return 1;

    }

}

 

protected override Visual GetVisualChild(int index)

{

    if (index!=0)

        throw new ArgumentOutOfRangeException();

    return visual;

}

A na ekranie ukaże się nam taki oto widok.

Jak zatem tego używać w sensowny sposób i czy jest jakiś zysk z tej klasy?

Zwykle robi się to tak, że tę klasę wykorzystuje się w oddzielnym elemencie typu UIElement i to w nim przeciąża się te dwie metody odpowiednio. Czy jest z tego zatem jakiś zysk? odpowiedź oczywiście jest twierdząca. Dzięki temu, że klasa ta jest bardzo nisko w hierarchii unika się całego narzutu związanego z tworzeniem “ciężkich” elementów Drawing. Klasa DrawingContext wspiera multum metod do rysowania bezpośrednio na niej, podobnie jak znany z WinForms obiekt Graphics. Co zatem jeszcze za wykonać?

HitTesting

Jak to sprawdzić? Wystarczy użyć pomocniczej klasy VisualTreeHelper.

protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)

{

    base.OnMouseLeftButtonDown(e);

 

    Point point = e.GetPosition(null);

    HitTestResult result = VisualTreeHelper.HitTest(visual, point);

    if(result.VisualHit.GetType() == typeof(DrawingVisual))

    {

        //do sth.

    }

}

W przypadku gdy mamy leżące na sobie elementy i wszystkie powinny reagować na kliknięcia należy posłużyć się trochę inną techniką.

protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)

{

    base.OnMouseLeftButtonDown(e);

 

    Point point = e.GetPosition(null);           

    VisualTreeHelper.HitTest(this, null, HitTestCallback, new PointHitTestParameters(point));

}

 

private HitTestResultBehavior HitTestCallback(HitTestResult result)

{

    if (result.VisualHit.GetType() == typeof(DrawingVisual))

    {

        DrawingVisual drawingVisual = result.VisualHit as DrawingVisual;

    }

    return HitTestResultBehavior.Continue;

}

Możemy oczywiście przerwać dalsze poszukiwania zwracając z metody HitTestResultBehavior.Stop

Następny odcinek będzie o Shapes.