Numerowanie wierszy w RowHeader, dodawanie obiektów do ColumnHeader

Pokażę dzisiaj wam jak ponumerować wiersze, nie wpisując w nie wartości i nie dodawać dodatkowej kolumny. Można to zrobić, wyświetlając taką numerację w RowHeader:

Takie rozwiązanie prezentuje się naprawdę ładnie i nie zakrywa nam ogólnego obrazu, jest estetyczne i ładne. Dodanie checkboxów do ColumnHeader:

Sprawa wygląda trochę błaho, ale może napsuć krwi. Szczególnie element umieszczony w ColumnHeader który może nie przesuwać się razem z podglądem danych. My sobie zrobimy taki który się przesuwa. 😛

Dodamy sobie nawet listę takich obiektów, które będą wykonywały określone zadania.

 

 

Zaczniemy od prostego projektu:

 

Rodzaj elementu Nazwa elementu Ustawienia
Form Form1 Name: Form1
Text: Okręty VisualMonsters.cba.pl
Size: 451; 411
DataGridView1 DataGridView1 Name: DataGridView1
Size: 411; 348
Location: 12; 12
Anchor: Top, Left, Right, Bottom

Do DatagridView1 dodałem dziewięć kolumn. Użyłem do tego opcji Collection w Visual Studio.

 

 

 

Kiedy mamy już przygotowaną formę, musimy zastanowić się jakiego zdarzenia w Datagridview użyjemy do rysowania naszej numeracji i checkboxów kolumn. Próbowałem kilku polecanych w internecie, ale najlepsze według mnie jest „CellPainting”. Najpierw dodamy sobie coś prostego, dodamy sobie checkbox do nagłówków wierszy i wypełnimy datagridview jakimiś danymi:

Public Class Form1

    Dim CheckBoxNaglowkowWierszy As New CheckBox

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        DodajwierszeDoTabeli()
        'Inicjujemy nasz Checkbox
        CheckBoxNaglowkowWierszy.Name = "CheckBoxNaglowkowWierszy"
        CheckBoxNaglowkowWierszy.Size = New Size(18, 18)
        CheckBoxNaglowkowWierszy.Checked = False
        CheckBoxNaglowkowWierszy.CheckAlign = ContentAlignment.MiddleCenter
        CheckBoxNaglowkowWierszy.BackColor = Color.White
        CheckBoxNaglowkowWierszy.Location = New Point((DataGridView1.RowHeadersWidth - 18) / 2, 3)
        'dodajemy kontrole checkboxu do DataGridView1
        DataGridView1.Controls.Add(CheckBoxNaglowkowWierszy)
        'Dodajemy mu adres obsługiwanego zdarzenia
        AddHandler CheckBoxNaglowkowWierszy.CheckedChanged, AddressOf CheckBoxNaglowkowWierszy_zaznaczWszystkieWiersze
    End Sub

    'Dodaje elementy do naszego Datagridview
    Private Sub DodajwierszeDoTabeli()
        For i As Integer = 0 To 5
            DataGridView1.Rows.Add()
            For j As Integer = 0 To DataGridView1.ColumnCount - 1
                DataGridView1.Rows(i).Cells(j).Value = i.ToString + "_" + j.ToString
            Next
        Next
    End Sub

    'Funkcjonalność naszego Checkboxa
    Private Sub CheckBoxNaglowkowWierszy_zaznaczWszystkieWiersze()

    End Sub
End Class

Efekt jest taki jak prezentowany poniżej:

Zauważmy jeden dość duży szczegół. Nasz checkbox nie zmienia położenia wraz ze zmianą rozmiaru RowHeader. Chcielibyśmy, aby zawsze był na środku. Aby tego dokonać, musimy dodać zdarzenie CellPainting. które będzie reagowało na każdą zmianę wierszy:

