Laden...

Trigger in CodeBehind erzeugen

Erstellt von OlafSt vor 2 Jahren Letzter Beitrag vor 2 Jahren 638 Views
O
OlafSt Themenstarter:in
79 Beiträge seit 2011
vor 2 Jahren
Trigger in CodeBehind erzeugen

Hallo Freunde,

ich habe hier eine Fragestellung, die auch durch Google und StackOverflow unbeantwortet geblieben ist - womöglich habe ich aber auch falsch gefragt.

Ich habe hier eine WPF-Anwendung, die eine Landkarte zeichnet. Das ganze sieht wie folgt aus:


private void DrawElement(float[][][] data)
        {
            //Und los gehts mit Zeichnen
            List<PathFigure> li = new();

            for (int areaCount = 0; areaCount < data.Length; areaCount++)
            {
                //Startpunkt ermitteln
                Point startPoint = ScaleCoordToPoint(data[areaCount][0][0], data[areaCount][0][1]);
                PointsDrawn++;

                //PathGeometry zeichnen
                PathFigure path = new ();
                path.StartPoint = startPoint;

                for (int coordCount = 1; coordCount < data[areaCount].Length; coordCount++)
                {
                    Point pt = ScaleCoordToPoint(data[areaCount][coordCount][0], data[areaCount][coordCount][1]);
                    PointsDrawn++;
                    path.Segments.Add(new LineSegment(pt, true));
                }
                li.Add(path);
            }

            PathGeometry pg = new();

            for (int i = 0; i < li.Count; i++)
                pg.Figures.Add(li[i]);

            li.Clear();

            System.Windows.Shapes.Path p = new();
            p.Stroke = Brushes.Black;
            p.StrokeThickness = 1;
            p.Data = pg;
            TheCanvas.Children.Add(p);
        }

So weit, so gut, so funktionierts. Wenn jemand eine performantere Idee hat, bin ich aufgeschlossen - ist aber nicht die Frage.

Ich möchte nun einen OnMouseOver-Trigger (oder MouseEnter/MouseLeave) an jeden der einzelnen PathGeometry anhängen. Alle Beispiele, die ich bisher gesehen habe - inklusive hier im Forum - befassen sich mit dem Trigger innerhalb des XAML. Das ist okay, wenn man das Objekt im XAML erstellt, aber das ist hier nicht der Fall.

Doch wie mache ich das per Code-Behind ?

Bin für jeden Schubser dankbar.

5.658 Beiträge seit 2006
vor 2 Jahren

Doch wie mache ich das per Code-Behind ?

Man würde den Inhalt der Canvas im XAML an die Canvas binden. So könntest du auch Trigger definieren.
Siehe dazu [Artikel] MVVM und DataBinding

Man kann Trigger zwar auch im Code-behind definieren, aber das ist umständlich, schwer zu lesen und zu verstehen, und von WPF eigentlich auch nicht so vorgesehen. Daher wirst du auch kaum jemanden finden, der dir damit weiterhelfen könnte.

Weeks of programming can save you hours of planning

J
641 Beiträge seit 2007
vor 2 Jahren

Muss es denn ein Trigger sein oder kannst du dich auch an die Events hängen?

Dann kannst ja einfach sowas


public static IEnumerable<T> FindVisualChilds<T>(DependencyObject depObj) where T : DependencyObject
{
    if (depObj == null) yield return (T)Enumerable.Empty<T>();
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
    {
        DependencyObject ithChild = VisualTreeHelper.GetChild(depObj, i);
        if (ithChild == null) continue;
        if (ithChild is T t) yield return t;
        foreach (T childOfChild in FindVisualChilds<T>(ithChild)) yield return childOfChild;
    }
}

von https://stackoverflow.com/a/978352/579623

verwenden und alle PathGeometrys suchen

cSharp Projekte : https://github.com/jogibear9988

G
154 Beiträge seit 2015
vor 2 Jahren

Pro System.Windows.Shapes.Path oder pro PathGeometry?

Dem Path müsste man doch einen Style zuweisen können?

So in etwa


Style style = new Style(typeof(DeineKlasse));

