Практична робота 4

 

Метою даної практичної роботи буде навчитися працювати із табличним представленням даних. Для цих речей в C# є візуальна компонента DataGridView, вона і реалізує механізми доступу до клітинок таблиці, та візуальне їх представлення. Після того, як Ви розробили блокнот, Ви вже маєте мати навики написання графічних інтерфейсів редагування даних. Тому, деякі моменти ми розглянемо бігло, спираючись на те, що Ви вже це робили.

Приступимо. В нашому табличному редакторі мусять бути функції збереження, завантаження даних із файлів, функції форматування тексту в клітинках таблиці, функції пошуку та заміни тексту в клітинках, та функції видалення/додання рядків/стовпців.

Для успішної здачі лабораторної роботи Вам буде необхідно всі написи і підписи на формах переписати українською, зрозумілою мовою, та ще дещо, що саме - ви дізнаєтесь в кінці умови практичної роботи.

І так, нам буде потрібно чотири вікна:

·        Головне

·        Вікно параметрів нового документу (для вводу потрібної кількості стовпців/рядків)

·        Вікно пошуку в клітинках

·        Вікно заміни тексту в клітинках

Інші вікна, такі як: закрити/відкрити файл, та вікно параметрів шрифтів будуть стандартними системними. Їх просто потрібно перемістити із палітри компонент на форму, і вони автоматично відобразяться під формою таким чином.

Список потрібних нам не візуальних компонент

         Тепер приведемо вигляд головного меню із описанням використаних візуальних компонент на ньому:

Головне вікно з компонентами

         Розберемо елементи головного меню, для того, щоб Ви не заплутались в обробниках подій елементів приведених в коді на наступному етапі.

Меню «Файл»

Описание: C:\Users\Sergiy\AppData\Local\Temp\SNAGHTML431416b.PNG

Меню «Сітка»

Описание: C:\Users\Sergiy\AppData\Local\Temp\SNAGHTML4318152.PNG

Меню «Стиль»

         Далі по компонентно розберемо допоміжні три вікна:

Описание: C:\Users\Sergiy\AppData\Local\Temp\SNAGHTML431de85.PNG

Вікно створення документу

Описание: C:\Users\Sergiy\AppData\Local\Temp\SNAGHTML432c319.PNG

Вікно пошуку в клітинках документу

Описание: C:\Users\Sergiy\AppData\Local\Temp\SNAGHTML4331aed.PNG

Вікно пошуку та заміни в клітинках документу

         Тепер перейдемо до опису подій компонент в програмному коді. Події нашого головного вікна.