Public Class Form1

    Private Sub DataGridView1_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting
        'dodajemy dynamiczną zmiane położenia naszego checkboxa
        If DataGridView1.RowHeadersWidth < 18 Then
            CheckBoxNaglowkowWierszy.Visible = False
        Else
            CheckBoxNaglowkowWierszy.Visible = True
            CheckBoxNaglowkowWierszy.Location = New Point((DataGridView1.RowHeadersWidth - 18) / 2, 3)
        End If
    End Sub

    Dim CheckBoxNaglowkowWierszy As New CheckBox

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        DodajwierszeDoTabeli()
        'Inicjujemy nasz Checkbox
        CheckBoxNaglowkowWierszy.Name = "CheckBoxNaglowkowWierszy"
        CheckBoxNaglowkowWierszy.Size = New Size(18, 18)
        CheckBoxNaglowkowWierszy.Checked = False
        CheckBoxNaglowkowWierszy.CheckAlign = ContentAlignment.MiddleCenter
        CheckBoxNaglowkowWierszy.BackColor = Color.White
        CheckBoxNaglowkowWierszy.Location = New Point((DataGridView1.RowHeadersWidth - 18) / 2, 3)
        'dodajemy kontrole checkboxu do DataGridView1
        DataGridView1.Controls.Add(CheckBoxNaglowkowWierszy)
        'Dodajemy mu adres obsługiwanego zdarzenia
        AddHandler CheckBoxNaglowkowWierszy.CheckedChanged, AddressOf CheckBoxNaglowkowWierszy_zaznaczWszystkieWiersze
    End Sub

    'Dodaje elementy do naszego Datagridview
    Private Sub DodajwierszeDoTabeli()
        For i As Integer = 0 To 5
            DataGridView1.Rows.Add()
            For j As Integer = 0 To DataGridView1.ColumnCount - 1
                DataGridView1.Rows(i).Cells(j).Value = i.ToString + "_" + j.ToString
            Next
        Next
    End Sub

    'Funkcjonalność naszego Checkboxa
    Private Sub CheckBoxNaglowkowWierszy_zaznaczWszystkieWiersze()

    End Sub

End Class

Efekt jest taki, że nasz checkbox, będzie trzymał się zawsze środka nagłówka i zniknie, gdy nagłówek będzie od niego mniejszy:

Pierwszy checkbox mamy już dodany. Wiecie już chyba, o co chodzi, teraz dodamy sobie numeracje wierszy. Posłuży nam do tego zdarzenie „RowPostPaint” które wykonywane jest, gdy zmienia się wyświetlanie wierszy:

    Private Sub DataGridView1_RowPostPaint(sender As Object, e As DataGridViewRowPostPaintEventArgs) Handles DataGridView1.RowPostPaint
        'pobiera numer wiersza (numerowany od 0)
        Dim NumerWiersza As String = (e.RowIndex + 1).ToString
        'liczy długość naszej numeracji, zapisanej wedłóg określonego Fontu
        Dim DlugoscWpisaneNumeracji As SizeF = e.Graphics.MeasureString(NumerWiersza, Me.Font)
        'Jeśli DlugoscWpisaneNumeracji jest dłuższa od naszego nagłówka wierszy wtedy zostanie on powiększony
        If DataGridView1.RowHeadersWidth < CInt((DlugoscWpisaneNumeracji.Width + 20)) Then
            DataGridView1.RowHeadersWidth = CInt((DlugoscWpisaneNumeracji.Width + 20))
        End If

        Dim b As Brush = SystemBrushes.ControlText
        'dodaje grafikę numeracji
        e.Graphics.DrawString(NumerWiersza, Me.Font, b, e.RowBounds.Location.X + 15, e.RowBounds.Location.Y + ((e.RowBounds.Height - DlugoscWpisaneNumeracji.Height) / 2))

    End Sub

Jeśli byśmy chcieli, aby nasza numeracja była wyświetlana na środku nagłówka, wystarczyło, by zmienić metodę DrawString:


        e.Graphics.DrawString(NumerWiersza, Me.Font, b, e.RowBounds.Location.X +
                              (DataGridView1.RowHeadersWidth - DlugoscWpisaneNumeracji.Width) / 2, _
                              e.RowBounds.Location.Y + ((e.RowBounds.Height - DlugoscWpisaneNumeracji.Height) / 2))

Ja preferuje opcje z lewej strony.

