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