public partial class Form1 : Form

 {

  public int x = 10;//розміри листа по замовчуванню

  public int y = 10;//по горизонталі і вертикалі відповідно

 

  public void initGrid(int c, int r) {//ініціалізація сітки

   //c - ширина, r - висота (в клітинках)

   x = c; y = r; //зберігаємо значення ширини та висоти в глобальні змінні

   dataGridView1.Columns.Clear();//очищуємо стовпці

   dataGridView1.Rows.Clear();//очищуємо рядки

   for (int i = 0; i < x; i++)//перебираємо від 0 до кількості стовпців -1 через змінну і

    {

     dataGridView1.Columns.Add(i.ToString(), i.ToString());//додаємо наступний стовпець

        //перший параметр назва стовпця

        //другий заголовок

    }

 

   for (int i = 0; i < y; i++)// перебираємо від 0 до кількості стовпців -1 через змінну і

    {

     dataGridView1.Rows.Add();//додаємо рядок

    }

  }

 

public void saveGrid(string fn) { //функція збереження сітки з даними в файл

   //fn – ім’я файлу

   //в файлі розділювачом клітинок буде виступати "|", тобто такий рядок

   // клітинка 1       клітинка 2      клітинка 3

   //в файлі буде записано, як "клітинка 1|клітинка 2|клітинка 3"

 FileInfo f = new FileInfo(fn);//ініціалізуємо об’єкт файлу f

 

 StreamWriter w = new StreamWriter(f.Create());//створюємо поток для запису в файл w,

   //якщо немає - файл створиться, якщо є - перезапишеться

 string l;//буде використовуватись для формування рядка з подальшим записом в файл

 for (int rc = 0; rc < dataGridView1.Rows.Count - 1; rc++)//перебираємо від 0 до кількості рядків -2 через rc

  {

   l="";//очищуємо рядок

   for (int cc = 0; cc <= dataGridView1.Columns.Count-1; cc++) //перебираємо від 0 до кількості стовпців -1 через сс

    {

     l += dataGridView1.Rows[rc].Cells[cc].Value + "|";//додаємо до рядка значення клітинки і символ "|"

    }

   l.Substring(0, l.Length - 1);//підтираємо останній символ "|" - він лишній

   w.WriteLine(l);//пишемо рядок в файл

  }

 w.Close(); //закриваємо файл

}

 

public void openGrid(string fn) {//відкриваємо файл fn та відображаємо його вміст на сітці

 FileInfo f = new FileInfo(fn);//ініціалізуємо об’єкт файлу f

 StreamReader r = new StreamReader(f.OpenRead());//відкриваємо поток r для читання даних з файлу

 string l;//буде використовуватись для формування рядка з подальшим записом в файл

 dataGridView1.Rows.Clear();//очищуємо рядки

 dataGridView1.Columns.Clear();//очищуємо стовпці

 while (!r.EndOfStream) {//цикл працює доки ми не досягли кінця файлу

  l = r.ReadLine();//зчитуємо наступний рядок з файлу

  String[] cells = l.Split('|');//розділяємо підрядки відокремлені '|' в масив типу String

  if (dataGridView1.Columns.Count < cells.Length - 1)//якщо кількість стовпців сітки менша за кількість елементів

    //зчитаних із файлу, тоді в  нас порожня сітка, і ми, мусимо, додати потрібну кількість стовпців

   for (int i = 0; i < cells.Length - 1; i++)//перебираємо від 0 до кількості елементів зчитаних з файлу

    dataGridView1.Columns.Add(i.ToString(),i.ToString());//додаємо наступний стовпець

  dataGridView1.Rows.Add(cells);//додаємо зчитаний рядок до сітки

 }

}

 

public void gridFind(string txt, bool c)//пошук по клітинкам сітки

   //с - чутливість до реєстру

   //txt - текст для пошуку

{

 string ct;//змінна в яку, для  зручності, буде копіюватися текст клітинки

   //так як процедура всі знайдені клітинки (якщо є) відображає вибраними

   //то потрібно зняти перед цим клітинку (клітинки) які вибрав користувач

 foreach (DataGridViewCell cl in dataGridView1.SelectedCells) { //якщо є вибрані клітинки на сітці

  cl.Selected = false;//помічаємо їх як не вибрані

 }

 for (int rc=0;rc<=dataGridView1.Rows.Count-1;rc++)//перебираємо від 0 до кількості рядків -1 через rc

  for (int cc = 0; cc <= dataGridView1.Columns.Count - 1; cc++)//перебираємо від 0 до кількості стовпців -1 через сс

   {

    if (!c)//якщо пошук не чутливий до реєстру

     {

      ct=dataGridView1.Rows[rc].Cells[cc].Value+""//беремо значення з поточної клітинки

      if (txt.ToUpper() == ct.ToUpper())//перевіряємо із шуканим значенням у верхньому реєстрі

       dataGridView1.Rows[rc].Cells[cc].Selected = true;//якщо значення співпадають виділяємо клітинку

     }

      else//пошук чутливий до реєстру

     {

      if (txt==dataGridView1.Rows[rc].Cells[cc].Value+"")//порівнюємо значення

      dataGridView1.Rows[rc].Cells[cc].Selected = true;//якщо значення співпадають виділяємо клітинку

     }

    }

  }

 

public Form1()//конструктор форми

 {

  InitializeComponent();

  initGrid(x,y);//ініціалізуємо сітку по значенням за замовчуванням

 }

 

private void newToolStripMenuItem_Click(object sender, EventArgs e)//обробка елементу меню новий

 {

  Form2 f2 = new Form2(x, y,this);//створюємо форму, яка запитує кількість стовпців та рядків

   //x - кількість стовпців, y - кількість рядків, this - посилання на головну форму

  f2.ShowDialog();//показуємо вікно модально

 }

 

private void saveToolStripMenuItem_Click(object sender, EventArgs e)//обробка елементу меню зберегти

 {

  if (saveFileDialog1.ShowDialog() == DialogResult.OK)//якщо користувач вибрав ім’я файлу

   {

    saveGrid(saveFileDialog1.FileName);//зберігаємо файл

   }

 }

 

private void openToolStripMenuItem_Click(object sender, EventArgs e)//обробка елементу меню відкрити

 {

  if (openFileDialog1.ShowDialog() == DialogResult.OK)//якщо користувач вибрав файл і натиснув ок

   openGrid(openFileDialog1.FileName);//відкриваємо файл

 }

 

private void addColumnToolStripMenuItem_Click(object sender, EventArgs e)//обробка елементу додати стовпець

 {

    //створюємо новий стовпець текстового типу, одразу передаємо значення параметрів нового стовпця

  DataGridViewColumn col = new DataGridViewColumn(new DataGridViewTextBoxCell()) { Name="col",HeaderText="col"};

    //col.Name - назва стовпця

    //col.HeaderText - заголовок стовпця

  dataGridView1.Columns.Insert(dataGridView1.CurrentCell.ColumnIndex,col);//додаємо новий стовпець, перед активним

 }

 

private void addRowToolStripMenuItem_Click(object sender, EventArgs e)//елемент меню додати рядок

 {

  dataGridView1.Rows.Insert(dataGridView1.CurrentCell.RowIndex,new String[]{});//додаємо новий рядок перед активним

 }

 

private void removeColumnToolStripMenuItem_Click(object sender, EventArgs e)//елемент меню видалити стовпець

 {

  dataGridView1.Columns.RemoveAt(dataGridView1.CurrentCell.ColumnIndex);//видаляє активний стовпець

 }

 

private void removeRowToolStripMenuItem_Click(object sender, EventArgs e)//елемент меню видалити рядок

 {

  dataGridView1.Rows.RemoveAt(dataGridView1.CurrentCell.RowIndex);//видаляє активний рядок

 }

 

private void boldToolStripMenuItem_Click(object sender, EventArgs e)//елемент меню жирний

 {

  try//пробуємо змінити жирність шрифту клітинки

   {

    if (dataGridView1.CurrentCell.Style.Font.Bold)//якщо клітинка вже жирна

     //створюємо новий екземпляр шрифту на базі поточного але нежирний

     dataGridView1.CurrentCell.Style.Font = new Font(dataGridView1.CurrentCell.Style.Font,dataGridView1.CurrentCell.Style.Font.Style&~FontStyle.Bold);

     //якщо шрифт не жирний створюємо новий екземпляр шрифту + жирний

      else dataGridView1.CurrentCell.Style.Font = new Font(dataGridView1.CurrentCell.Style.Font, dataGridView1.CurrentCell.Style.Font.Style|FontStyle.Bold);

   }

    catch//якщо трапилася помилка, тобто у клітинці ще не створений об’єкт шрифт

   {

      //створюємо його на базі стандартного + жирний

    dataGridView1.CurrentCell.Style.Font = new Font(dataGridView1.DefaultCellStyle.Font, FontStyle.Bold);

   }

}

 

private void italicToolStripMenuItem_Click(object sender, EventArgs e)//елемент меню курсив

 {

  try//пробуємо змінити нахиленість шрифту клітинки

   {

    if (dataGridView1.CurrentCell.Style.Font.Italic)//якщо клітинка вже курсивна

        //створюємо новий екземпляр шрифту на базі поточного але не курсивний

     dataGridView1.CurrentCell.Style.Font = new Font(dataGridView1.CurrentCell.Style.Font, dataGridView1.CurrentCell.Style.Font.Style&~FontStyle.Italic);

        //якщо шрифт не курсивний створюємо новий екземпляр шрифту + курсив

     else dataGridView1.CurrentCell.Style.Font = new Font(dataGridView1.CurrentCell.Style.Font, dataGridView1.CurrentCell.Style.Font.Style|FontStyle.Italic);

   }

    catch//якщо трапилася помилка, тобто у клітинці ще не створений об’єкт шрифт

   {

         //створюємо його на базі стандартного + курсив

    dataGridView1.CurrentCell.Style.Font = new Font(dataGridView1.DefaultCellStyle.Font, FontStyle.Italic);

   }

 }

 

private void underlineToolStripMenuItem_Click(object sender, EventArgs e)//елемент меню підкреслений

 {

  try//пробуємо змінити підкресленість шрифту клітинки

   {

    if (dataGridView1.CurrentCell.Style.Font.Italic)//якщо клітинка вже підкреслена

       //створюємо новий екземпляр шрифту на базі поточного але не підкреслений

     dataGridView1.CurrentCell.Style.Font = new Font(dataGridView1.CurrentCell.Style.Font, dataGridView1.CurrentCell.Style.Font.Style&~FontStyle.Underline);

      //якщо шрифт не підкреслений створюємо новий екземпляр шрифту + підкреслений

     else dataGridView1.CurrentCell.Style.Font = new Font(dataGridView1.CurrentCell.Style.Font, dataGridView1.CurrentCell.Style.Font.Style|FontStyle.Underline);

   }

    catch//якщо трапилася помилка, тобто у клітинці ще не створений об’єкт шрифт

   {

     //створюємо його на базі стандартного + підкреслений

    dataGridView1.CurrentCell.Style.Font = new Font(dataGridView1.DefaultCellStyle.Font, FontStyle.Underline);

   }

 }

 

private void fontToolStripMenuItem_Click(object sender, EventArgs e)//елемент меню шрифт

 {

  if (fontDialog1.ShowDialog() == DialogResult.OK)//якщо у вікні був вибраний шрифт і натиснута ок

   dataGridView1.CurrentCell.Style.Font = fontDialog1.Font;//змінюємо шрифт клітинки на шрифт обраний у формі

 }

 

private void findToolStripMenuItem_Click(object sender, EventArgs e)//елемент меню знайти

 {

  Form3 f3=new Form3(this);//створюємо форму пошуку, як аргумент передаємо посилання на головну форму

  f3.ShowDialog();//показуємо форму модально

 }

 

private void replaceToolStripMenuItem_Click(object sender, EventArgs e)//елемент меню замінити

 {

  Form4 f4 = new Form4(this,dataGridView1);//створюємо вікно пошуку та заміни

   //передаємо аргументами посилання на головну форму, та посилання на сітку, яка відображає дані

  f4.ShowDialog();//показуємо вікно модально

 }

}

         Тепер будемо наводити обробники подій допоміжних форм. Форма створення нового документа, яка запитує потрібну кількість стовпців та рядків. Код форми досить короткий, так як форма просто запитує два числа.