Został już ostatni element, dodanie checkboxów do nagłówków kolumn. Najpierw przygotujemy sobie listę checkboxów, do której będziemy się odwoływali w zdarzeniu „CellPainting”. Wracamy do Form_load:

    Dim CheckBoxNaglowkowWierszy As New CheckBox
    ' lista wszyskich checkboxach w nagłówkach kolumn
    Public listaCheckBoxow As New List(Of CheckBox)

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        DodajwierszeDoTabeli()
        'Inicjujemy nasz Checkbox
        CheckBoxNaglowkowWierszy.Name = "CheckBoxNaglowkowWierszy"
        CheckBoxNaglowkowWierszy.Size = New Size(18, 18)
        CheckBoxNaglowkowWierszy.Checked = False
        CheckBoxNaglowkowWierszy.CheckAlign = ContentAlignment.MiddleCenter
        CheckBoxNaglowkowWierszy.BackColor = Color.White
        CheckBoxNaglowkowWierszy.Location = New Point((DataGridView1.RowHeadersWidth - 18) / 2, 3)
        'dodajemy kontrole checkboxu do DataGridView1
        DataGridView1.Controls.Add(CheckBoxNaglowkowWierszy)
        'Dodajemy mu adres obsługiwanego zdarzenia
        AddHandler CheckBoxNaglowkowWierszy.CheckedChanged, AddressOf CheckBoxNaglowkowWierszy_zaznaczWszystkieWiersze

        For i As Integer = 0 To DataGridView1.ColumnCount - 1
            'dla każdego nagłówka kolumny
            'jeśli nagłówek będzie niewidoczny wtedy rect.x będzie <0
            'a rect.Width=0
            Dim rect As Rectangle = DataGridView1.GetCellDisplayRectangle(DataGridView1.Columns(i).Index, -1, True)
            rect.Y = 3
            rect.X = rect.Location.X + rect.Width - 20
            'tworzymy nowy checkbox i nadajemu mu parametry startowe
            Dim NaglowekKolumny = New CheckBox()
            NaglowekKolumny.BackColor = Color.White
            NaglowekKolumny.Name = DataGridView1.Columns(i).Name.ToString
            NaglowekKolumny.CheckAlign = ContentAlignment.MiddleCenter
            NaglowekKolumny.Size = New Size(18, 18)
            NaglowekKolumny.Checked = False
            'jeśli nie dodamy tej pętli, check boxy pojawią się w dziwnych miejscach
            'wyświetlimy checkboxy tylko dla widocznych kolumn
            If rect.Width = DataGridView1.Columns(i).Width Then
                NaglowekKolumny.Location = rect.Location
            Else
                NaglowekKolumny.Visible = False
            End If
            'dodajemy wszystkie checkboxy do datagridview
            'będziemy zmieniać tylko ich położenie i opcje Visible
            DataGridView1.Controls.Add(NaglowekKolumny)
            'dodajemy mu jeszcze jakąś fonkcjonalność, aby nie było, że nic nie robią
            AddHandler NaglowekKolumny.CheckedChanged, AddressOf CheckBoxCheckChange
            listaCheckBoxow.Add(NaglowekKolumny)
        Next
    End Sub

Doda nam to pierwsze widoczne elementy:

