DatagridView grafic Cells mitZahl überlagern

  • VB.NET
  • .NET 4.5

SSL ist deaktiviert! Aktivieren Sie SSL für diese Sitzung, um eine sichere Verbindung herzustellen.

Es gibt 37 Antworten in diesem Thema. Der letzte Beitrag () ist von Peter329.

    DatagridView grafic Cells mitZahl überlagern

    Hi,

    ich hab ein Problem, das ich prinzipiell lösen kann. Leider sind meine Lösungswege extrem aufwändig. Vielleicht habt ihr Ideen, wie man das eleganter lösen kann.

    Reversi. Ich habe eine Spielmatrix als DatagridView abgebildet. Die Zellen enthalten einfache Grafiken. Nun möchte ich, in die Zellen, die für mögliche Züge infrage kommen, die Anzahl der invertierbaren Zellen einstellen, also eine Zahl.

    Ich hab das mal so realisiert, dass ich über die Zellen jeweils ein Label gelegt habe. (s. Anhang)

    Das wäre schon eine Lösung meines Problems. Aber ich müsste dann 60 Label definieren (4 Felder der 8x8 Matrix sind fest belegt) ... und ich habe keinen Index, um die Label zu adressieren ! Keine gute Lösung.

    Alternativ könnte ich Grafiken erstellen, die den Zellenhintergrund und die gewünschte Zahl beinhalten. Die jeweilige Grafik könnte ich zuweisen.

    VB.NET-Quellcode

    1. If BoardUpdates(i, j) = 1 Then dgvBoard.Item(j, i).Value = Grafik01


    Da habe ich dann zwar für die Zelle einen Index. Aber die Grafik mit der jeweiligen Zahl ist nicht indiziert. Und da bei einem Reversi Zug bis zu 18 invertierte Zellen auftreten (wie man leicht mathematisch beweist) ist das auch kein besonders elegante Lösung. Weil ich 18 Grafiken (mit den Zahlen 1 - 18) erstellen und ohne Index zuweisen müsste. Die Lösung gefällt mir auch nicht.

    Also, wie kann ich eine Grafik Zelle mit einer Zahl überlagern ? Kann ich in eine Grafik eine Zahl einblenden ? Ich wüsste nicht wie das gehen sollte. Kann man Label dynamisch erstellen ? Wie verwalte ich die dann ohne Index ? Aber vielleicht wisst ihr ja mehr.

    Ich hoffe, ich habe mein Problem verständlich darstellen können.

    LG
    Peter
    Bilder
    • s 2017-12-17 13-22-564.jpg

      25,42 kB, 493×490, 25 mal angesehen

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

    Peter329 schrieb:

    Reversi. Ich habe eine Spielmatrix als DatagridView abgebildet. Die Zellen enthalten einfache Grafiken. Nun möchte ich, in die Zellen, die für mögliche Züge infrage kommen, die Anzahl der invertierbaren Zellen einstellen, also eine Zahl.

    Ich hab das mal so realisiert, dass ich über die Zellen jeweils ein Label gelegt habe. (s. Anhang)
    Mir scheint, da versucht jmd ein Spiel zu programmieren, ohne ein Datenmodell dafür zu konzipieren.
    Aber erfreulich, dass du selbst merkst, wie es an allen Ecken und Enden anfängt zu murksen und zu ächzen.

    Also Vorschlag: schaff dir ein Datenmodell. Mindestens eine Klasse ReversiCell, die die Info beinhaltet, ob und von wem besetzt, und mw auch deine komische Zahl, deren Sinn ich noch nicht verstanden hab.
    Und dann ein 2d-jagged Array von derlei ReversiCells - das ist dann dein Spielbrett.
    Vielleicht auch noch eine richtige Spielbrett-Klasse drumherum - bestimmt musses Methoden geben, die herausfinden, bei welchem Zug welche Zellen zu drehen und so Zeugs.
    Jut.
    Und dieses Spielbrett zeigste im DGV an. Und zwar im _CellPainting malst du hin, was hinmuss, um den Zustand der entsprechenden ReversiCell deines Datenmodells adäquat abzubilden.
    Also keine DgvCell "hat eine einfache Grafik drin", und keine Labels werden über irgendwas gelegt.

    Wenn das ReversiCell-Objekt einen Zustand hat, der als Grafik anzuzeigen ist, dann wird diese Grafik im _CellPainting gepaintet. Und wenn da ein Nümmerken zu painten ist, dann ebenso.
    Erst mal vielen Dank für die Antworten, die ja irgendwie in die gleiche Richtung gehen.

    NoIde schrieb:

    Aber es gibt auch ein DataGridView.CellPainting-Event da kannst du die Cell selbst malen.


    Das würde ich gern versuchen umzusetzen. Wenn ich das Ereignis CellPaint abonniere, dann habe ich unter e. eine ganze Reihe von Methoden ... unter anderem die Paint Methode.

    Und wie kann ich damit jetzt eine Zahl in die Zelle hineinmalen ?

    ErfinderDesRades schrieb:

    auch deine komische Zahl, deren Sinn ich noch nicht verstanden hab.


    Die Zahl gibt an, wie viele gegnerische Steine umgedreht werden, wenn dieses Feld besetzt wird. Als einfache Strategie könnte man immer eines der Felder besetzen, wo diese Zahl am größten ist (obwohl das vermutlich nicht die BESTE Strategie ist).

    LG
    Peter
    Bilder
    • s 2017-12-17 15-32-577.jpg

      32,18 kB, 1.007×298, 20 mal angesehen

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

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private dgv As New DataGridView
    3. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Shown
    4. Controls.Add(dgv)
    5. dgv.Dock = DockStyle.Fill
    6. dgv.Columns.Add("col1", "col1")
    7. dgv.Rows.Add()
    8. AddHandler dgv.CellPainting, AddressOf dgv_Cellpainting
    9. End Sub
    10. Private Sub dgv_Cellpainting(ByVal sender As Object, ByVal e As DataGridViewCellPaintingEventArgs)
    11. If e.ColumnIndex >= 0 AndAlso e.RowIndex >= 0 Then
    12. e.Handled = True
    13. e.Graphics.FillEllipse(Brushes.Black, e.CellBounds)
    14. e.Graphics.DrawString("1", Font, Brushes.White, e.CellBounds.X + 20, e.CellBounds.Y)
    15. End If
    16. End Sub
    17. End Class
    Ok, erst mal vielen Dank für dein Code Beispiel. Das hab ich jetzt versucht umzusetzen. Meine ursprungliche Lösung habe ich nach rechts verschoben ... links steht der neue Lösungsansatz:

    VB.NET-Quellcode

    1. Private Sub frmReversi_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    2. Controls.Add(dgv)
    3. Dim myLocation As New Drawing.Point(50, 100)
    4. dgv.Location = myLocation
    5. dgv.Dock = DockStyle.Fill
    6. dgv.Columns.Add("col1", "col1")
    7. dgv.Columns.Add("col2", "col2")
    8. dgv.Columns.Add("col3", "col3")
    9. dgv.Columns.Add("col4", "col4")
    10. dgv.Columns.Add("col5", "col5")
    11. dgv.Columns.Add("col6", "col6")
    12. dgv.Columns.Add("col7", "col7")
    13. dgv.Columns.Add("col8", "col8")
    14. dgv.ColumnHeadersVisible = False
    15. dgv.RowHeadersVisible = False
    16. dgv.RowTemplate.Height = 60
    17. For i As Integer = 0 To 7
    18. dgv.Columns(i).Width = 60
    19. Next
    20. For i As Integer = 0 To 7
    21. dgv.Rows.Add()
    22. Next
    23. AddHandler dgv.CellPainting, AddressOf dgv_Cellpainting
    24. End Sub


    Bevor ich mir zuviel Arbeit mache: Verstehe ich das richtig, dass ich jetzt alle Dinge, die ich normalerweise im Designer eintrage, jetzt programmgesteuert im Load vereinbaren muss?

    Die 8 Spalten kann ich definieren ... wenn ich die 8 Zeilen hinzufüge erhalte ich 9 ?

    Die LOCATION Angabe kommt nicht zum Tragen.

    VB.NET-Quellcode

    1. Private Sub dgv_Cellpainting(ByVal sender As Object, ByVal e As DataGridViewCellPaintingEventArgs)
    2. If e.ColumnIndex >= 0 AndAlso e.RowIndex >= 0 Then
    3. e.Handled = True
    4. e.Graphics.FillEllipse(Brushes.Black, e.CellBounds)
    5. e.Graphics.DrawString("1", Font, Brushes.White, e.CellBounds.X + 20, e.CellBounds.Y)
    6. e.CellStyle.BackColor = Color.LightGreen
    7. End If
    8. End Sub


    Das Einfärben des Zellenhintergrundes klappt nicht.

    Fragen über Fragen ... ich hoffe, ich stelle mich nicht gar zu blöde an.

    LG
    Peter
    Bilder
    • s 2017-12-17 17-01-062.jpg

      112,26 kB, 1.149×742, 22 mal angesehen
    Ok, Dgv.ColumnCount=8 nimmt mir die Arbeit ab, die 8 Spalten von Hand zu definieren. Das ist ja schon mal etwas.

    Aber so Sachen wie ColumnHeaders.Visible = False und RowHeaders.Visible=False die muss ich ja schon von Hand einstellen?

    Und nach wie vor ist das dynamische Control weit entfernt von meiner Vorgabe !

    Wie geht das denn jetzt mit dem Einfärben des Backgrounds? Wie kann ich denn die Location festlegen ? etc. etc.

    LG
    Peter
    probiers aus - das meiste geht nachwievor im Designer - warum solls auf einmal nicht mehr gehen?

    ah - weil Nolde das dgv per code gefrickelt hat! Hatte ich übersehen.
    Jo - per code Controls erstellen soll man nur, wenns einen triftigen Grund dafür gibt.
    Sonst nicht - sondern den Designer sein Job machen lassen.

    ( Ach - der heisst ja auch so - das l ist ja ein I!
    alles klar! ;)
    /OT )

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

    Du solltest dich ein wenig mit deiner IDE vertraut machen. Das Event kannst du einfach von deiner Datagridview abbonieren und nutze. Ich hatte die Datagridview halt nur per Code erstellt anstatt im Designer. Schau mal auf dem Screenshot, im Eigenschaftenfenster(Wenn DataGridView selektiert), klicke auf den Blitz und schau dir an welche Events du du alles aboonieren kannst, da ist auch das CellPainting zu finden.

    Edit/ Musst du rechts neben dem Event in der Box doppelklicksen.
    Bilder
    • Unbenannt.png

      21,28 kB, 329×574, 19 mal angesehen

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

    Ok, dann ist dass mit dem dynamisch generierten Control also nicht so gemeint ! Ist ja schön zu wissen. :)

    Wie man die Ereignisprozeduren eines Controls anzeigt weiß ich schon. Aber die Frage ich jetzt welches Ereignis ich verwenden soll .... und was ich darin tun soll.

    Ich hab das schon (vor deinem Beitrag) wie folgt versucht:

    VB.NET-Quellcode

    1. Private Sub dgvBoard_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles dgvBoard.CellPainting
    2. If e.ColumnIndex >= 0 AndAlso e.RowIndex >= 0 Then
    3. 'Debug.Print("i=" & e.RowIndex.ToString & " j=" & e.ColumnIndex.ToString)
    4. e.Graphics.DrawString("1", Font, Brushes.White, e.CellBounds.X + 20, e.CellBounds.Y)
    5. End If
    6. End Sub


    Da tut sich aber nix. Die vermaledeite "1" wird nicht angezeigt.

    Der Debugger zeigt, dass die Routine enorm oft aufgerufen wird.

    Was mache ich falsch ?
    probierma

    VB.NET-Quellcode

    1. Private Sub dgvBoard_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles dgvBoard.CellPainting
    2. If e.ColumnIndex >= 0 AndAlso e.RowIndex >= 0 Then
    3. 'Debug.Print("i=" & e.RowIndex.ToString & " j=" & e.ColumnIndex.ToString)
    4. e.Graphics.DrawString("1", Font, Brushes.White, e.CellBounds.X + 20, e.CellBounds.Y)
    5. e.Handled = true
    6. End If
    7. End Sub
    auch mal die eventargs im ObjectBrowser angugge - hält noch einige Überraschungen bereit.

    Kannst auch mal im Tutorialbereich gucken, da habich ja so einiges mit Dgv-OwnerDrawing gebastelt
    FightingSnakes
    coloriertes DatagridView
    Icons im DatagridView und das CellFormatting-Event

    Aber wie gesagt: ohne Datenmodell wirds mühsam (ich hab den Eindruck, dass diese PaintEventerei dich davon ablenkt)
    Probier mal so:

    VB.NET-Quellcode

    1. Private Sub DataGridView_CellPainting(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting
    2. If e.ColumnIndex >= 0 AndAlso e.RowIndex >= 0 Then
    3. e.Paint(e.CellBounds, e.PaintParts And (DataGridViewPaintParts.All Xor DataGridViewPaintParts.ContentBackground Or DataGridViewPaintParts.SelectionBackground Or DataGridViewPaintParts.ContentForeground))
    4. e.Handled = True
    5. e.Graphics.FillEllipse(Brushes.Black, e.CellBounds)
    6. e.Graphics.DrawString("1", Font, Brushes.White, e.CellBounds.X + 20, e.CellBounds.Y)
    7. End If
    8. End Sub
    Na, so langsam nähern wir uns ja einer Lösung an. Aber am Ziel sind wir noch nicht.

    Ich habe jetzt versucht in eine Zeile Kreise zu malen und in die nächste eine Zahl zu schreiben und die dritte Zeile frei zu lassen.

    VB.NET-Quellcode

    1. Private Sub dgvBoard_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles dgvBoard.CellPainting
    2. If e.ColumnIndex >= 0 AndAlso e.RowIndex >= 0 Then
    3. e.Paint(e.CellBounds, e.PaintParts And
    4. (DataGridViewPaintParts.All Xor
    5. DataGridViewPaintParts.ContentBackground Or
    6. DataGridViewPaintParts.SelectionBackground Or
    7. DataGridViewPaintParts.ContentForeground))
    8. e.Handled = True
    9. Select Case e.RowIndex Mod 3
    10. Case 0
    11. e.Graphics.FillEllipse(Brushes.Black, e.CellBounds)
    12. Case 1
    13. e.Graphics.DrawString("1", Font, Brushes.White, e.CellBounds.X + 20, e.CellBounds.Y + 30)
    14. Case 2
    15. End Select
    16. End If
    17. End Sub


    Die Kreise klappen aber die Zahlen kommen nicht an.

    Außerdem ist noch offen, wie ich die Hintergrundfarbe der Zelle setze. Meine Versuche dazu sind bisher alle gescheitert.

    Das ist schon eine zähe Angelegenheit !
    Bilder
    • s 2017-12-17 21-07-513.jpg

      34 kB, 495×494, 18 mal angesehen
    verdammt ... das hab ich doch glatt übersehen ! :)

    Ok ... also dann bleibt die Sache mit der Hintergrundfarbe.

    VB.NET-Quellcode

    1. dgvBoard.Item(e.ColumnIndex, e.RowIndex).Style.BackColor = Color.LightGreen


    Das hätte ich mir beispielsweise vorgestellt. Aber es funktioniert nicht.

    Außerdem wäre es schön, wenn man die Kreise ein bissl kleiner machen könnte.

    Außerdem würde ich gern den Schriftgrad der Zahlen etwas vergrößern.

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

    Hintergrundfarbe
    Guck dir die Graphics-Klasse mal im OB an - die kann ja noch bisserl mehr als nur Kreise.

    Btw - haste die Eventargs mal angeguckt? Poste bitte Screenshot.



    Peter329 schrieb:

    Kreise ein bissl kleiner
    dann mach das Rechteck kleiner, in das sie geschrieben werden. Guck dir die Rectangle-Struktur im OB an, insbesondere Methoden, die mit I anfangen.
    [edit]

    Jetzt habe ich doch noch ein Problem:

    Wenn ich eine Zelle verändere, dann ändern sich ja auch die "eligible numbers" und damit muss ich ALLE Zellen der Datagrid View neu malen.

    Ich habe deshalb versucht die Grafics Methode AUSSERHALB des CellPainting Events aufzurufen. Aber das klappt wohl nicht, weil die Methode zu dem CellEvent e.der Cell gehört.

    VB.NET-Quellcode

    1. Graphics.FillRectangle(Brushes.LightGray, myCellBounds) 'Set grid color


    Fehler BC30469 Der Verweis auf einen nicht freigegebenen Member erfordert einen Objektverweis.

    Wie kann ich denn die Zellen bemalen, wenn ich mich nicht im Ereignis CellPaint befinde ?

    LG Peter





    ok ... jetzt hab ich mich ein bissl mit den grafischen Methoden befasst ... und siehe da, nun schaut das Display sehr manierlich aus.

    Ich stelle mal den Code ein, vielleicht nutzt es ja anderen.

    VB.NET-Quellcode

    1. If e.ColumnIndex >= 0 AndAlso e.RowIndex >= 0 Then 'Process cell
    2. Dim i As Integer = e.RowIndex
    3. Dim j As Integer = e.ColumnIndex
    4. e.Handled = True
    5. Dim drawFont As New Font("Microsoft Sans Serif", 12) 'Set font size
    6. Dim myRectangle As Rectangle = e.CellBounds 'Define size of colored cell
    7. myRectangle.X += 1
    8. myRectangle.Y += 1
    9. myRectangle.Height -= 1
    10. myRectangle.Width -= 1
    11. Dim myCircle As Rectangle = e.CellBounds 'Define size of circle
    12. myCircle.X += 5
    13. myCircle.Y += 5
    14. myCircle.Height -= 10
    15. myCircle.Width -= 10
    16. e.Graphics.FillRectangle(Brushes.LightGray, e.CellBounds) 'Set grid color
    17. e.Graphics.FillRectangle(Brushes.DarkGreen, myRectangle) 'Set background color
    18. Select Case Board(i, j) 'Setup board display
    19. Case "" 'Empty cell
    20. If BoardCount(i, j) > 0 Then 'Eligible cell
    21. Dim myText As String = BoardCount(i, j).ToString.PadLeft(2, " "c)
    22. Dim myBrushes As Brush = If(currentPlayer = Player.white, Brushes.White, Brushes.Black)
    23. e.Graphics.DrawString(myText, drawFont, myBrushes, e.CellBounds.X + 15, e.CellBounds.Y + 20)
    24. End If
    25. Case "b" 'Black cell
    26. e.Graphics.FillEllipse(Brushes.Black, myCircle)
    27. Case "w" 'White cell
    28. e.Graphics.FillEllipse(Brushes.White, myCircle)
    29. End Select
    30. End If


    Das war's ! :)

    Herzlichen Dank an alle Ratgeber, die mir (ein ums andere Mal) mit großer Nachsicht auf die Sprünge geholfen haben.

    LG
    Peter
    Bilder
    • s 2017-12-18 10-21-441.jpg

      24,86 kB, 489×492, 12 mal angesehen

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

    Hmm - ich find, ich hab garnet so viel geschrieben - aber trotzdem ist wohl nur 1/2 angekommen. Machemer halt Schritt für Schritt, erstmal dieses:

    ErfinderDesRades schrieb:

    Peter329 schrieb:
    Kreise ein bissl kleiner

    dann mach das Rechteck kleiner, in das sie geschrieben werden. Guck dir die Rectangle-Struktur im OB an, insbesondere Methoden, die mit I anfangen.


    Guck dir die Rectangle-Struktur im OB an, insbesondere Methoden, die mit I anfangen.
    Welche sind das?
    Ich hoffe mal, dass sich dein Posting auf den neuesten Stand meines Posting bezieht. Dass ich die Sache mit der Hintergrundfarbe, der Schriftgröße und der Größe des Kreises gelöst habe, hast du gelesen ?

    ErfinderDesRades schrieb:


    Guck dir die Rectangle-Struktur im OB an, insbesondere Methoden, die mit I anfangen.
    Welche sind das?


    Also wenn ich nach Rectangle im OB suche, dann erhalte ich System.Drawing.Rectangle. Das wird gleich ZWEIMAL angezeigt. Warum auch immer?

    Dort finde ich dann die Eigenschaften die ich verwendet habe: X, Y, Width und Heigth.

    Irgendwelche Methoden, die mit "I" anfangen finde ich da nicht.

    Wo sollte ich deiner Meinung nach suchen? Und wie kann das mir dabei helfen, die Zellen außerhalb des CellPainting Events neu zu malen ?

    Ich bin ja gern bereit deinen Ratschlägen zu folgen. Nur musst du mir schon ein Hinweis geben, WAS ich da genau tun sollte.