Custom Zeichenoberfläche Unterschied C# und VB

  • WPF

Es gibt 31 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Custom Zeichenoberfläche Unterschied C# und VB

    Hallo,

    ich habe aus dem Netz eine Sample Solution für eine grafische Eingabeoberfläche heruntergeladen und diese probiert von C# nach VB.net zu übersetzen.
    Quelle:

    github.com/High-Core/WPFSamples
    stackoverflow.com/questions/15…aluation/15580293#1558029

    Die Version NodeEditor von HighCore funktioniert natürlich problemlos.
    Nur die benötigten Dateien für den NodeEditor in ein neues C# Projekt kopiert funktioniert auch problemlos.
    In meiner Übersetzung nach VB kann ich jedoch keine Eingabe mit der Maus auf der Benutzeroberfläche machen. Meiner Meinung nach habe ich es gleichwertig nachprogrammiert, es wird kein Fehler ausgegeben und im Ausgabefenster werden keine Bindungsfehler angezeigt. Wenn man die

    Quellcode

    1. ObservableCollection(Of Node)
    überwacht, sieht man auch, dass trotz Mausklicks keine neuen Knoten hinzugefügt werden.
    Getestet habe ich bisher die Frameworkversion, Unterschied mit Mahapps.metro und ohne, Visual Studio 2012 und 2015.

    Zusätzlich habe ich bereits letzte Woche eine ähnliche Erfahrung gemacht, als ich noch versuchte selbst eine Zeichenoberfläche zu erstellen. Auf dem canvas kamen nie die Mausklicks an. Aus diesem Grund habe ich im Netz nach funktionierenden Lösungen geforscht (und auch gefunden) Liegt das Problem an meiner Unfähigkeit oder an VB.net?

    Viele Grüße
    SKeks
    Edit:
    Habe heute mein gezipptes VB-Projekt noch einmal gecheckt und einen Schreibfehler in MainWindow.xaml gefunden:

    XML-Quellcode

    1. <CompositeCollection x:Key="Col">
    2. <CollectionContainer Collection="{Binding DataKontext.Elements,Source={x:Reference view}}"/>
    3. <CollectionContainer Collection="{Binding DataKontext.Nodes,Source={x:Reference view}}"/>
    4. </CompositeCollection>

    Muss natürlich DataContext.Elements und DataContext.Nodes heißen
    Daran lag es aber leider nicht.
    Dateien
    • NodeEditor.zip

      (7,11 MB, 282 mal heruntergeladen, zuletzt: )

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „SKeks“ ()

    Hi!
    Hab mir das mal runter geladen und gestartet. Folgende Fehlermeldung in der Anzeige sagt, das was mit dem Binding nicht hinhaut.

    System.Windows.Data Error: 40 : BindingExpression path error: 'Datakontext' property not found on 'object' ''MainView' (Name='view')'. BindingExpression:Path=Datakontext.Nodes; DataItem='MainView' (Name='view'); target element is 'CollectionContainer' (HashCode=21581053); target property is 'Collection' (type 'IEnumerable')
    @hlghyr Er hat ja geschrieben, dass da nen Schreibfehler drin ist:

    Muss natürlich DataContext.Elements und DataContext.Nodes heißen Daran lag es aber leider nicht.


    Das erklärt auch deine Fehlermeldung. Das musst du in MainWindow.xaml manuell ändern.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Du hast da nen EndIf etwas falsch platziert. Mit dem Code funktionierts bei mir:

    VB.NET-Quellcode

    1. If vm.CreatingNewElement Then
    2. Dim node As Node = GetNodeUnderMouse()
    3. Dim element As Element = TryCast(vm.SelectedObject, Element)
    4. If node IsNot Nothing AndAlso element IsNot Nothing Then
    5. element.StartingNode = node
    6. node.IsHighlighted = True
    7. e.Handled = True
    8. Exit Sub
    9. End If
    10. End If
    11. If vm.SelectedObject IsNot Nothing Then
    12. vm.SelectedObject.IsNew = False
    13. End If
    14. vm.CreatingNewNode = False
    15. vm.CreatingNewElement = False

    Twitch Viewer Display Chat-, Zuschauer- und Statistiktool für Streamer

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „newcat“ ()

    Ja Wahnsinn. Dankeschön. Sieht man mal wie man betriebsblind wird :)

    Z
    Der Vollständigkeit halber:
    Um Elemente zeichnen muss die Methode

    Quellcode

    1. ListBox_PreviewMouseDown
    mit dem hier ersetzt werden

    VB.NET-Quellcode

    1. Private Sub ListBox_PreviewMouseDown(sender As Object, e As MouseButtonEventArgs)
    2. Dim vm As MainVM = TryCast(DataContext, MainVM)
    3. If vm.CreatingNewElement Then
    4. Dim node As Node = GetNodeUnderMouse()
    5. Dim element As Element = TryCast(vm.SelectedObject, Element)
    6. If node IsNot Nothing AndAlso element IsNot Nothing AndAlso element.StartingNode Is Nothing Then
    7. element.StartingNode = node
    8. node.IsHighlighted = True
    9. e.Handled = True
    10. Exit Sub
    11. End If
    12. End If
    13. If vm.SelectedObject IsNot Nothing Then
    14. vm.SelectedObject.IsNew = False
    15. End If
    16. vm.CreatingNewNode = False
    17. vm.CreatingNewElement = False
    18. End Sub


    sonst werden keine Verbindungen gezeichnet. Das konnte ich vorher noch nicht testen.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „SKeks“ ()

    Ich habe ein verwirrendes Verhalten festgestellt, wenn man ein Verbindungselement selektiert welches sich in einem Viewport der Listbox nicht beim Koordinatenursprung befindet.
    Erreichen kann man dies, indem die Höhe und Breite des Zeichenbereichs auf >500 gesetzt wird und dann dort zeichnet.
    Das selektierte Element rutscht immer in die rechte untere Ecke. Aus irgendeinem Grund wird das ScrollChanged-Ereignis der Scrollviewer aufgerufen.

    Eingegrenzt habe ich das Problem auf das Binding der Elemente mit der Canvas:

    VB.NET-Quellcode

    1. ' The Connector's X and Y properties are always 0 because the line coordinates are actually determined the Nodes properties.
    2. Public Overrides Property X As Double
    3. Get
    4. Return 0
    5. End Get
    6. Set(value As Double)
    7. End Set
    8. End Property
    9. Public Overrides Property Y As Double
    10. Get
    11. Return 0
    12. End Get
    13. Set(value As Double)
    14. End Set
    15. End Property


    Diese sind also 0 und das Element bezieht seine Binding-Koordinaten aus den Knotenkoordinaten. Funktioniert prächtig beim normalen Zeichnen und dem Dragging auf der Canvas. Scheinbar wird jedoch beim Selektieren auf die tatsächlichen Binding-Koordinaten der Child-Elemente der Canvas zurückgegriffen.

    Hat jemand eine Idee wie man das Verhindern kann? Warum wird beim Selektieren in einer Listbox mit Scrollviewern überhaupt das OnScrollChanged-Ereignis der Scrollviewer aufgerufen? Rein logisch gesehen kann ich ja nur selektieren was sich aktuell im Viewport der ScrollViewer befindet, es gibt also keinen Grund überhaupt etwas scrollen zu wollen.
    gibts eine neue, funktionierende Version deines Werkes?
    Bei mir öffnet sich ein Window, aber ich bin zu dumm, da iwelche Objekte in die Zellen reinzumachen - vlt. fehlt mir auch einfach die Bedienanleitung.
    Ja, und an Fehlern habich rumgebastelt, aber ich finds eiglich nicht sinnvoll, dass jeder, der das anguggen will, von neuem erstmal fett ins Debuggen einsteigen muss, wo du doch wohl eine funktionierende Anwendung hast.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Hi,

    Du hast natürlich vollkommen recht. Im Anhang ein funktionierendes Projekt, was meinen Fehler verdeutlicht.

    Eine kurze Erklärung dazu:
    Ich möchte Knoten auf die Zeichenfläche zeichnen. Funktioniert prächtig, ich kann sie verziehen, es ist alles schick. Gebunden wird an ein Canvas als Itemspanel einer Listbox über

    XML-Quellcode

    1. Canvas.Left = X
    und

    XML-Quellcode

    1. Canvas.Top = Y
    im MainViewEditor Zeile 180, 181
    Nun möchte ich weitere Formen zeichnen, die aber an die Position des Knotens gebunden sind. Wenn ich also den Knoten markiere und an eine andere Position setze, sollen die angebundenen Formen mitwandern. Die anderen Formen sollen nur selektierbar sein, aber nie die Position für einen Knoten vorgeben (also kein Draggen erlauben). Nur für die Knoten ist also freie Platzwahl vorgesehen. Alle anderen geometrischen Formen können nur auf Knoten gezeichnet/ angehängt werden, also keine freie Koordinatenwahl.

    Hierbei hat man jetzt 2 grundsätzliche Möglichkeiten:
    1. Ich setze eine Referenz im ViewModel auf den Knoten und binde über die Knotenkoordinaten an das Canvas. Die geometrischen Formen selbst werden über ein Datatemplate (MainViewEditor Zeile 107) gezeichnet und mit Margins lokal verschoben, so dass sie zum Knoten passen. Die tatsächlichen X und Y Koordinaten der Objekte sind im ViewModel also stets = 0, da ja die Knotenkoordinaten als Grundlage hergenommen werden. In dem Beispielprojekt sind dies die Dreiecke bzw. Triangles
    2. Ich setze auch hier eine Referenz auf einen zugehörigen Knoten. Im ViewModel abonniere ich allerdings das

      Quellcode

      1. PropertyChanged-Event
      des zugehörigen Knotens und berechne mir immer dann neue eigene Koordinaten zur Darstellung. Hierbei sind also die X und Y Koordinaten des ViewModels =/= 0. Gebunden wird dann an das Canvas als eigenständiges Objekt. Im zugehörigen Datatemplate (MainViewEditor Zeile 125) wird nur Form und Farbe vorgegeben. Wenn jetzt der Knoten verschoben wird (sich also seine Koordinaten ändern) bekomme ich dies mit und berechne mit dann neue Bindingkoordinaten. Im Beispielprojekt die Vierecke bzw. Rectangles.
    Es ist mir hierbei ein komisches Verhalten beim Selektieren aufgefallen:
    Wenn ich ein Dreieck selektiere, verschiebt sich mein Viewport immer in die linke obere Ecke des Canvas (Koordinatenursprung des Canvas). Wenn ich ein Viereck selektiere bleibt der ViewPort an der gleichen Stelle, also genau das verhalten, welches ich erwarte.

    Ich meine, dies hängt mit dem Binding an das Canvas zusammen, da die Dreiecke ja nicht selbst gebunden und gezeichnet werden sondern nur indirekt über die Knoten (Koordinaten = 0). Meine Vermutung lautet, das der Viewport oder Scrollviewer oder wer auch immer aber diese Koordinaten her nimmt um das Bild auf das selektierte Objekt zu zentrieren.
    Stimmt das so? Gibt es einen eleganten Weg dieses Verhalten zu verhindern statt irgendwie mit dem Holzhammer einfach neue Koordinaten für die Vierecke zu berechnen?

    Meine 2. Frage, die sich mir stellt: Codebehind ist ja ein wenig verpönt :) zumindest wenn man die einschlägigen Fachberichte liest. Gibt es eine Möglichkeit über RelayCommands die Maus-Position an das ViewModel zu übergeben? Ein Tastendruck bzw. Mausklick ist kein Problem, nur die Mausposition macht mir Sorgen, da Mouse.GetPosition() ein Ui-element erwartet, zu dem die Position relativ zurückgegeben wird. Dieses habe ich allerdings ja im ViewModel nicht. Das MVVM-Pattern gibt ja eine eindeutige Unabhängigkeit von View und ViewModel vor.
    Persönlich habe ich kein Problem das Ganze nicht so eng zu sehen und Patterns zu erweitern, aber ich lerne gerne Neues kennen und anzuwenden soweit möglich.
    Hierbei auch einen großen Dank an dich EDR: Deine vielen Tuts und Beispielprojekte helfen mir in fast allen Lebenslagen und Fragestellungen sehr weiter :)
    Dateien
    • UserInterface.zip

      (203,94 kB, 138 mal heruntergeladen, zuletzt: )

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „SKeks“ ()

    SKeks schrieb:

    Codebehind ist ja ein wenig verpönt
    Jo, da gehöre ich auch zu den Code-Behind-Verpönern.
    Hab mir auch die c#-Samples angeguckt, das läuft ja auch zumeist, aber von dem vielen CodeBehind bin ich doch sehr unbegeistert - das hätte man sicher besser modellieren können.
    Als ich mich mit https://www.vb-paradise.de/index.php/Thread/115664-Linien-Figuren-Formen/ beschäftigt hab, habich auch was gebastelt, wo man Zeichen-Figuren herum-draggen kann.

    (Ja, und grad beim Draggen komme ich zumindest auch mittm Viewmodel an Grenzen.
    Also man sagt ja, das Viewmodel soll keine ViewElemente enthalten, aber wenn ich im Viewmodel auf DragEnter, DragOver, DragDrop, MouseDown, MouseMove, Hover, ... reagieren muss, dann wird mir das iwann zu blöd, und ich hole mir sehr wohl das View-Element ins Viewmodel, weil da sind die Events viel leichter zu abonnieren.)

    Aber das nur am Rande - wie gesagt: in den Samples der Codebehind ist viel zu viel.

    So, nu gugge ich mal dein Sample... danke übrigens für die Blumen ;)
    *Push*

    ​Stehe immer noch vor diesem Problem. Das Projekt mit den Drei- und Vierecken dient nur der Verdeutlichung.
    ​Spätestens wenn man Linien zeichnen möchte, welche Knoten verbinden müsste man für die Linien ziemliche Verrenkungen anstellen um die Box, in der die Linien dargestellt werden zu berechnen und darüber zu binden.
    Es ist mir hierbei ein komisches Verhalten beim Selektieren aufgefallen:
    Wenn ich ein Dreieck selektiere, verschiebt sich mein Viewport immer in die linke obere Ecke des Canvas (Koordinatenursprung des Canvas). Wenn ich ein Viereck selektiere bleibt der ViewPort an der gleichen Stelle, also genau das verhalten, welches ich erwarte.

    Ich meine, dies hängt mit dem Binding an das Canvas zusammen, da die Dreiecke ja nicht selbst gebunden und gezeichnet werden sondern nur indirekt über die Knoten (Koordinaten = 0). Meine Vermutung lautet, das der Viewport oder Scrollviewer oder wer auch immer aber diese Koordinaten her nimmt um das Bild auf das selektierte Objekt zu zentrieren.
    Stimmt das so? Gibt es einen eleganten Weg dieses Verhalten zu verhindern statt irgendwie mit dem Holzhammer einfach neue Koordinaten für die Vierecke zu berechnen?
    Ellipse zeichnen:
    Auf Button Ellipse klicken - irgendwo auf Zeichenfläche klicken - Ellipse sollte erscheinen, ausgefüllt rot.
    Ellipse verschieben - auf Ellipse klicken - Maus gedrückt halten (Ellipse sollte nun grün sein) - Maus verschieben

    Dreieck zeichnen:
    Auf Button Dreieck klicken - auf Zeichenfläche auf eine bereits vorhandene Ellipse klicken - Dreieck sollte erscheinen leicht nach unten versetzt, beim Verschieben der Ellipse sollte Dreieck mitwandern

    Viereck zeichnen:
    Auf Button Viereck klicken - auf Zeichenfläche auf eine bereits vorhandene Ellipse klicken - Viereck sollte erscheinen genau mittig der Ellipse, beim Verschieben der Ellipse sollte Viereck mitwandern

    Dreiecke und Vierecke ohne Ellipse zeichnen geht nicht. Nachdem man etwas gezeichnet hat muss einer der Buttons geklickt werden um eine neue Form zu zeichnen.

    Markieren kann man alles einzeln; mit eben dem bereits geschildertem Problem beim Selektieren.
    Sollte so stimmen, ist aus dem Kopf, da ich das Sample gerade nicht verfügbar habe.

    Viele Grüße
    ah - endlich!

    Hast du gedacht, ich können da von selbst drauf kommen, wie man so ein 3Eck an den Kringel kriegt?

    Und dein Dragging scheint eiglich ganz ok zu funktionieren. Man muss allerdings erst den Kringel anklicksen, und Maus loslassen, dasser rot wird.
    Dann draufklicksen, dasser grün wird, und dann kann man (ohne loslassen) richtig draggen.

    Wenn du's erste mal draufklickst, und er ist noch rot, und dann ziehst du die maus, dann wechselt die Rote Farbe vom Kringel ins 3Eck, und ausserdem hopft der Scrollviewer herum.

    wie dieses komische Verhalten bewerktstelligt ist, habich aber auch noch nicht raus.
    Hallo,

    Ja, ich habe ehrlich gedacht, dass meine Erklärungen ausreichen....
    Ich glaube ich habe die Lösung nach stundenlangen googlen:

    Das Problem ist scheinbar das Bubble-Event "RequestBringIntoView" der ListBoxItems, welches von "FrameworkElement" geerbt wird. Man muss es abfangen, bevor es irgendeinen ScrollViewer erreicht. Ich habe bereits versucht es in meinem CustomScrollviewer zu verarbeiten (weil heute mittag mein Google-Fu noch noch zu schlecht war :) ), aber das hat nicht zum Ziel geführt.

    Dieser Link schlägt vor, es im Codebehind des Itemspanel auf handled = true zu setzen:
    social.msdn.microsoft.com/Foru…g-bringintoview?forum=wpf

    Werde es morgen früh mal testen (hab hier leider kein Visual Studio) und berichten.
    Dieser Post dient dazu eventuelle Helfer in die richtige Richtung zu lenken bzw. stochern im Nebel von WPF zu verhindern.

    Rückmeldung:
    Es funktioniert, man muss das Event abfangen und bearbeiten bevor es den ScrollViewer erreicht. Jetzt muss ich mir nur noch überlegen wo es geschickt ist und wie man hier gleich den Codebehind vermeiden kann. Das ändert zwar nichts am grundsätzlichen Problem für das ich eben das Binding am Canvas vermute, aber es ist zumindest mal ein funktionsfähiger Workaround. Klar ist, dass man jetzt darauf vollständig verzichten muss, wenn man ein ListboxItem von außerhalb selektiert, z.B. einer Tabelle, dass diese automatisch auf der Zeichenfläche in den Viewport gebracht wird. Kann ich erstmal mit leben.

    Wenn du's erste mal draufklickst, und er ist noch rot, und dann ziehst du die maus, dann wechselt die Rote Farbe vom Kringel ins 3Eck, und ausserdem hopft der Scrollviewer herum

    Das Verhalten hier hat aber glaube ich nichts mit meiner Programmierung zu tun sondern ist ein Verhalten des ListBox.Selectors, der das SelectItem-Event der ListBox steuert.
    Viele Grüße

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „SKeks“ ()

    Vielleicht hast du recht - vlt auch nicht.
    Jdfs. ich hab nochmal geguckt: Der Scrollviewer hopft, wenn das 3-Eck den Focus bekommt (wodurch es rot gemacht wird).
    Höchst unverständlich ist mir, warum er nicht hopft, wenn ein 4-Eck den Focus bekommt (wodurch es halbtransparent-grün gemacht wird).
    Was macht das 4-Eck richtig, was das 3-Eck falsch macht?