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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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:
1 |
1 2 3 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
'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