Witajcie.

Na wstępie errata dotycząca klasy z poprzedniej części:
Chodzi mianowicie o MySqlDB.cs.
W updateRow i createRow wykorzystałem nazwę pola name natomiast tabela zamiast tego posiada nazwę service. Należy zmienić błędną wartość. Poprzedni artykuł został już zaktualizowany.

W tej części naszych zmagań z aplikacją do przechowywania naszych haseł zajmiemy się obsługą DataGridView czyli następującymi tematami:

  • konfiguracja DataGridView,
  • uwzględnianie filtru nazwy,
  • dodawanie wpisów,
  • edycja wpisu,
  • usuwanie wpisów,

Konfiguracja DataGridView

Zaczynamy więc od konfiguracji naszego DataGridView nazwanego w ramach zeszłego artykułu dataGridViewPasswords. Aby dokonać edycji kolumn naciskamy prawym przyciskiem myszy na nasze dataGridView i wybieramy Edit Columns… .
Dodawanie poszczególnych kolumn następuje po wciśnięciu Add….

Edycja tabeli w Visual Studio

Edycja tabeli w Visual Studio

Moje propozycje względem jego prawidłowego zachowania i wyświetlania są następujące:

pole o wartości HeaderText = Id, Visible na False, ReadOnly = True, DataPropertyName = id, (Name) = PasswordId, ColumnType = DataGridViewTextBoxColumn, AutoSizeMode = NotSet
pole o wartości HeaderText = Serwis, Visible na True, ReadOnly = True, DataPropertyName = service, (Name) = Service, ColumnType = DataGridViewTextBoxColumn, AutoSizeMode = None, FillWeight = 200, Width = 200
pole o wartości HeaderText = Wartość, Visible na True, ReadOnly = True, DataPropertyName = value, (Name) = Value, ColumnType = DataGridViewTextBoxColumn, AutoSizeMode = None, FillWeight = 150, Width = 150
pole o wartości HeaderText = Notatka, Visible na True, ReadOnly = True, DataPropertyName = note, (Name) = Note, ColumnType = DataGridViewTextBoxColumn, AutoSizeMode = Fill, Width = 332