Chwilo martwe elementy, ożywimy przy użyciu „CellPainting”. Tworząc pętlę, musimy wziąć pod uwagę pewne okoliczności. Ponieważ dla niewidocznych nagłówków ich wielkość będzie równa zero, ale jeśli są odrobinę widoczne, wtedy ich wielkość może być różna. Problem polega na tym, że trudno odróżnić który element jest z lewej strony, a który z prawej. Dlatego dodamy sobie zmienną boolean, która będzie nam to określać, będzie nam wychwytywać zmniejszony element z lewej strony. Dla tego zmniejszonego elementu po lewej stronie będziemy ustawiać lokalizację dynamicznie. Na potrzeby tutoriala dodałem im kolory, czerwony i żółty:

    Private Sub DataGridView1_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting
        'dodajemy dynamiczną zmiane położenia naszego checkboxa
        If DataGridView1.RowHeadersWidth < 18 Then
            CheckBoxNaglowkowWierszy.Visible = False
        Else
            CheckBoxNaglowkowWierszy.Visible = True
            CheckBoxNaglowkowWierszy.Location = New Point((DataGridView1.RowHeadersWidth - 18) / 2, 3)
        End If

        'wychwytuje zmniejszony element po lewej stronie
        Dim pierwszyElement As Boolean = True
        'pętla ustawiająca lokalizację checkboxów
        For i As Integer = 0 To listaCheckBoxow.Count - 1
            'nasz nagłówek
            Dim rect As Rectangle = DataGridView1.GetCellDisplayRectangle(DataGridView1.Columns(i).Index, -1, True)
            'lokalizacja checkboxa
            Dim Pt As New Point
            Pt.Y = 3 'ustawienie z góry
            'pętla sprawdza czy nagłówki są widoczne w całości (zmniejszone o wielkość checkboxu)
            If rect.Width >= DataGridView1.Columns(i).Width - 20 Then
                If rect.Location.X > 20 Then
                    listaCheckBoxow(i).Visible = True
                    listaCheckBoxow(i).BackColor = Color.Red
                    If pierwszyElement = True Then
                        Pt.X = rect.Location.X + rect.Width - 20
                    Else
                        Pt.X = rect.Location.X + DataGridView1.Columns(i).Width - 20
                    End If
                    pierwszyElement = False
                Else
                    listaCheckBoxow(i).Visible = False
                End If
            Else
                If rect.Location.X > 20 Then
                    If pierwszyElement = True Then
                        If rect.Location.X > 0 Then
                            'określa czy pierwszy nagłówek jest większy od wielkości Checkboxa 
                            '(czy się zmieści)
                            If rect.Width > 20 Then
                                listaCheckBoxow(i).Visible = True
                                listaCheckBoxow(i).BackColor = Color.Yellow
                                Pt.X = rect.Location.X + rect.Width - 20
                                pierwszyElement = False
                            Else
                                listaCheckBoxow(i).Visible = False
                            End If
                        End If
                    Else
                        listaCheckBoxow(i).Visible = False
                    End If
                Else
                    listaCheckBoxow(i).Visible = False
                End If
            End If
            'ustawia lokalizację obiektów
            listaCheckBoxow(i).Location = Pt
        Next
    End Sub

 

Efekt jest chyba zadowalający:

Można teraz zmienić im kolor tła na biały, będą działały szybciej (nie polecam ustawiać ich tła na transparent). Dodamy sobie tylko funkcjonalność, aby nie było, że są bezużyteczne. Pierwszy checkbox (ten w nagłówku wierszy) będzie zaznaczał wszystkie komórki, te w nagłówkach tylko te komórki w kolumnach:

    'Funkcjonalność naszego Checkboxa
    Private Sub CheckBoxNaglowkowWierszy_zaznaczWszystkieWiersze()
        If CheckBoxNaglowkowWierszy.Checked = True Then
            For Each dgRow As DataGridViewRow In DataGridView1.Rows
                dgRow.Selected = True
            Next
        Else
            For Each dgRow As DataGridViewRow In DataGridView1.Rows
                dgRow.Selected = False
            Next
        End If
    End Sub

    'Funkcjonalność checkboxów w nagłówkach kolumn
    Private Sub CheckBoxCheckChange()
        For i As Integer = 0 To listaCheckBoxow.Count - 1
            If listaCheckBoxow(i).Checked = True Then
                For j As Integer = 0 To DataGridView1.ColumnCount - 1
                    If DataGridView1.Columns(j).Name = listaCheckBoxow(i).Name Then
                        For k As Integer = 0 To DataGridView1.Rows.Count - 1
                            DataGridView1.Rows(k).Cells(DataGridView1.Columns(j).Index).Selected = True
                        Next

                    End If
                Next
            Else
                For j As Integer = 0 To DataGridView1.ColumnCount - 1
                    If DataGridView1.Columns(j).Name = listaCheckBoxow(i).Name Then
                        For k As Integer = 0 To DataGridView1.Rows.Count - 1
                            DataGridView1.Rows(k).Cells(DataGridView1.Columns(j).Index).Selected = False
                        Next
                    End If
                Next
            End If
        Next
    End Sub

Efekt:

Pełen projekt do pobrania tutaj: RowHeader_ColumnHeader_objects_visualmonsters

Sam kod do pobrania tutaj: RowHeader_ColumnHeader_objects_visualmonsters

 

 

Permalink do tego artykułu: https://visualmonsters.cba.pl/numerowanie-dodawanie-columnheader/

Dodaj komentarz

Twój adres email nie będzie publikowany.