WPF oder WinForms CustomControl?

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 26 Antworten in diesem Thema. Der letzte Beitrag () ist von Joshi.

    WPF oder WinForms CustomControl?

    Hallo,

    ich möchte Buttons oder Panels oder allgemein ein Control in einer beliebigen Form darstellen. Jetz habe ich geschaut es gibt als mögliche Ansätze WPF und UserControls. WPF gibts denke ich einiges zu lernen. Und UserControls sind bestimmt auch kein kleines Thema. Ich kenne beides noch nicht und werde wahrscheinlich woanders anfangen müssen. Daher die Frage welche dieser Optionen besser für mein Vorhaben geeignet ist.

    Das Vorhaben:
    Gegeben sind ein Set an Koordinaten (Dimension in Pixel), wobei eine Koordinate = (0,0). Und gegebenenfalls Subsets an Koordinaten (Bezug zu (0,0) aus dem Set).
    Gesucht ist ein Button/Panel/Ähnliches (Position auf dem Form egal), dessen Ecken durch das Set der Koordinaten bestimmt sind. Der Button soll "Löcher" haben können, welche jeweils durch ein Subset an Koordinaten festgelegt ist.
    Das Control soll seine Fläche kennen / berechnen können

    Das ist sicher nichts leichtes, daher hoffe ich mich direkt auf den richtigen Weg zu machen und vor allem einen sinnvollen.

    Viele Grüße

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

    Bei WinForms weiss ich, wie man Löcher in einen Button kriegt - bei Wpf nicht - aber geht sicher auch.
    Ich hab allerdings Zweifel bezüglich der Löcher: Also in Wpf kann man Löcher machen, und dann sieht man hindurch, welche Anwendung drunter ist.
    Man kann sogar durch die Löcher hindurch auf ddie andere Anwendung klickssen.
    Könnte mirr gut vorrstellen, das ist nicht die Art Löcher, die du dir vorstellst.
    Also musste die Anforderung "Löcher" wohl noch genauer definieren.
    Doch hört sich genau nach der Art Löcher an, die ich mir vorgestellt habe.

    Loch im Control:
    Also eine weiteres Polygon, das in irgendeiner Weise dem Hauptpolygon(Control) untergeordnet ist und quasi von der entstehenden Fläche des Hauptpolygons abgezogen wird.
    Ein "Unterunterpolygon" soll es nicht geben.
    Das "Loch" soll nicht zum Control gehören, nicht geklickt werden können und der Flächeninhalt des Lochs soll nicht ein Teil des Flächeninhalts des Controls sein.

    Vielleicht habe ich aber auch etwas nicht bedacht an das du denkst?

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    @Haudruferzappeltnoch Du kannst ja mal ein wenig mit diesem Snippet rumspielen, überall, wo die .BackColor gleich dem .TransparencyKey der Form ist, wird die Form durchsichtig:

    VB.NET-Quellcode

    1. ' transparente Form2 über Form1
    2. Public Class Form2
    3. Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4. Me.TransparencyKey = Color.BlanchedAlmond
    5. Me.BackColor = Color.BlanchedAlmond
    6. End Sub
    7. End Class

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Danke auf jeden Fall.

    Ich suche eigentlich noch nicht nach konkreten Umsetzungen. Nur nach dem Weg den ich einschlagen sollte.
    Es soll mehr als ein visueller Effekt sein. Es soll schon wirklich ein Loch da sein. Gerade auch wegen der Flächenberechnung, denke ich relevant. Sonst könnte man wohl auch einfach ein zweites Control drübersetzen.

    Wenn du @ErfinderDesRades sagst bei Winforms weiß du wies geht, meinst du damit die UserControls?
    nein, ich meine, was RFG gepostet hat - das mit dem TransparencyKey.
    Das kann man verwenden auf jedem Control.
    Wobei ich jetzt unsicher werde, weil bei vielen Löchern sollte man mit OwnerDrawing arbeiten, also die Löcher malen - in der transparencyColor.
    Und das weiss ich nicht, ob eine selbstgemalte Figur dann auch transparent wird - kannst du das mal ausprobieren?
    @Haudruferzappeltnoch Du kannst z.B. eine Bitmap transparent machen: Bitmap.MakeTransparent().
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Haudruferzappeltnoch schrieb:

    Bevor ich ausprobiere muss ich wohl mit den Grundlagen starten, wie man überhaupt einen eigenen Button baut.
    nein, einen Button musst du nicht bauen.
    einfach eine Klasse machen, mit INherits Control, und schon hast du dein eigenes Control.

    Aber um das mit dem OwnerDrawing zu probieren brauchst du auch kein eigenes Control.
    einfach Form1.Paint abonnieren, und darin irgendetwas malen, mit der Transparencycolor.

    ps: eine transparente Bitmap brauchst du höchstwahrscheinlich auch nicht.




    habs jetzt selbst probiert - folgender bahnbrechender Code:

    VB.NET-Quellcode

    1. 'in frmLochImForm, ein Form mit TransparencyKey=Color.Red
    2. Private Sub frmLochImForm_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
    3. e.Graphics.FillEllipse(Brushes.Red, 10, 10, 100, 200)
    4. End Sub
    sieht nett aus.

    Nachdem du diese "Hürde" genommen hast, weisst du auch, wie man selbstgemalte Löcher ins Form kriegt.
    Dann gehts an die Grundlagen, wie man komplexe Figuren zeichnet, auch Multi-Löcher, und wie man das mit Daten-Klassen verknüpfen kann.
    es Gibt ein grosses OwnerDrawing-Tut im Tutbereich.




    hey - da hat sich was geändert in Win10: durchs Loch durchklicken geht nicht mehr.
    evtl. muss man iwas mit einem Api-Style drehen, in CreateParams() - aber dazu braucht man dann doch ein eigenes Control.

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

    Ah das meintest du. Ja ich wollte ja kein Loch im Form sondern im Control, das ich aufs Form packe, eben ein Panel oder ein Button. Deswegen hab ich das geschrieben mit den Grundlagen.

    Ich habe ein paar Tuts gefunden auf myCsharp, eins war auch von dir (oder zumindest einem Nutzer mit demselben Namen^^)
    Also was zeichnen konnte ich schon, aber beim Versuch einen Button draus zu machen scheitert es. Schlimmer noch ich zerschieße mir auch den Form-Designer wenn ich das Programm ausführe.
    Ich mache dann eine Änderung in der Designer.vb, mache diese aber direkt rückgängig und dann is der Designer wieder heile.

    Ich habe im Designer auf dem Form ein Panel, auf dem Panel ein UserControl, Im UserControl-Designer nichts außer dem UC selbst
    Form

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private WithEvents uc1 As UCTest
    3. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    4. uc1 = New UCTest(New List(Of Point) From {New Point(0, 0), New Point(0.3, 1), New Point(1.1, 0.7), New Point(2, 0)})
    5. uc1.CreateOwnShape()
    6. End Sub
    7. Private Sub Panel1_Paint(sender As Object, e As PaintEventArgs) Handles Panel1.Paint
    8. uc1.DrawAt(e.Graphics, 200, 200)
    9. End Sub
    10. Private Sub uc1_Click(sender As Object, e As EventArgs) Handles uc1.Click
    11. MessageBox.Show("")
    12. End Sub
    13. End Class


    UserControl

    VB.NET-Quellcode

    1. Public Class UCTest
    2. Private ownShape As Boolean
    3. Property Fläche As Single
    4. Property CornerPoints As List(Of Point)
    5. Property mygo As MyGraphicsObject
    6. Friend Function HasShapeSource()
    7. If CornerPoints.Count > 0 Then ownShape = True
    8. Return ownShape
    9. End Function
    10. Friend Sub New()
    11. InitializeComponent()
    12. End Sub
    13. Friend Sub New(lst As List(Of Point))
    14. InitializeComponent()
    15. CornerPoints = lst
    16. End Sub
    17. Friend Sub CreateOwnShape()
    18. mygo = New MyGraphicsObject(CornerPoints)
    19. End Sub
    20. Friend Sub DrawAt(g As Graphics, X As Integer, Y As Integer)
    21. Place(X, Y)
    22. g.DrawPath(mygo.myPen, mygo.Path)
    23. End Sub
    24. Friend Sub Place(x As Integer, y As Integer)
    25. Dim mat As New Matrix
    26. mat.Translate(x, y)
    27. mat.Scale(50, 50)
    28. mygo.Path.Transform(mat)
    29. End Sub
    30. End Class


    MyGraphicsObject

    VB.NET-Quellcode

    1. Public Class MyGraphicsObject
    2. Private points2polygon As Boolean
    3. Friend Property Coords As List(Of Point)
    4. Friend Property Path As GraphicsPath
    5. Friend Property myPen As Pen
    6. Friend Sub New()
    7. myPen = New Pen(Color.Black, 1)
    8. Path = New GraphicsPath
    9. End Sub
    10. Friend Sub New(lst As List(Of Point))
    11. myPen = New Pen(Color.Black, 1)
    12. Path = New GraphicsPath
    13. Coords = lst
    14. ConstructPolygon()
    15. End Sub
    16. Friend Sub ConstructPolygon()
    17. If Coords.Count > 1 Then
    18. For i = 0 To Coords.Count - 1
    19. If i > 1 AndAlso i + 1 = Coords.Count Then
    20. Path.AddLine(Coords(i), Coords(0))
    21. Else
    22. Path.AddLine(Coords(i), Coords(i + 1))
    23. End If
    24. Next
    25. points2polygon = True
    26. Else
    27. points2polygon = False
    28. End If
    29. End Sub
    30. Friend Function HasPolygon() As Boolean
    31. Return points2polygon
    32. End Function
    33. End Class


    Das UC ist nicht wirklich da, das ist nur n Bild.

    PS: Der Designer zerschießt jetzt nicht mehr, Wunderheilung Er zerschießt nun doch wieder, also alles wacklig

    Außerdem bekomme ich ein unterschiedliches Ergebnis wenn ich alle Werte in den Points mal 10 rechne und die Werte in mat.Scale durch 10 rechne. Ich vermute mal halbe Pixel sind für Points nicht zulässig, selbst wenn man es später aufbläst?

    Dieser Beitrag wurde bereits 8 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    @Haudruferzappeltnoch Wann wolltest Du anfangen, mit Option Strict On zu programmieren?
    Was soll wann passieren?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Haudruferzappeltnoch schrieb:

    Ich vermute mal halbe Pixel sind für Points nicht zulässig, selbst wenn man es später aufbläst?


    Du kannst z.B. einfach float nehmen und beim callen der Sub zum Render aus den floats Integers machen. Alternativ kannste Bibliotheken verwenden, kann sich soagr lohnen, denn GDI ist nicht sehr schnell, oft reicht das aber aus. Wenn du nun noch feiner malen willst, kannste einen SDL2-Wrapper für net nehmen, komplett mit floats arbeiten. Dafür hat SDL2 z.B. SDL_FRect oder SDL_FPoint.

    Haudruferzappeltnoch schrieb:

    Ich vermute mal halbe Pixel sind für Points nicht zulässig, selbst wenn man es später aufbläst?
    Nimm zunächst PointF.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Haudruferzappeltnoch schrieb:

    Ja ich wollte ja kein Loch im Form sondern im Control, das ich aufs Form packe, eben ein Panel oder ein Button.
    Da sind wir wieder bei der Frage, was du unter Loch verstehst.
    Wenn du ein Control auffm Form hast, auf welchem du in der TransparencyColor zeichnest - dann wird auch diese Zeichnung zum Loch.
    Und zwar zum Loch durchs ganze Form.

    Zu die Tuts: warum nimmst du dir nicht das von vbparadise vor? das ist um 10 Jahre moderner und ausserdem vb.net statt c#.
    Deine Snippets sehen mir auch nicht ansatzweise aus wie von mir.
    Ich wüsste nicht, dass ich je ein MyControl geschrieben hätte, oder mir mit CreatePolygon einen abgebrochen hätte.
    Auch die Matrix-Klasse benutze ich immer viel effizienter (die ist übrigens Disposable).
    Naja in dem Tut wurde nur ein Rectangle benutzt und ein Control wird da nicht selbst gebastelt, ich dachte ich versuch erstmal was, mit dem was ich aus dem Tut mitgenommen hab. Daran kann man meist einfacher sehen was zu tun ist. Zur Matrix gabs kein spezielles Tut, da wurde nur einmal mat.Translate benutzt. Das warn ja auch 3 verschiedene Tuts

    Durchs Ganze Form möchte ich kein Loch. Wie soll man dadrauf auch kommen das Loch nur ganz oder gar nicht geht?
    Naja dann kann ich die Stelle vielleicht einfach überdecken mit einem weiteren Control.

    Ich schau nochmal ins hiesige Tut.

    Ok Option Strict On lässt einen das mit den Integers sehen. Muss immer dran denken das bei nem neuen Projekt reinzuschreiben. Gerade weil ich strict on normalerweise nutze guckt man nicht mehr überall hin.
    Passieren soll erstmal nur die Erzeugung eines Control mit den Eckpunkten, die ich definiert habe.

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Moin.

    Wie wäre es mit der Eigenschaft OpacityMask?

    Damit wird eine Geomtrie abgeschitten, und diese kann zur "Ausstanzung" genutzt werden.

    Dieser Geometrie dann irgendeine Füllfarbe (Leer und Transparent geht nicht).

    In Kombination kann dann jegliche 2D-Form/Geometrie in WPF ausgestanzt oder gegreenscreened* werden.

    XML-Quellcode

    1. <Button Content="Slippin Jim"
    2. x:Name="The_Slippin_Jim_Button"
    3. Width="400"
    4. Height="200"
    5. Background="Yellow">
    6. <Button.OpacityMask>
    7. <DrawingBrush>
    8. <DrawingBrush.Drawing>
    9. <GeometryDrawing>
    10. <GeometryDrawing.Brush>
    11. <SolidColorBrush Color="Black" />
    12. </GeometryDrawing.Brush>
    13. <GeometryDrawing.Geometry>
    14. <GeometryGroup>
    15. <RectangleGeometry Rect="0,0 400,200" />
    16. <EllipseGeometry
    17. Center="75,75"
    18. RadiusX="50"
    19. RadiusY="50" />
    20. <EllipseGeometry
    21. Center="325,125"
    22. RadiusX="50"
    23. RadiusY="50" />
    24. </GeometryGroup>
    25. </GeometryDrawing.Geometry>
    26. </GeometryDrawing>
    27. </DrawingBrush.Drawing>
    28. </DrawingBrush>
    29. </Button.OpacityMask>
    30. </Button>




    Natürlich ist zu beachten, das bei meinem Beispiel, nicht umbedingt, auf ein Ballspiel abgezielt wurde...

    * gegreenscreened <- Copyright und Katsching-Moenymaking-Rights by Joshi ;)

    Haudruferzappeltnoch schrieb:

    Durchs Ganze Form möchte ich kein Loch. Wie soll man dadrauf auch kommen das Loch nur ganz oder gar nicht geht?
    Naja dann kann ich die Stelle vielleicht einfach überdecken mit einem weiteren Control.
    ich tät vorschlagen, dass du den Code aus post#9 mal probierst - ehe hier ellenlang über was gesprochen wird, was am Ende garnet brauchbar ist.
    Du kannst zusätzlich auch noch ein Label aufs Form legen, mit BackColor.Red - dann ist dieses Label ebenfalls ein Loch im Form.

    Wie gesagt: Unter "Loch" kann man verschiedenerlei verstehen, ich hab eine Version von "Loch" geliefert, wo ich allerdings schon von Anfang an (s.Post#2) unsicher war, ob das deinen Anforderungen entspricht.
    Definiere "Loch" also genauer, nachdem du gesehen hast, was WinForms unter "Loch" versteht.



    Ein anderer Ansatz ist mit Regions - damit kann man einem Control eine beliebige Form zuweisen - auch Löcher reinmachen. Dann hat nur das Control die Löcher, und darunter sieht man (und klickt man auf) das Panel, wo es drauf aufsitzt.
    Oder eben du lernst Wpf, bzw die WinForms-Wpf-Hybridisierung.
    Kenne ich mich garnet mit aus, fürchte aber, das mit dem Hybriden sieht anfangs leichter aus als Wpf richtig lernen, aber bewirkt langfristig dass du Wpf nie richtig lernst.
    Aber wie gesagt: k.A. - solltest dir unbedingt berufeneren Rat einholen.

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

    Ja genau darum gings mir ja, um Rat. Ich weiß ja auch nicht wie umfangreich Wpf im Vergleich ist.
    Ich habe deinen Code probiert, aber da habe ich noch nicht verstanden wie du das meintest.

    Hab das Tutorial angeschaut, da gehts nur ums Zeichnen selbst und auch viel, dass der Benutzer etwas kreieren kann.
    Was mir fehlt ist das Verständnis einem Control zu sagen du sollst jetzt so aussehen.

    Im Moment mache ich eine Zeichnung, aber die ist halt nur da, die kann nix und die sieht auch nicht aus wie ein Button


    Die graue Box ist ein UserControl. Dem würd ich gern eine andere Form hauen. So wie das Gepinsel daneben aussieht z.B.
    Aber wie man dem UC das sagen kann, kommt in keinem Tut vor.
    Einfach sache. Ist die "Box" wirklich ein UserControl oder doch ein CustomControl?
    Rein präventativ:
    Ein UserCOntrol legt man an indem man ein neues Element dem Projekt hinzufügt(Dieser Dialog, dort UserControl oder auf deutsch BenutzerSteuerElement auswählen) , darauf platziert man dann wie auf einem Form Controls. Kann man dann nachdem man einmal kompilert hat ausser toolbox so auf Forms ziehen(das UC).
    Ein CustomControl ist streng genommen nur eine Klasse die von Control(System.Windows.Forms.Control) erbt.

    Hier ist so ein Klasse:

    VB.NET-Quellcode

    1. Imports System.Drawing.Drawing2D
    2. Public Class CustomControl ' kannste nennen wie du grad lust hast
    3. Inherits Control
    4. Sub New()
    5. Width = 400
    6. Height = 400
    7. Using gp As New GraphicsPath()
    8. gp.AddLine(0, 0, Width, Height \ 2)
    9. gp.AddLine(Width, Height \ 2, 0, Height)
    10. gp.CloseFigure() 'verbindet hier den ersten und letzten punkt, so entsteht hier ein dreieck
    11. Region = New Region(gp)
    12. End Using
    13. End Sub
    14. Protected Overrides Sub OnPaint(e As PaintEventArgs)
    15. MyBase.OnPaint(e)
    16. e.Graphics.FillRectangle(Brushes.Blue, ClientRectangle)
    17. End Sub
    18. End Class


    Füge das mal einem TestProjekt hinzu, kompiliere einmal, ab in den Designer, ToolBox auf und zieh "CustomControl" aufs Form. Das findest du ganz oben inner ToolBox
    Im Konstruktor sieht du auch gleich die Antwort auf deine Frage mit geometrischen Controls. Jedes Control hat die Region Property(auch Forms, sind ja auch Controls)

    Wobei das setzen der Region pixelig aussehen kann, hat der Parent, also das wo das Control drauf ist eine gleichmäßige Hintergrundfarbe, also kein Image oder irgendeine Art von GradientBrush, ist es optisch schöner die gleiche Farbe beim CustomControl für' Hintergund zu nehmen und passend zu malen, mit Antialising sieht das dann besser aus als wie die Region so zu setzen.

    Noch eine Anmerkung, ich weiss nicht ob es im aktuellen Studio noch so ist, bei x64 und CustomControls gab es Probleme mit dem Designer, notfalls die Controls in eine AnyCPU Dll packen, und Verweis drauf setzen, dann kommt auch der Designer wenn für x64 gearbeitet wird auch klar. Da ich schon lange kaum noch NET verwende kann ich nicht sagen was diesbezüglich Stand der Dinge ist.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Takafusa“ ()

    Ich denke, das, womit du arbeiten wollen würdest, ist - wie oben beschrieben - die GeometryGroup. Mit Hilfe dieser lassen sich Polygone zeichnen.

    Und als Hilfsmittel dazu hat die GeometryGroup noch eine Eigenschaft "FillRule", der du Werte zuweisen kannst. Und da gibt es verschiedene Techniken. Eine sorgt dafür, dass zwei sich überlappende Figuren gegenseitig neutralisieren. Schau mal folgenden Link und scroll weiter runter, bis du zu einer Figur kommst, die türkis gezeichnet ist, dann siehst du, was ich meine: programmer.group/wpf-draws-geometry.html