W kodzie (o ile wcześniej nie było zmienione zmieniamy dataGridViewPasswords.AutoGenerateColumns = true na false.
Dla spokoju warto jest sprawdzić, czy nasza aplikacja dalej wyświetla dane z tabeli czyli Debug. Jeżeli wszystko jest ok to przechodzimy do kolejnego punktu.

Uwzględnianie filtru nazwy

Mamy tabelę, więc teraz zajmiemy się filtrem.
Aby zabezpieczyć naszą aplikację przed zwiechą, musimy ustalić sztywne bariery możliwych do wpisywania znaków, które będą wpisywane w pole szukania. Moim ulubionym sposobem jest wykorzystanie zdarzenia KeyPress.
Zaznaczamy nasze pole tekstowe (jeżeli wcześniej nie zmieniliśmy zmieńmy jego wartość „Name” na textBoxServiceFilter) i przechodzimy w Properties na zdarzenia. W zdarzeniach znajdźmy event KeyPress i w puste pole po prawej stronie tzw. 2-klikiem przechodzimy na kod bezpośrednio do składni eventu.
Nasza metoda będzie wyglądała mniej więcej tak:

private void textBoxServiceFilter_KeyPress(object sender, KeyPressEventArgs e)
        {
            e.Handled = !((char.IsLetter(e.KeyChar) || char.IsNumber(e.KeyChar) || char.IsWhiteSpace(e.KeyChar)) || e.KeyChar == (char)Keys.Back);
        }

Powyższy kod ogranicza nam wpisywanie znaków do liter, numerów, spacji i backspace.

Teraz zrealizujemy naszą funkcję filtrującą:
Na pierwszy ogień pójdzie nasza metoda refreshPasswordsTable(). Musimy dodać linię, która będzie Filtrowała nasze wiersze. Do takich zadań służy Właściwość DataView zwana RowFilter.
Nasza metoda będzie wyglądała tak

private void refreshPasswordsTable()
        {
            dataGridViewPasswords.DataSource = null;
            dataGridViewPasswords.Rows.Clear();



            MySqlDB mySql = new MySqlDB();

            DataSet passwordsDataSet = mySql.readRows();
            
            DataTable passwordsDataTable = passwordsDataSet.Tables[0];

            DataView passwordsDataView = new DataView(passwordsDataTable);

            dataGridViewPasswords.AutoGenerateColumns = false;

            if (textBoxServiceFilter.Text.Length > 0)
            {
                passwordsDataView.RowFilter = "service like '%" + textBoxServiceFilter.Text + "%'";
            }

            dataGridViewPasswords.DataSource = passwordsDataView;
            
        }

Wystarczy już tylko dodać odświeżanie tabeli za każdym razem kiedy zmieni się tekst w naszym polu tekstowym.
Poszukajmy w zdarzeniach dla textBoxServiceFilter eventu TextChange, 2-klikiem przechodzimy do kodowania i wpisujemy:

private void textBoxServiceFilter_TextChanged(object sender, EventArgs e)
        {

            refreshPasswordsTable();
        }

Testujemy aplikację poprzez Debug. Jeżeli aplikacja się uruchamia i filtr działa wiedz że coś się dzieje 🙂 ale tak czy inaczej wszystko gra i przechodzimy dalej.

Teraz zrealizujemy kolejny punkt a właściwie punkty.

Dodawanie wpisów

Edycja wpisów

Aby zrealizować dodawanie i edycję musimy dodać nową formatkę.

Dodawanie i Edycja Rekordu

Dodawanie i Edycja Rekordu

Zachowujemy się tak jak wcześniej gdy dodawaliśmy nowe klasy tylko tym razem wybieramy zamiast Class – Windows Form i nazywamy go np. FormPassword.
Z Toolboxa dodajemy do niego 2 x TextBox, 1 x RichTextBox i 3 x Label oraz 2 x Button.
Dwa pola textBox nazwiemy sobie textBoxService i textBoxValue, pole richTextBox nazwiemy richTextBoxNote. W label możemy zmienić tylko pole text, tak aby dotyczyły poszczególnych pól tekstowych (patrz grafika).
Przyciski nazwijmy buttonCancel – text Anuluj i buttonSave – text Zapisz.

W kodzie dla formatki musimy przewidzieć 2 sytuacje. Pierwsza – dodawanie a druga edycja.
Dobrym sposobem na to będzie wykorzystanie argumentu, z którym będzie odpalana nasza nowa formatka. W klasie public FormPassword() w nawiasie wpisujemy int passwordId a Cały kod głównej klasy w tym momencie wygląda tak.

public partial class FormPassword : Form
    {
        public int passwordId;
        MySqlDB mySql; //ułatwiamy sobie życie, aby nie inicjować za każdym razem tej klasy.

        public FormPassword(int passwordId = -1) //w przypadku braku argumentu zostanie przypisana wartość -1
        {
            InitializeComponent();
            this.passwordId = passwordId;
            mySql = new MySqlDB();
        }
   }

Aby możliwe było edytowanie musimy jeszcze mieć możliwość wyciągania pojedynczego rekordu.
Tworzymy więc sobie metodę w klasie MySqlDB.cs jak poniżej:

public DataSet readRow(int id)
        {

            connection.Open();
            MySqlDataAdapter dataAdapterReadOne = new MySqlDataAdapter();
            MySqlCommand commandReadOne = new MySqlCommand();
            commandReadOne.Connection = connection;
            commandReadOne.CommandText = "select * from passwords WHERE id = @id ";
            commandReadOne.Parameters.Add(new MySqlParameter("id", id));

            dataAdapterReadOne.SelectCommand = commandReadOne;
            DataSet dataSetForRead = new DataSet();
            dataAdapterReadOne.Fill(dataSetForRead);

            connection.Close();
            connection.Dispose();

            return dataSetForRead;
        }

Teraz ustawmy zdarzenie Load dla naszej nowej formatki.

private void FormPassword_Load(object sender, EventArgs e)
        {
            if (passwordId > 0)
            {
               
                DataSet dataSetRow = mySql.readRow(passwordId); //inicjujemy readRow z klasy MySqlDB
                DataTable dataTableRow = dataSetRow.Tables[0]; //wczytujemy dane do tabeli
                textBoxService.Text = dataTableRow.Rows[0][1].ToString(); //przypisujemy zmienną do pola tekstowego 
                textBoxValue.Text = dataTableRow.Rows[0][2].ToString(); //przypisujemy zmienną do pola tekstowego 
                richTextboxNote.Text = dataTableRow.Rows[0][3].ToString(); //przypisujemy zmienną do bogatego :) pola tekstowego 
            }
        }

Zauważmy, że w przypadku podania passwordId program zaczyta nam dane z bazy.
W przeciwnym razie nic nie wczyta i formatka będzie czysta niczym do edycji dla dodania nowego rekordu.

Wieńcząc dzieło dla formatki dodamy jeszcze zdarzenia tj. buttonCancel_Click, buttonSave_Click oraz metodę dla walidacji.
Metoda walidacji będzie wyglądała np tak:

private bool validation()
        {
            bool validatonResoult = false;

            if (textBoxService.Text.Length > 0 && textBoxValue.Text.Length > 0) validatonResoult = true;
            else validatonResoult = false;

            return validatonResoult;


        }

Oczywiście może wyglądać inaczej, zgodnie z życzeniem projektanta kodu ale do podstawowej zasady (wynikającej z wypełniania bazy danych) wystarczy.

Event buttonCancel_Click nie będzie krył żadnych rewolucji :

 private void buttonCancel_Click(object sender, EventArgs e)
        {
            this.Close(); //zamyka to - czyli nasze okno
        }

Natomiast ciekawiej będzie wyglądało 2 zdarzenie:

private void buttonSave_Click(object sender, EventArgs e)
        {
            if (validation()) //jeżeli walidacja jest spełniona
            {
                if (passwordId > 0) //jeżeli był wczytany rekord
                {
                    mySql.updateRow(passwordId, textBoxService.Text, textBoxValue.Text, richTextboxNote.Text);
                }
                else //a jeżeli nie był to
                {
                    mySql.createRow(textBoxService.Text, textBoxValue.Text, richTextboxNote.Text);
                }
                this.Close(); //zamykamy okienko
            }
            else //nie spełnienie walidacji musi kończyć się spektakularnie jakimś MessageBox-em
            {
                MessageBox.Show("Wstawiłeś coś nie tak. Pola Serwis i Wartość muszą być wypełnione"); 
            }
            
        }

Szybkość tego rozwiązania zawdzięczamy wcześniejszemu przygotowaniu metod w klasie MySqlDB tj. updateRow i createRow

Wracamy do głównej formatki:
Zaprogramujmy więc nasze przyciski Dodaj i Edytuj.
Aby dodać event dodawania wchodzimy w naszą formatkę i dla przycisku Dodaj robimy na nim 2-klik. W metodzie buttonAdd_Click musimy wstawić:

        private void buttonAdd_Click(object sender, EventArgs e)
        {
          
            using (FormPassword passwordAdd = new FormPassword())  //tworzymy nową intancję klasy FormPassword 
            {
                passwordAdd.ShowDialog();  // pokazujemy klasę - formatkę
            }

            refreshPasswordsTable(); //odświeżamy 
          
        }

Tak samo z przyciskiem Edytuj. W zdarzeniu buttonEdit_Click wpisujemy

        private void buttonEdit_Click(object sender, EventArgs e)
        {
            if (dataGridViewPasswords.SelectedRows.Count > 0)
            {
                using (FormPassword passwordAdd = new FormPassword(int.Parse(dataGridViewPasswords.CurrentRow.Cells["PasswordId"].Value.ToString()))) //tworzymy nową intancję klasy FormPassword ale tym razem wczytujemy do niej Id który jest ukryty w najszej tabeli
                {
                    passwordAdd.ShowDialog(); // pokazujemy klasę - formatkę
                }
            }

            refreshPasswordsTable(); //odświeżamy 
        }

Usuwanie wpisów

Zróbmy jeszcze usuwanie rekordów aby wykonać wszystkie zmiany w tej formatce zaplanowane w tej części.

W buttonRemove_Click wpisujemy :

            if (dataGridViewPasswords.SelectedRows.Count > 0)
            {
                MySqlDB mySql = new MySqlDB();
                mySql.deleteRow(int.Parse(dataGridViewPasswords.CurrentRow.Cells["PasswordId"].Value.ToString()));
                refreshPasswordsTable();
            }

Aby program działał w 100 % potrzebny mu w tabeli dataGrid uchwyt typi Row a nie jak teraz Cell. Zmienimy więc sposób zaznaczenia.
W Propierties dla dataGridViewPassword zmieniamy : MultiSelect na False oraz SelectionMode na FullRowSelect.

Teraz musimy jeszcze zakończyć prace w naszej formatce FormPassword.
Dla przycisku buttonCancel ustawiamy zdarzenie Click i w metodzie wpisujemy : this.Close();
To było łatwe, prawda ?
Teraz będzie trochę trochę turniej

        private void buttonRefresh_Click(object sender, EventArgs e)
        {
            refreshPasswordsTable();
        }

To by było na tyle. Sprawdźcie jeszcze czy wasza aplikacja odpala i do zobaczenia w 3 ostatniej części.

Finalnie aplikacja winna wyglądać tak:

Aplikacja finalna w czesci 2giej

Aplikacja finalna w czesci 2giej

Przynajmniej na tym etapie.

W następnej części zrobimy:

  • logowanie,
  • wykorzystamy szyfrowanie danych,
  • schowamy naszą aplikację do tray-a.
  • dodamy ikonki i zrobimy wianek.