public partial class Form2 : Form

 {

  public Form1 f1;//посилання на головну форму

 

  public Form2(int c,int r,Form1 f)//конструктор форми

            //с - кількість стовпців, r - кількість рядків, f - посилання на головну форму

   {

    InitializeComponent();

    textBox1.Text = c.ToString();//записуємо в поле значення стовпців по замовчуванню

    textBox2.Text = r.ToString();//записуємо в поле значення рядків по замовчуванню

    f1 = f;// зберігає посилання на головну форму

   }

 

  private void button2_Click(object sender, EventArgs e)//кнопка відмінити

   {

    this.Close();//закриваємо форму

   }

 

  private void button1_Click(object sender, EventArgs e)//кнопка створити

   {

    f1.x = Int32.Parse(textBox1.Text);//передаємо в головну форму нову кількість стовпців

    f1.y = Int32.Parse(textBox2.Text);//передаємо в головну форму нову кількість рядків

    f1.initGrid(f1.x, f1.y);//викликаємо ініціалізацію сітки головної форми із новими параметрами

    this.Close();//закриваємо форму

   }

 }

         Наступна форма це – пошук тексту в клітинках, вона також буде не об’ємною, так як код процедури пошуку описаний в головній формі, ця ж форма, як і попередня, просто здійснює збір даних.