Trigger trigger = new Trigger(){Property = DeineKlasse.IsMouseOver, Value = true/false});
Setter setter = new Setter(DeineKlasse.DasProperty, value);// statt value: new Binding(...falls vorhanden...) {Converter = new DeinConverter() }));

trigger.Setters.Add(setter);
style.Trigger.Add(trigger);

DasObjekt.Style = style;

PathGeometry hat aber kein Property für einen Style.

3.170 Beiträge seit 2006
vor 2 Jahren

Hallo,

PathGeometry hat aber kein Property für einen Style.

Richtig. Der Style kommt erst mit dem FrameworkElement, und das ist Deinem Fall eben erst der Path.
Das bedeutet, man müsste allen einzelnen PathFigures eine eigene PathGeometry und diesen wiederum einen eigenen Path verpassen, statt alles zusammen in einem Path zu zeichnen.
Ich hab es jetzt nicht ausprobiert, aber dann müsste/sollte es auch mit Style und Trigger funktionieren.

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

O
OlafSt Themenstarter:in
79 Beiträge seit 2011
vor 2 Jahren

Hallo Freunde,

ich hatte keine Gelegenheit bis gestern, da wieder bei zu gehen. Ich brauchte echt mal Abstand von C#-Code 😁

Durch den letzten Post von @MarsStein kam ich auf eine andere Idee: Das Path-Element hat einen MouseOver-, MouseEnter- und MouseLeave-Event, alle darunterliegenden (Geometry etc) haben das nicht. Anstatt also nur einen allumfassenden Path zu zeichnen, zeichne ich für jedes Element, das ein MouseOver-Event benötigt, einen eigenen Path.

Ergo schrumpft der Algorithmus auf dies hier zusammen:


private void DrawElement(float[][][] data, string KreisName = "")
        {
            //Und los gehts mit Zeichnen

            for (int areaCount = 0; areaCount < data.Length; areaCount++)
            {
                PathGeometry pg = new();
                //Startpunkt ermitteln
                Point startPoint = ScaleCoordToPoint(data[areaCount][0][0], data[areaCount][0][1]);
                PointsDrawn++;

                //PathGeometry zeichnen
                PathFigure path = new();
                path.StartPoint = startPoint;

                for (int coordCount = 1; coordCount < data[areaCount].Length; coordCount++)
                {
                    Point pt = ScaleCoordToPoint(data[areaCount][coordCount][0], data[areaCount][coordCount][1]);
                    PointsDrawn++;
                    path.Segments.Add(new LineSegment(pt, true));
                }
                pg.Figures.Add(path);

                System.Windows.Shapes.Path p = new();
                p.Stroke = Brushes.Black;
                p.StrokeThickness = 1;
                p.Data = pg;
                p.Tag = KreisName;

                //Events hier anklemmen
                p.MouseEnter += AreaEnter;
                p.MouseLeave += AreaLeave;
                TheCanvas.Children.Add(p);
            }
        }

Ich gebe einen string im Tag-Property mit, damit ich im Eventhandler erkennen kann, welches der Elemente das Event gefeuert hat.

Das fühlt sich für mich viel eher nach "Windows Forms-Style" an denn WPF, aber für dieses kleine Programm hier mag das gehen. Viel größer wird es eh nicht. So oder so, es funktioniert und ich danke für den Schubser in eine zielführende Richtung.

Äußerst interessant für mich wäre, wie man das "in reiner Lehre" in WPF wohl machen würde. Ich denke, das da konzeptionell schon anders herangegangen werden muss, ich habe aber keine blasse Idee, wie...

4.938 Beiträge seit 2008
vor 2 Jahren

MVVM entsprechend würde man die Path-Daten an das Canvas binden, und die Ereignisse dann als ICommand (evtl. mit Hilfe von AttachedCommandBehavior V2 aka ACB o.ä.).

2.079 Beiträge seit 2012
vor 2 Jahren

evtl. mit Hilfe von
>
o.ä.

Das gibt's auch von Microsoft:
https://github.com/Microsoft/XamlBehaviorsWpf