public partial class Form3 : Form

 {

  public Form1 f1;//посилання на головну форму

        

  public Form3(Form1 f)

   {

    InitializeComponent();

    f1 = f;//зберігаємо посилання на головну форму

   }

 

  private void button1_Click(object sender, EventArgs e)//кнопка знайти

   {

    f1.gridFind(textBox1.Text, checkBox1.Checked);//викликаємо процедуру пошуку головної форми

       //перший аргумент - шуканий текст, другий - чутливість до реєстру

    this.Close();//закриваємо форму

   }

 

  private void button2_Click(object sender, EventArgs e)//кнопка відмінити

   {

    this.Close();//закриваємо форму

   }

 }

         Далі опишемо події форми заміни тексту в клітинках. Суть форми в тому, що вона так як і попередня звертається до процедури пошуку клітинок головної форми, але після неї перебирає всі виділені клітинки і замінює їх. Таким чином ми перший етап де потрібно знайти текстове співпадання в клітинках «перенесли» з цієї форми, тим самим полегшили собі життя, і саме тому процедура пошуку описана в головній формі, бо, якщо її описати в формі пошуку, то при виклику форми заміни, ми би її викликати не змогли. Так як форма пошуку просто не була б створена, так, можна було б динамічно створити форму пошуку, та викликати процедуру з неї, вона б навіть знищилася автоматично, але навіщо ускладнювати собі процес розробки?

public partial class Form4 : Form

 {

  public Form1 f1;//посилання на головну форму

  public DataGridView dg;//посилання на сітку з даними

  public int c = 0;//змінна для проміжного зберігання кількості стовпців

  public int r = 0;//змінна для проміжного зберігання кількості рядків

 

  public Form4(Form1 f,DataGridView d)//конструктор форми

   {

    InitializeComponent();

    f1 = f;//зберігаємо посилання на головну форму

    dg = d;//зберігаємо посилання на сітку з даними

   }

 

  private void button2_Click(object sender, EventArgs e)//кнопка відмінити

   {

    this.Close();//закриваємо форму

   }

 

  private void button1_Click(object sender, EventArgs e)//кнопка замінити все

   {

    f1.gridFind(textBox1.Text, checkBox1.Checked);//викликаємо процедуру пошуку із головної форми

     //перший аргумент - шуканий текст, другий - чутливість до реєстру

     //після виконання функції, всі клітинки в яких міститься шуканий фрагмент тексту,

     //будуть виділеними

    foreach (DataGridViewCell cl in dg.SelectedCells) {//перебираємо всі виділені клітинки

     cl.Value = textBox2.Text;//замінюємо значення кожної клітинки

    }

    this.Close();//закриваємо форму

   }

 

 private void button3_Click(object sender, EventArgs e)//кнопка замінити

  {

   f1.gridFind(textBox1.Text, checkBox1.Checked);//викликаємо процедуру пошуку із головної форми

    //навіщо викликати - описано вище

   for (int i = dg.SelectedCells.Count-1; i >= 0; i--)//перебираємо вибрані клітинки через змінну і

    {

     //показуємо повідомлення із запитом на заміну

     if (MessageBox.Show("Replace?", "Question", MessageBoxButtons.YesNo) == DialogResult.Yes)

      {//якщо користувач схвалив заміну

       dg.SelectedCells[i].Value = textBox2.Text;//міняємо текст поточної клітинки

      }

     }

    this.Close();//закриваємо форму

   }

 }

         Ось і дійшли до кінця прикладу. Тепер перейдемо до Ваших додаткових завдань, завдання не складні, але ж все-таки потрібно буде доробити:

·        Додати до головного меню елемент «Файл» - «Вихід» який завершує роботу додатку

·        Додати до додатку елемент меню «Про програму» який викликає вікно відомостей про автора.

·        Додати до головного вікна обробник події закриття форми, який перед закриттям буде подавати запит на збереження файлу, якщо той був змінений

·        Додати пункт меню «Зберегти як» який примусово зберігає файл під новим іменем, а елемент меню «Зберегти» переробити так, щоб при повторному збереженні файлу на запитував ім’я знову

·        Додати елемент в меню «Сітка» - «Змінити назву стовпця», який показує на екрані вікно для вводу нової назви стовпця та змінює назву поточного стовпця на введений, якщо користувач дав згоду

·        В вікні пошуку та заміни, при натисканні кнопки замінити все, додати в повідомлені про завершення кількість замінених клітинок, наприклад: «Заміна завершена, замінено ХХ клітинок.»