Создание Windows-приложений на основе Visual C#

Работа с элементами управления

Разбить на страницы
Показывать лекцию целиком
Для работы с данной лекцией используйте примеры.

Элементы управления — это компоненты, обеспечивающие взаимодействие между пользователем и программой. Среда Visual Studio.NET предоставляет большое количество элементов, которые можно сгруппировать по нескольким функциональным группам.

Группа командных объектов

Элементы управления Button, LinkLabel, ToolBar реагируют на нажатие кнопки мыши и немедленно запускают какое-либо действие. Наиболее распространенная группа элементов.

Группа текстовых объектов

Большинство приложений предоставляют возможность пользователю вводить текст и, в свою очередь, выводят различную информацию в виде текстовых записей. Элементы TextBox, RichTextBox принимают текст, а элементы Label, StatusBar выводят ее. Для обработки введенного пользователем текста, как правило, следует нажать на один или несколько элементов из группы командных объектов.

Группа переключателей

Приложение может содержать несколько предопределенных вариантов выполнения действия или задачи; элементы управления этой группы предоставляют возможность выбора пользователю. Это одна из самых обширных групп элементов, в которую входят ComboBox, ListBox, ListView, TreeView, NumericUpDown и многие другие.

Группа контейнеров

С элементами этой группы действия приложения практически никогда не связываются, но они имеют большое значение для организации других элементов управления, их группировки и общего дизайна формы. Как правило, элементы этой группы, расположенные на форме, служат подложкой кнопкам, текстовым полям, спискам — поэтому они и называются контейнерами. Элементы Panel, GroupBox, TabControl, кроме всего прочего, разделяют возможности приложения на логические группы, обеспечивая удобство работы.

Группа графических элементов

Даже самое простое приложение Windows содержит графику — иконки, заставку, встроенные изображения. Для размещения и отображения их на форме используются элементы для работы с графикой — Image List, Picture Box.

Диалоговые окна

Выполняя различные операции с документом — открытие, сохранение, печать, предварительный просмотр, — мы сталкиваемся с соответствующими диалоговыми окнами. Разработчикам .NET не приходится заниматься созданием окон стандартных процедур: элементы OpenFileDialog, SaveFile Dialog, ColorDialog, PrintDialog содержат уже готовые операции.

Группа меню

Многие пользователи настраивают интерфейс приложений на свой вкус: одним нравится наличие определенных панелей инструментов, другим – индивидуальное расположение окон. Но в любом приложении будет присутствовать меню, содержащее в себе доступ ко всем возможностям и настройкам приложения. Элементы MainMenu, ContextMenu представляют собой готовые формы для внесения заголовков и пунктов меню.

Практическая работа с различными элементами управления будет рассмотрена в этой и последующих лекциях.

Создание главного меню

Большинство Windows-приложений оснащено главным меню, которое представляет собой иерархическую структуру выполняемых функций и команд. Практически все функции, которые можно осуществить при помощи элементов управления, имеют свой аналог в виде пункта меню.

Для создания главного меню используется элемент управления MainMenu, расположенный на панели инструментов ToolBox. Создайте новое приложение и назовите его NotepadCSharp. Установите следующие свойства формы:

Form1, форма, свойствоЗначение
Name frmmain
Icon Code\Glava2\NotepadCSharp\Icon\README.ICO
Text Notepad C#
WindowState Maximized

Перетаскиваем элемент управления на форму. Нам предстоит заполнить строки меню следующими пунктами (рис. 2.1):

(рис 2.1) Пункты главного меню приложения Notepad C#

Каждый пункт главного меню имеет свое окно свойств, в котором, подобно другим элементам управления, задаются значения свойств Name и Text (рис. 2.2). В поле Text перед словом New стоит знак — так называемый амперсанд, указывающий, что N должно быть подчеркнута и будет частью встроенного клавиатурного интерфейса Windows. Когда пользователь на клавиатуре нажимает клавишу Alt и затем N, выводится подменю New.

(рис 2.2) Свойства пункта меню New

В Windows есть еще интерфейс для работы с так называемыми быстрыми клавишами, или акселераторами. Сочетание клавиш указывают из перечисления Shortcut. Следует назначать стандартным пунктам общепринятые сочетания клавиш. Горизонтальная разделительная линия используется в тех случаях, когда надо визуально отделить сходные группы задач; для ее появления в свойстве Text пункта меню просто вводим знак тире. Для использования пунктов меню в коде, им также назначают имена (свойство Name), которые особенно важны, т.к. пунктов меню обычно бывает много. Свойства пунктов меню в приложении Notepad C# приводятся в таблице 2.1.

Пункты главного меню приложения Notepad C#
NameTextShortcut
mnuFile File
mnuNew New CtrlN
mnuOpen Open CtrlO
mnuSave Save CtrlS
menuItem5 -
mnuExit Exit AltF4
mnuEdit Edit
mnuCut Cut CtrlX
mnuCopy Copy CtrlC
mnuPaste Paste CtrlV
mnuDelete Delete Del
mnuSelectAll SelectAll CtrlA
mnuFormat Format
mnuFont Font…
mnuColor Color…
mnuWindow Window
mnuArrangeIcons Arrange Icons
mnuCascade Cascade
mnuTileHorizontal Tile Horizontal
mnuTileVertical Tile Vertical
mnuHelp ?
mnuAbout About Programm...

Вы можете самостоятельно выбрать сочетания клавиш, не указанные в табл. 2.1, для соответствующих пунктов меню.

Создание MDI-приложений

Такие программы, как блокнот и Microsoft Paint, относятся к SDI (Single — Document Interface) приложениям, способным работать только с одним документом. Другие, такие как Microsoft Word или Adobe Photoshop, поддерживают работу сразу с несколькими документами и называются MDI (Multiple — Document Interface) приложениями (рис. 2.3 и 2.4).

(рис 2.4) SDI приложение Microsoft Paint(рис 2.3) MDI-приложение Adobe Photoshop

В MDI-приложениях главная форма содержит в себе несколько документов, каждый из которых является холстом в графических программах или полем для текста в редакторах. Продолжим работу над приложением Notepad C#. В окне Solution Explorer щелкаем правой кнопкой на имени проекта и в появившемся контекстном меню выбираем Add/ Add Windows Form… . В появившемся окне называем форму – blank.cs. В нашем проекте появилась новая форма — будем называть ее дочерней. В режиме дизайна перетаскиваем на нее элемент управления RichTextBox: в отличие от элемента textBox, размер содержимого текста в нем не ограничивается 64 Кб; кроме того, RichTextBox позволяет редактировать цвет текста, добавлять изображения. Свойству Dock этого элемента устанавливаем значение Fill (рис. 2.5):

(рис 2.5) Свойство Dock элемента RichTextBox

Переходим в режим дизайна формы Frmmain и устанавливаем свойству IsMdiContainer значение true. Цвет формы при этом становится темно-серым. Новые документы будут у нас появляться при нажатии пункта меню New (которому мы установили сочетание клавиш Ctrl+N), поэтому дважды щелкаем в этом пункте и переходим в код:

private void mnuNew_Click(object sender, System.EventArgs e)
  {
    //Создаем новый экземпляр формы  frm
    blank  frm = new blank();      
    //Указываем, что родительским контейнером 
    //нового экземпляра будет эта, главная форма.
    frm.MdiParent = this;      
    //Вызываем форму
    frm.Show();
  }

Запускаем приложение. Теперь при нажатии клавиш Ctrl+N или выборе пункта меню New появляется несколько окон, расположенных каскадом. Однако заголовок у них всех одинаковый — blank. При создании нескольких документов, например в Microsoft Word, они называются ДокументN, где N — номер документа. Переключаемся в код формы blank, и в классе blank объявляем переменную DocName:

public string DocName = "";

Переключаемся в код формы frmmain и в классе frmmain объявляем переменную openDocuments:

private int openDocuments = 0;

Присваиваем переменной DocName часть названия по шаблону, в который включен счетчик числа открываемых документов, затем это значение передаем свойству Text создаваемой формы frm:

private void mnuNew_Click(object sender, System.EventArgs e)
  {
    blank  frm = new blank();
    frm.DocName = "Untitled " + ++openDocuments;
    frm.Text = frm.DocName;
    frm.MdiParent = this;
    frm.Show();
  }

Запускаем приложение. Теперь новые документы содержат разные заголовки (рис. 2.6).

(рис 2.6) Новые документы имеют упорядоченные названия

Перечисление MdiLayout

При работе с несколькими документами в MDI-приложениях удобно упорядочивать их на экране. Можно, конечно, распределять формы вручную, но при работе с большим количеством документов это представляется затруднительным. В пункте меню Window реализуем процедуру выравнивания окон.

Создаем обработчиков:

private void mnuArrangeIcons_Click(object sender, System.EventArgs e)
  {
  this.LayoutMdi(MdiLayout.ArrangeIcons);
  }

private void mnuCascade_Click(object sender, System.EventArgs e)
  {
    this.LayoutMdi(MdiLayout.Cascade);
  }

private void mnuTileHorizontal_Click(object sender, System.EventArgs e)
  {
    this.LayoutMdi(MdiLayout.TileHorizontal);
  }

private void mnuTileVertical_Click(object sender, System.EventArgs e)
  {
    this.LayoutMdi(MdiLayout.TileVertical);
  }

Метод LayoutMdi содержит перечисление MdiLayout, содержащее четыре члена. ArrangeIcons переключает фокус на выбранную форму, в свойстве MdiList пункта меню ArrangeIcons устанавливаем также значение true. При открытии нескольких новых документов окна располагаются каскадом (рис. 2.6), их можно расположить горизонтально — значение TileHorizontal или вертикально — значение TileVertical, а затем снова вернуть каскадное расположение — Cascade (рис. 2.7):

(рис 2.7) Расположение окон TileHorizontal и пункт меню ArrangeIcons

Вырезание, копирование и вставка текстовых фрагментов

С приложением работать будет удобней, если при создании нового документа он сразу будет занимать всю область главной формы. Для этого установим свойство WindowState формы blank Maximized. Теперь приступим к созданию обработчиков для стандартных операций вырезания, копирования и вставки. Элемент управления RichTextBox имеет свойство SelectedText, которое содержит выделенный фрагмент текста. На основании этого свойства и будут реализованы действия по работе с текстом. В коде формы blank объявляем переменную BufferText, в которой будет храниться буферизованный фрагмент текста:

private string BufferText = "";

Далее создаем соответствующие методы:

// Вырезание текста
  public void Cut()
    {
      this.BufferText = richTextBox1.SelectedText;
      richTextBox1.SelectedText = "";
    }

  // Копирование текста
  public void Copy()
    {
      this.BufferText = richTextBox1.SelectedText;
    }

  // Вставка
  public void Paste()
    {
      richTextBox1.SelectedText = this.BufferText;
    }

  // Выделение всего текста — используем свойство SelectAll элемента управления RichTextBox 
  public void SelectAll()
    {
      richTextBox1.SelectAll(); 
    }

  // Удаление
    public void Delete()
    {
      richTextBox1.SelectedText = "";  
      this.BufferText = "";
  }

Переключаемся в режим дизайна формы и создаем обработчиков для пунктов меню:

private void mnuCut_Click(object sender, System.EventArgs e)
    {
      blank frm = (blank)this.ActiveMdiChild;
      frm.Cut();
    }

private void mnuCopy_Click(object sender, System.EventArgs e)
    {
    blank frm = (blank)this.ActiveMdiChild;
      frm.Copy();
    }

private void mnuPaste_Click(object sender, System.EventArgs e)
    {
      blank frm = (blank)this.ActiveMdiChild;
      frm.Paste();
    }

private void mnuDelete_Click(object sender, System.EventArgs e)
    {
      blank frm = (blank)this.ActiveMdiChild;
      frm.Delete();
    }

private void mnuSelectAll_Click(object sender, System.EventArgs e)
    {
      blank frm = (blank)this.ActiveMdiChild;
      frm.SelectAll();
    }

Свойство ActiveMdiChild переключает фокус на текущую форму, если их открыто несколько, и вызывает один из методов, определенных в дочерней форме. Запускаем приложение. Теперь мы можем выполнять все стандартные операции с текстом.

Контекстное меню

Контекстное меню, дублирующее некоторые действия основного меню, — не самый быстрый способ работы с программой, но самый привычный для пользователя. Элемент управления TextBox содержит в себе простейшее контекстное меню, дублирующее действия подменю Edit. Для того чтобы убедиться в этом, достаточно нанести этот элемент управления на форму и запустить приложение (рис. 2.8):

(рис 2.8) Контекстное меню элемента TextBox

В нашем приложении Notepad C# в качестве текстового элемента мы используем RichTextBox. Добавим элемент управления contextMenu из окна ToolBox на форму blank. Добавляем пункты контекстного меню точно так же, как мы это делали для главного меню (рис. 2.9):

(рис 2.9) Пункты контекстного меню

Свойство Text и Shortcut пунктов меню оставляем прежними. Если мы установим затем для свойства ShowShortcut значение false, то сочетания клавиш будут работать, но в самом меню отображаться не будут — как на рис. рис. 2.8. Свойство Name будет формироваться следующим образом: для пункта Cut — cmnuCut, для Сopy — cmnuCopy и т.д.

В обработчике пунктов просто вызываем соответствующие методы:

private void cmnuCut_Click(object sender, System.EventArgs e)
  {
    Cut();
  }

  private void cmnuCopy_Click(object sender, System.EventArgs e)
  {
    Copy();
  }

  private void cmnuPaste_Click(object sender, System.EventArgs e)
  {
    Paste();
  }

  private void cmnuDelete_Click(object sender, System.EventArgs e)
  {
  Delete();
  }

  private void cmnuSelectAll_Click(object sender, System.EventArgs e)
  {
  SelectAll();
}

Последнее, что нам осталось сделать, — это определить, где будет появляться контекстное меню. Элемент RichTextBox, так же как и формы frmmain и blank, имеет свойство ContextMenu, где мы и указываем contextMenu1, поскольку нам нужно отображать меню именно в текстовом поле. Запускаем приложение — теперь в любой точке текста доступно меню (рис. 2.10).

(рис 2.10) Контекстное меню

Диалоговые окна

Среда Visual Studio .NET содержит готовые диалоговые окна, предлагающие выбрать файл для открывания или путь на диске для сохранений текущего файла (рис. 2.11).

(рис 2.11) Диалоговое окно открытия файла

Мы рассмотрим одну часть диалоговых окон в этой лекции, а другую, относящуюся к подготовке документа к печати, — в лекции 6.

OpenFileDialog

Добавьте на форму frmmain элемент управления OpenFileDialog из окна панели инструментов ToolBox. Подобно элементу MainMenu, он будет располагаться на панели невидимых компонент (рис. 2.12):

(рис 2.12) Добавление OpenFileDialog на форму

Свойство FileName задает название файла, которое будет находиться в поле "Имя файла:" при появлении диалога. На рис. рис. 2.12 название в этом поле — "Текстовые файлы", поскольку на рис. рис. 2.11 был введен именно этот текст. Свойство Filter задает ограничение файлов, которые могут быть выбраны для открытия — в окне будут показываться только файлы с заданным расширением. Через вертикальную разделительную линию можно задать смену типа расширения, отображаемого в выпадающем списке "Тип файлов". Здесь введено Text Files (*.txt)|*.txt|All Files(*.*)|*.* что означает обзор либо текстовых файлов, либо всех. Свойство InitialDirectory позволяет задать директорию, откуда будет начинаться обзор. Если это свойство не установлено, исходной директорией будет рабочий стол.

Для работы с файловыми потоками в коде формы blank подключаем пространство имен System.IO:

using System.IO;

В методе Open считываем содержимое файла в RichTextBox:

//Создаем метод Open, в качестве параметра объявляем строку адреса  файла.
  public void Open(string OpenFileName)
  {
    //Если файл не выбран, возвращаемся назад (появится встроенное предупреждение)
    if (OpenFileName == "") 
    {
      return;
    }
    else
    {
//Создаем новый объект StreamReader и передаем ему переменную //OpenFileName
      StreamReader sr = new StreamReader(OpenFileName);
      //Читаем весь файл и записываем его в richTextBox1
      richTextBox1.Text = sr.ReadToEnd();
      // Закрываем поток
      sr.Close();
      //Переменной DocName присваиваем адресную строку
      DocName = OpenFileName;
    }
  }

Добавим обработчик пункта меню Open формы frmmain:

private void mnuOpen_Click(object sender, System.EventArgs e)
  {
    //Можно программно задавать доступные для обзора расширения файлов 
    //openFileDialog1.Filter = "Text Files (*.txt)|*.txt|All Files(*.*)|*.*";
    
      
    //Если выбран диалог открытия файла, выполняем условие
    if (openFileDialog1.ShowDialog() == DialogResult.OK) 
    {
      //Создаем новый документ
      blank frm = new blank();
      //Вызываем метод Open формы blank
      frm.Open(openFileDialog1.FileName);
      //Указываем, что родительской формой является форма frmmain
      frm.MdiParent = this;
      //Присваиваем переменной DocName имя открываемого файла
      frm.DocName = openFileDialog1.FileName;
      //Свойству Text формы присваиваем переменную DocName
      frm.Text = frm.DocName;
      //Вызываем форму frm
      frm.Show();  
    }
  }

Запускаем приложение и открываем текстовый файл, сохраненный в формате блокнота (рис. 2.13).

(рис 2.13) Заголовок формы представляет собой адрес и имя открытого файла

Для корректного чтения кириллицы текст в блокноте должен быть сохранен в кодировке Unicode. К сожалению, встроенные диалоговые окна OpenFileDialog Visual Studio .NET не содержат дополнительного поля, позволяющего выбирать кодировку файла при его открытии или сохранении, как это реализовано, например, в блокноте.

SaveFileDialog

Для сохранения файлов добавляем на форму frmmain элемент управления saveFileDialog1. Cвойства этого диалога в точности такие же, как у OpenFileDialog (рис. 2.12). Переходим в код формы blank:

//Создаем метод Save, в качестве параметра объявляем строку адреса  файла.
    public void Save(string SaveFileName)
    {
      //Если файл не выбран, возвращаемся назад (появится встроенное предупреждение)
      if (SaveFileName == "") 
      {
        return;
      }
      else
      {
//Создаем новый объект StreamWriter и передаем ему переменную //OpenFileName
        StreamWriter sw = new StreamWriter(SaveFileName);
        //Содержимое richTextBox1 записываем в файл
        sw.WriteLine(richTextBox1.Text);
        //Закрываем поток
        sw.Close();
//Устанавливаем в качестве имени документа название сохраненного файла
        DocName = SaveFileName;
      }
    }

Добавляем обработчик пункта меню Save формы frmmain:

private void mnuSave_Click(object sender, System.EventArgs e)
  {
    //Можно программно задавать доступные для обзора расширения файлов 
    //openFileDialog1.Filter = "Text Files (*.txt)|*.txt|All Files(*.*)|*.*";

    //Если выбран диалог открытия файла, выполняем условие
    if (saveFileDialog1.ShowDialog() == DialogResult.OK) 
    {
      //Переключаем фокус на данную форму.
      blank frm = (blank)this.ActiveMdiChild;
      //Вызываем метод Save формы blank
      frm.Save(saveFileDialog1.FileName);
      //Указываем, что родительской формой является форма frmmain
      frm.MdiParent = this;
      //Присваиваем переменной FileName имя сохраняемого файла
      frm.DocName = saveFileDialog1.FileName;
      //Свойству Text формы присваиваем переменную DocName
      frm.Text = frm.DocName;      
  
    }
  }

Запускаем приложение. Теперь файлы можно открывать, редактировать и сохранять. Однако, при сохранении внесенных изменений в уже сохраненном файле вместо его перезаписи вновь появляется окно SaveFileDialog. Изменим нашу программу так, чтобы можно было сохранять и перезаписывать файл. В конструкторе формы frmmain после InitializeComponent отключим доступность пункта меню Save:

mnuSave.Enabled = false;

Переключаемся в режим дизайна формы frmmain и добавляем пункт меню Save As после пункта Save. Устанавливаем следующие свойства этого пункта: Name — mnuSaveAs, Shortcut — CtrlShiftS, Text Save As. В обработчике Save As вставляем вырезанный обработчик пункта Save и добавляем включение доступности Save:

mnuSave.Enabled = true;

Сохранять изменения требуется как в только что сохраненных документах, так и в документах, созданных ранее и открытых для редактирования. Поэтому добавим в метод Open включение доступности пункта меню Save:

private void mnuOpen_Click(object sender, System.EventArgs e)
{
  mnuSave.Enabled = true;
}

В обработчике пункта Save добавим простую перезапись файла — вызов метода Save формы blank:

private void mnuSave_Click(object sender, System.EventArgs e)
  {
    //Переключаем фокус на данную форму.
    blank frm = (blank)this.ActiveMdiChild;
    //Вызываем метод Save формы blank
    frm.Save(frm.DocName);
    
      
  }

Запускаем приложение. Теперь, если мы работаем с несохраненным документом, пункт Save неактивен (рис. 2.14), после сохранения он становится активным (рис. 2.15) и, кроме того, работает сочетание клавиш Ctrl+S. Можно сохранить копию текущего документа, вновь воспользовавшись пунктом меню Save As (рис. 2.16).

(рис 2.15) Новый документ(рис 2.14) Сохраненный документ(рис 2.16) Сохранение копии

Конечно, эту задачу можно решить более элегантно — совместив оба пункта сохранения документа в один. Но мне хотелось показать разницу между простым сохранением и перезаписью файлов, кроме того, мы научились отключать доступность пунктов меню.

Сохранение файла при закрытии формы

Всякий раз, когда мы закрываем документ Microsoft Word, в который внесли изменения, появляется окно предупреждения, предлагающее сохранить документ. Добавим аналогичную функцию в наше приложение. В классе blank : System.Windows.Forms.Form формы blank создаем переменную, которая будет фиксировать сохранение документа:

public bool IsSaved = false;

В обработчик методов Save и Save As формы frmmain добавляем изменение значения этой переменной:

private void mnuSave_Click(object sender, System.EventArgs e)
    {
…
frm.IsSaved = true;
    } 

  private void mnuSaveAs_Click(object sender, System.EventArgs e)
    {
…
frm.IsSaved = true;
    }

Переходим в режим дизайна формы blank и в окне свойств переключаемся на события формы, щелкнув на значок с молнией. В поле события Closing дважды щелкаем и переходим в код:

private void blank_Closing(object sender, System.ComponentModel.CancelEventArgs e)
  {
  //Если переменная IsSaved имеет значение true, т. е.  новый документ 
//был сохранен (Save As) или в открытом документе были сохранены изменения (Save), то //выполняется условие
 if(IsSaved ==true)
  //Появляется диалоговое окно, предлагающее сохранить документ.
  if(MessageBox.Show("Do you want save changes in " + this.DocName + "?", 
      "Message", MessageBoxButtons.YesNo, 
      MessageBoxIcon.Question) == DialogResult.Yes)
      //Если была нажата  кнопка Yes, вызываем метод Save
    {
      this.Save(this.DocName); 
    }    
     
    
  }

Запускаем приложение. При закрытии формы blank или frmmain появляется окно предупреждения (рис. 2.17):

(рис 2.17) Окно предупреждения при закрытии формы

OpenFileDialog и SaveFileDialog для SDI-приложений

При создании MDI-приложений приходится разделять код для открытия и сохранения файлов, как мы делали для приложения Notepad C#. В случае SDI-приложений весь код будет находиться в одном обработчике. Cоздаем новое приложение, называем его TextEditor. На форме размещаем элемент управления TextBox и устанавливаем следующие свойства:

TextBox, свойствоЗначение
Name txtBox
Dock Fill
Multiline true
Text Да

Добавляем на форму элемент MainMenu, в котором будет всего три пункта — File, Open и Save (свойства этих пунктов см. в таблице 2.1). Из окна ToolBox перетаскиваем элементы OpenFileDialog и SaveFileDialog — свойства этих элементов в точности такие же, как и у диалогов приложения Notepad C#. Переходим в код формы. Подключаем пространство имен для работы с файловыми потоками:

using System.IO;

Добавляем обработчик для пункта меню Open:

private void mnuOpen_Click(object sender, System.EventArgs e)
  {
    //Показываем диалог выбора файла
    openFileDialog1.ShowDialog();
    // Переменной fileName присваиваем имя открываемого файла
    string fileName = openFileDialog1.FileName;
    //Создаем поток fs и открываем файл для чтения.
    FileStream filestream= File.Open(fileName, FileMode.Open, FileAccess.Read);
    //Проверяем,  открыт ли поток,  и если открыт, выполняем условие
    if(filestream != null)
    {
      //Создаем объект streamreader и связываем его с потоком filestream
      StreamReader streamreader = new StreamReader(filestream);
      //Считываем весь файл и записываем его в TextBox
      txtBox.Text = streamreader.ReadToEnd();
      //Закрываем поток.
      filestream.Close();
    }
  }

Добавляем обработчик для пункта меню Save:

private void mnuSave_Click(object sender, System.EventArgs e)
  {
    //Показываем диалог выбора файла
    saveFileDialog1.ShowDialog();
    // В качестве имени сохраняемого файла устанавливаем переменную fileName
    string fileName=saveFileDialog1.FileName;
    //Создаем поток fs и открываем файл для записи.
    FileStream filestream = File.Open(fileName, FileMode.Create, FileAccess.Write);
      //Проверяем,  открыт ли поток,  и если открыт, выполняем условие
    if(filestream != null)
    {
      //Создаем объект streamwriter и связываем его с потоком filestream
      StreamWriter streamwriter = new StreamWriter(filestream);
      //Записываем данные из TextBox в файл
      streamwriter.Write(txtBox.Text);
      //Переносим данные из потока в файл
      streamwriter.Flush();
      //Закрываем поток
      filestream.Close();
    }
  }

Запускаем приложение. Как уже отмечалось, элемент управления TextBox имеет встроенное контекстное меню, поддерживает сочетание клавиш для редактирования, а диалоговые окна открытия и сохранения позволяют этому простейшему приложению работать с внешними файлами.

На диске, прилагаемом к книге, вы найдете приложение TextEditor (Code\Glava2\TextEditor).

FontDialog

Продолжим работу над приложением Notepad C#. Добавим теперь возможность выбирать шрифт, его размер и начертание. В режиме дизайна перетащим на форму frmmain из окна ToolBox элемент управления FontDialog. Не изменяя ничего в свойствах этого элемента, переходим в обработчик пункта Font главного меню:

private void mnuFont_Click(object sender, System.EventArgs e)
  {
    //Переключаем фокус на данную форму.
    blank  frm = (blank)this.ActiveMdiChild;
    //Указываем, что родительской формой является форма frmmain  
    frm.MdiParent = this;
    //Вызываем диалог
    fontDialog1.ShowColor = true;
    //Связываем свойства SelectionFont и SelectionColor элемента RichTextBox 
    //с соответствующими свойствами диалога
    fontDialog1.Font = frm.richTextBox1.SelectionFont;
    fontDialog1.Color = frm.richTextBox1.SelectionColor; 
    //Если выбран диалог открытия файла, выполняем условие
    if (fontDialog1.ShowDialog() == DialogResult.OK)
    {
      frm.richTextBox1.SelectionFont = fontDialog1.Font;
      frm.richTextBox1.SelectionColor = fontDialog1.Color; 
    }
    //Показываем форму
    frm.Show();
  }

Запускаем приложение, и в окне Output появляется список ошибок (рис. 2.18).

(рис 2.18) Список ошибок — NotepadCSharp.blank.richTextBox1 недоступен из-за своего уровня защиты

В чем же дело? При расположении на форме blank элемента управления RichTextBox среда Visual Studio.NET сгенерировала экземпляр richTextBox1 класса System.Windows.Forms.RichTextBox с модификатором доступа private, из-за чего при обращении к нему и возникает исключение:

private System.Windows.Forms.RichTextBox richTextBox1;

Изменим модификатор на public и снова запустим приложение. При выборе пункта меню Font теперь можно менять параметры текущего текста.

ColorDialog

Диалоговое окно FontDialog содержит список цветов, которые могут быть применены к тексту, но предлагаемый список ограничен. Более интересной представляется возможность назначать пользовательский цвет, который может быть определен в большом диапазоне (рис. 2.19):

(рис 2.19) Диалоговое окно ColorDialog

Из окна ToolBox добавляем элемент управления ColorDialog и, вновь не изменяя его свойств, переходим к обработчику пункта Color главного меню формы frmmain:

private void mnuColor_Click(object sender, System.EventArgs e)
    {
      blank frm = (blank)this.ActiveMdiChild;
      frm.MdiParent = this;
      colorDialog1.Color = frm.richTextBox1.SelectionColor; 
      
      if (colorDialog1.ShowDialog() == DialogResult.OK)
      {
        frm.richTextBox1.SelectionColor = colorDialog1.Color; 
      }

      frm.Show();
    }

Обратите внимание на то, что код для ColorDialog в точности такой же, как и часть кода для свойства Color диалога FontDialog. Это и не удивительно: ведь мы связываем эти диалоги со свойствами одного и того же объекта — RichTextBox.

StatusBar

Элемент управления StatusBar применяется в программах для вывода информации в строку состояния — небольшую полоску, расположенную внизу приложения. В Microsoft Word, например, на ней отображается количество страниц, язык ввода, состояние проверки правописания и другие параметры. Добавим к приложению Notepad C# строку состояния, на которой осуществляется подсчет вводимых символов и выводится системное время.

Добавляем на форму blank элемент управления StatusBar. Удаляем содержимое поля свойства Text. В поле свойства Panels щелкаем на кнопку (…). Открывается StatusBarCollectionEditor, в котором мы создаем панели для отображения. Создайте две панели, дважды щелкая на кнопке Add, и установите им следующие свойства (измененные значения выделяются жирным шрифтом) (рис. 2.20 и рис. 2.21):

(рис 2.21) Свойства панели sbAmount(рис 2.20) Свойства панели sbTime

Значения некоторых свойств панели приводятся в таблице 2.2.

СвойствоЗначение
Alignment Выравнивание содержимого свойства Text на панели
AutoSize Изменение размеров панели по содержимому
BorderStyle Внешний вид панели – утопленная, приподнятая или без выделения
Icon Добавление иконки
Style Стиль панели
Text Текст, располагаемый на панели
ToolTipText Всплывающая подсказка — появляется при наведении курсора на панель
Width Ширина панели в пикселях
Name Название панели для обращения к ней в коде

Свойства панели, назначаемые в окне редактора StatusBarCollectionEditor, можно изменять в коде —именно так мы сейчас и поступим.

После завершения работы над панелями закрываем редактор. Свойству ShowPanels элемента управления StatusBar устанавливаем значение True. На форме немедленно отображаются две панели.

Выделяем элемент управления RichTextBox, в окне его свойств переключаемся на события и создаем обработчик для события TextChanged:

private void richTextBox1_TextChanged(object sender, System.EventArgs e)
  {
    //Свойству Text панели sbAmount устанавливаем надпись "Аmount of symbols" 
    //и длину  текста в RichTextBox.
    sbAmount.Text = "Аmount of symbols" + richTextBox1.Text.Length.ToString(); 
  }

Свойство Text панели sbAmount мы изменяем программно: даже если бы мы ничего не написали в окне редактора StatusBarCollectionEditor, при возникновении события TextChanged на панели появится надпись.

Займемся теперь второй панелью — той, на которую будем выводить системное время. В конструкторе формы blank добавляем код:

public blank()
  {
    InitializeComponent();
    //Свойству Text панели sbTime устанавливаем системное время, 
    // конвертировав его в тип String
    sbTime.Text = Convert.ToString(System.DateTime.Now.ToLongTimeString());
    //В тексте всплывающей подсказки  выводим текущую дату
    sbTime.ToolTipText = Convert.ToString(System.DateTime.Today.ToLongDateString());
  }

Запускаем приложение. Панель с выводимым временем располагается достаточно необычно (рис. 2.22), при необходимости для вывода времени в привычном правом нижнем углу можно добавить третью пустую панель.

(рис 2.22) Строка состояния с двумя панелями

Закрывание формы

При запуске приложения Notepad С# на форме имеются три стандартные кнопки — "Свернуть", "Развернуть" и "Закрыть". Большинство пользователей предпочитают использовать именно кнопку "Закрыть" для выхода из приложения. Тем не менее принято дублировать кнопку формы пунктом меню Exit. В обработчике этой кнопки добавляем код

private void mnuExit_Click(object sender, System.EventArgs e)
    {
      this.Close();
  }

Метод Close закрывает форму и может быть назначен другим элементам управления — например, кнопке.

CheckBox

Чекбоксы относятся к так называемым кнопкам отложенного действия, т. е. их нажатие не запускает немедленно какого-либо процесса. С их помощью пользователь устанавливает определенные параметры, результат действия которых скажется после запуска других элементов управления. Добавим в приложение Notepad C# форму для поиска заданного текста, в которой будут использованы элементы управления CheckBox. В окне Solution Explorer щелкаем правой кнопкой мыши на названии проекта — NotepadCSharp — и в появившемся меню выбираем пункт Add Windows Form. Называем новую форму FindForm.cs и устанавливаем следующие свойства:

FindForm, форма, свойствоЗначение
Name FindForm
FormBorderStyle FixedToolWindow
Size 328; 112
Text Find

Располагаем на форме TextBox, две кнопки и два элемента CheckBox так, как показано на рис. рис. 2.23:

(рис 2.23) Форма FindForm в режиме дизайна

Устанавливаем следующие свойства элементам управления:

TextBox, свойствоЗначение
Name txtFind
Size 192; 20
Text
checkBox1, свойствоЗначение
Name cbMatchCase
Text Match Case
checkBox2, свойствоЗначение
Name cbMatchWhole
Text Match whole word
button1, свойствоЗначение
Name btnOK
DialogResult OK
Text OK
button2, свойствоЗначение
Name btnCancel
DialogResult Cancel
Text Cancel

Выбор первого чекбокса — cbMatchCase — будет устанавливать поиск слова с учетом регистра, второго – cbMatchWhole — поиск по целому слову. Обратите внимание на свойство кнопок DialogResult — выбирая соответствующие свойства, мы устанавливаем обработчики для кнопок без обращения к коду. Значение ОК закрывает форму, выполняя установленное действие, — в данном случае — поиск; значение Cancel просто закрывает форму. В главном меню формы frmmain добавляем разделительную линию и пункт Find и устанавливаем значения свойств: Name — mnuFind, Shortcut — CtrlF, Text — Find. Переходим в обработчик этого пункта:

private void mnuFind_Click(object sender, System.EventArgs e)
  {
    //Создаем новый экземпляр формы FindForm
    FindForm frm = new FindForm();
    //Если выбран результат DialogResult.Cancel, закрываем форму (до этого 
    //мы использовали DialogResult.OK)
    if(frm.ShowDialog(this) == DialogResult.Cancel) return;
    ////Переключаем фокус на данную форму.
    blank form = (blank)this.ActiveMdiChild;
    ////Указываем, что родительской формой является форма frmmain  
    form.MdiParent = this;
    //Вводим переменную для поиска в определенной части текста —
    //поиск слова будет осуществляться от текущей позиции курсора
    int start = form.richTextBox1.SelectionStart;
    //Вызываем предопределенный метод Find элемента richTextBox1.
    form.richTextBox1.Find(frm.FindText, start, frm.FindCondition);
  }

Основой поиска будет метод Find элемента управления RichTextBox. Нам остается лишь указать параметры поиска, которые могут принимать различные значения (рис. 2.24):

(рис 2.24) Параметры метода Find. Щелкая на маленькую кнопку всплывающей подсказки Intel Sense, выбираем разные подсказки для групп передаваемых параметров

В коде формы FindForm осталось реализовать логику работы, зависящую от положения элементов CheckBox:

// Создаем перечисление, возвращающее параметр FindCondition
    public RichTextBoxFinds FindCondition
    {
      get
      {
        //Выбраны два чекбокса
        if (cbMatchCase.Checked  cbMatchWhole.Checked)
        {
          // Возвращаем свойство MatchCase и WholeWord 
          return RichTextBoxFinds.MatchCase| RichTextBoxFinds.WholeWord;
        }
        //Выбран один чекбокс
        if (cbMatchCase.Checked )
        {
          // Возвращаем свойство MatchCase
          return RichTextBoxFinds.MatchCase;
        }
        //Выбран другой чекбокс
        if (cbMatchWhole.Checked )
        {
          // Возвращаем свойство WholeWord
          return RichTextBoxFinds.WholeWord;
        }
        //Не выбран ни один чекбокс
        return RichTextBoxFinds.None;
      }
    }

И наконец, создаем свойство FindText, возвращающее в качестве переменной поиска введенный текст в текстовое поле формы FindForm:

public string FindText
    {
      get{return txtFind.Text;}
      set{txtFind.Text = value;}
    }

Запускаем приложение. Выбирая различные варианты, можно однократно искать заданное слово (рис. 2.25):

(рис 2.25) Поиск слова в тексте

Свойство TabIndex элементов управления

Многие пользователи предпочитают работать по большей части с клавиатурой и прибегать к помощи мыши только там, где без нее нельзя обойтись. При работе с формой Find было бы удобно ввести слово в текстовое поле, затем, нажав клавишу Tab и Enter, осуществить поиск, — или, дважды нажав клавишу Tab, переключить фокус ввода на чекбокс Match Case. Для этого в режиме дизайна формы Find следует установить свойство TabIndex элементов управления в виде последовательной нумерации, начиная с нуля:

Элемент управленияTabIndex
TxtFind 0
BtnOK 1
CbMatchCase 2
CbMatchWhole 3
BtnCancel 4

При такой нумерации фокус ввода будет последовательно переходить в порядке возрастания — от элемента txtFind до btnCancel. Свойство TabIndex имеет особенно большое значение при создании ряда текстовых полей, где пользователю будет предлагаться ввести информацию.

Label, LinkLabel и PictureBox

Программы, как правило, содержат пункт главного меню "О программе", где в отдельном окне помещается логотип компании, лицензионное соглашение, гиперссылки на сайт разработчика и другая информация. Создадим подобную форму, используя новые элементы управления — Label, LinkLabel и PictureBox.

Итак, добавим в наш проект новую форму и назовем ее About.cs. Установим следующие свойства формы:

About, форма, свойствоЗначение
Name About
FormBorderStyle FixedSingle
MaximizeBox False
MinimizeBox False
Size 318; 214
Text About Notepad C#

Добавим на форму элемент управления PictureBox — он представляет собой подложку, размещаемую на форме, которая может содержать в себе рисунки для отображения. В поле свойства Image щелкаем на кнопку (…) и выбираем рисунок по адресу Code\Glava2\NotepadCSharp\Icon\logo.gif. Поскольку logo.gif является анимированным рисунком, элемент PictureBox начинает воспроизводить анимацию сразу же, даже в режиме дизайна.

Из окна ToolBox перетащим на форму кнопку, Label и LinkLabel. В поле свойства Text кнопки введем OK. Элемент Label предназначен для размещения на форме надписей, которые в готовом приложении будут доступны только для чтения. В поле свойства Text введем Notepad C# 2006 All rights reserved. Элемент LinkLabel отображает текст на форме в стиле web-ссылок и обычно используется для создания навигации между формами или ссылки на сайт. В поле Text этого элемента вводим адрес гипотетического сайта — www.notepadcsharp.com. Пользователь будет переходить на сайт, нажимая на эту ссылку, поэтому реализуем переход по гиперссылке для события Click. В окне Properties щелкаем дважды на событии Click и добавляем обработчик:

private void linkLabel1_Click(object sender, System.EventArgs e)
    {
    //Добавляем блок для обработки исключений — по разным причинам 
    //пользователь может не получить доступа к ресурсу.
      try
      {
        //Вызываем метод VisitLink, определенный ниже
        VisitLink();
      }
      catch (Exception ex )
      {
        MessageBox.Show(ex +"Unable to open link that was clicked.");
      }
      
    }
    //Создаем метод VisitLink
      private void VisitLink()
      {
        // Изменяем цвет посещенной ссылки, программно 
        //обращаясь к свойству LinkVisited элемента LinkLabel 
        linkLabel1.LinkVisited = true;
        //Вызываем метод Process.Start method  для запуска браузера, 
        //установленного по умолчанию, и открытия ссылки
        System.Diagnostics.Process.Start("http://www.notepadcsharp.com");
      }

Кнопка OK просто будет закрывать форму:

private void button1_Click(object sender, System.EventArgs e)
    {
      this.Close();
    }

В пункте главного меню About Programm… формы frmmain добавляем процедуру вызова формы About:

private void mnuAbout_Click(object sender, System.EventArgs e)
    {
      //Создаем новый экземпляр формы  About
      About  frm = new About();
      frm.Show();
    }

Запускаем приложение (рис. 2.26):

(рис 2.26) Окно About Notepad C#

Режимы запуска окон

В приложении Notepad C# с открытой формой About без труда можно вернуться к другим пунктам меню программы или документу, т. е. форма About позволяет переключать фокус. Такое поведение дочерних форм характерно для панелей инструментов в программах и называется немодальным. Наоборот, форма, запущенная в модальном режиме, не позволит работать с приложением до тех пор, пока мы не закроем эту форму. Для открытия формы в модальном режиме используется метод ShowDialog:

private void mnuAbout_Click(object sender, System.EventArgs e)
    {
      //Создаем новый экземпляр формы  About
      About  frm = new About();
      frm.ShowDialog();
    }

Модификаторы доступа и наследование форм

С модификаторами доступа вы сталкивались, когда изучали синтаксис языка C#. Используя их, можно определить область видимости, иначе говоря, уровень доступности типов и членов классов, — под этим понимается участок кода, из любой точки которого будет доступен запрашиваемый тип или член класса. Технология Common Type System (CTS, система общих типов) — одна из технологий, обеспечивающих функционирование среды .NET, определяет правила для видимости типов и доступа к их членам. Различные типы модификаторов и их описание приводятся в таблице 2.3.

МодификаторОпределение
public (общественный, открытый) Объект доступен любому коду из любой сборки
private (частный, закрытый) Объект может быть вызван другими методами только из того же типа
internal (внутренний, сборочный) Объект может быть вызван любым кодом из той же сборки
protected (защищенный, родовой) Объект может быть вызван производными типами независимо от их сборки
protected internal (родовой сборочный) Объект может быть вызван производными типами, определенными в той же самой сборке

В качестве объекта могут выступать методы, свойства, классы и, как мы далее увидим, — формы. Под сборкой здесь понимается функциональная единица кода (строгое определение сборок см. в лекции 9).

Наследование классов — достаточно простое и чрезвычайно удобное понятие синтаксиса С#. Формы, которые содержат большую часть методов и свойств, определенных как public, тоже можно наследовать — для получения доступа к свойствам, определенным как protected, и для ускорения работы. В самом деле, если у нас имеется уже готовая форма (например, About Notepad C#), которую мы собираемся использовать в нескольких приложениях, удобно просто создать наследуемую форму.

Запускаем приложение Notepad C# и в окне Solution Explorer щелкаем правой кнопкой на названии проекта. В появившемся контекстном меню выбираем Add\Add Inherited Form… . В окне Add New Item задаем название формы — IAbout.cs. Далее появляется окно Inheritance Picker, в котором отображаются все формы текущего проекта (рис. 2.27).

(рис 2.27) Окно выбора формы для наследования Inheritance Picker

В этом окне мы выбираем форму About. Обратите внимание на кнопку Browse, позволяющую искать формы для наследования в библиотеках dll. Нажимаем кнопку OK — в проекте появилась новая форма, содержащая все элементы управления формы About (рис. 2.28). Для завершения связей необходимо скомпилировать проект cнова — выбрав пункт главного меню Build/Rebuild Solution.

(рис 2.28) Форма IAbout, наследованная от формы About

При редактировании родительской формы изменения также касаются дочерней — добавьте, например, кнопку на форму About, и она появится на форме IAbout. Изменения же, производимые с дочерней формой, не затрагивают родительскую форму.

ToolBar и ImageList

Панели инструментов ToolBar содержат наборы кнопок, как правило, дублирующих пункты главного меню. В графических программах панели инструментов — основное средство работы. Откроем снова приложение Notepad C# и перетащим из окна ToolBox элемент управления ToolBar. На кнопках панели обычно располагаются иконки, поэтому, прежде чем мы начнем заниматься ими, нам следует подумать о рисунках. Добавим на форму элемент управления ImageList, применяемый для хранения рисунков, которые могут быть использованы для оформления приложения. Щелкнем в поле Images элемента ImageList (рис. 2.29).

(рис 2.29) Cвойство Images элемента ImageList

Добавим файлы иконок, последовательно нажимая кнопку Add и выбирая директорию — Code\Glava2\NotepadCSharp\Icon. Мы создадим кнопки панели инструментов, дублирующие действия пунктов меню New, Open, Save, Cut, Copy и Paste, поэтому вы можете выбрать подходящие изображения на своем компьютереДля создания иконки подходит даже редактор Paint. Для иконки панели инструментов размер рисунка должен быть 16х16 пикселей, сохраненный как 256-цветный рисунок. Для преобразования сохраненного рисунка, например, 1.bmp, просто смените его расширение на 1.ico.. В любом случае, подобрав иконки, завершаем работу с редактором ImageCollectionEditor, нажимая OK. Займемся теперь элементом ToolBar. Свойству Name установим значение toolBarMain, а в поле свойства ImageList выберем imageList1. Запустим редактор ToolBarButton Collection Editor для создания кнопок, щелкнув кнопку (…) в поле свойства Buttons (рис. 2.30).

(рис 2.30) Редактор ToolBarButton Collection Editor

Для добавления кнопок в этом редакторе тоже следует нажимать клавишу Add. Создадим шесть кнопок, установив для них следующие свойства:

NameImage IndexToolTipText
tbNew Create New
tbOpen Open
tbSave Save
tbCut Cut
tbCopy Copy
tbPaste Paste

Свойство Name устанавливает название кнопки для обращения к ней в коде. Свойство Image Index определяет изображение на кнопке, а в поле ToolTipText вводим текст всплывающей подсказки, которая будет появляться при наведении курсора на кнопку. Завершив работу с редактором кнопок в режиме дизайна формы frmmain, дважды щелкаем на ToolBar и переходим в код. Для создания функциональности кнопок связываем событие Click заданной кнопки c соответствующим обработчиком пунктов меню:

private void toolBarMain_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e)
    {
      //New
      if (e.Button.Equals(tbNew)) 
      {
        mnuNew_Click(this, new EventArgs());
      }
      //Open
      if (e.Button.Equals(tbOpen))
      {
        mnuOpen_Click(this, new EventArgs());
      }
      //Save
      if (e.Button.Equals(tbSave))
      {
        mnuSave_Click(this, new EventArgs());
      }
      //Cut
      if (e.Button.Equals(tbCut))
      {
        mnuCut_Click(this, new EventArgs());
      }
      //Copy
      if (e.Button.Equals(tbCopy))
      {
        mnuCopy_Click(this, new EventArgs());
      }
      //Paste
      if (e.Button.Equals(tbPaste))
      {
        mnuPaste_Click(this, new EventArgs());
      }
    }

Запускаем приложение. Кнопки панели инструментов дублируют пункты меню, а при наведении на них появляются подсказки (рис. 2.31).

(рис 2.31) Панель инструментов Toolbar

На диске, прилагаемом к книге, вы найдете приложение NotepadCSharp (Code\Glava2\NotepadCSharp). В конце этой лекции приводится листинг форм frmamin и blank.

Чтение и запись файлов

При изучении синтаксиса языка С# вы сталкивались с чтением и записью файлов. Если при рассмотрении обработчиков OpenFileDialog и SaveFileDialog вы не до конца поняли, как .NET Framework работает с файлами, давайте еще раз разберемся с этим понятием.

Файл — это совокупность данных, сохраненных на диске под общим названием. Когда мы открываем файл, он становится потоком, с которым мы можем осуществлять следующие операции:

  • считывать данные из потока;
  • записывать данные в поток;
  • производить поиск в потоке.
  • В С# все классы, которые представляют потоки, наследуются от абстрактного класса Stream. Класс Stream вместе со своими подклассами обеспечивает универсальное представление источников данных и архивов, абстрагированное от подробностей операционной системы. Классы потоков определены в пространстве имен System.IO.

    Чтение и запись текстовых файлов

    Для чтения и записи файлов, содержащих только текст, используются классы StreamReader и StreamWriter. Класс Stream Reader наследуется от класса TextReader, который считывает строки из стандартного текстового файла. Класс StreamWriter наследуется от класса TextWriter, записывающего символы в поток в специальном кодировании.

    Создайте новое консольное приложение и назовите его FileStream. Скомпилируйте приложение (Ctrl+F5). В папке проекта создайте текстовый документ 1.txt. Откройте его в блокноте (или в приложении Notepad C#) и напишите в нем любой текст на русском и английском языках, сохранив затем его в кодировке Unicode. Выведем теперь записанный текст в окно консольного приложения. Переходим в код FileStream и подключаем пространство имен:

    using System.IO;

    В методе Main добавляем код:

    [STAThread]
      static void Main(string[] args)
      {
    //Cоздаем экземпляр Chitat  класса StreamReader и указываем путь к файлу 
    и его название
        StreamReader Chitat = new StreamReader("1.txt");
        // Объявляем переменную A.
        string A;
        //В переменную А считываем содержимое файла
        A = Chitat.ReadToEnd();
        //Закрываем поток
        Chitat.Close();
        //Выводим переменную A в окно консольного приложения.
        Console.WriteLine(A);
      }

    Когда вы работаете с файлом, например, редактируете документ Microsoft Word, вы не сможете удалить его или переместить — потому что приложение занято и поток открыт. Только завершив работу с документом и, следовательно, закрыв поток, можно перемещать его или открывать с помощью других приложений. Аналогично, в этом примере мы закрываем поток, вызывая метод Close, освобождая тем самым задействованные системные ресурсы.

    Запускаем приложение. В консольном окне появляется текст, считанный из файла (рис. 2.32 ).

    (рис 2.32) Чтение текста из файла

    Добавим теперь объект класса StreamWriter, который будет записывать текст в файл:

    [STAThread]
      static void Main(string[] args)
        {
          //Cоздаем экземпляр Chitat  класса StreamReader и указываем путь к файлу и его название
          StreamReader Chitat = new StreamReader("1.txt");
          // Объявляем переменную A.
          string A;
          //В переменную А считываем содержимое файла
          A = Chitat.ReadToEnd();
          //Закрываем поток
          Chitat.Close();
          //Выводим переменную A в окно консольного приложения.
          Console.WriteLine(A);
          //Cоздаем экземпляр Pisat  класса StreamWriter
          StreamWriter Pisat = new StreamWriter("1.txt", true, System.Text.Encoding.Unicode);
          //Вызываем метод Write экземпляра Pisat
          Pisat.Write("Добавляем текст");
          //Закрываем поток
          Pisat.Close();
    
          // Для того, чтобы не переходить в директорию 
          //для просмотра файла вручную, выведем его еще раз
          StreamReader ChitatSnova = new StreamReader("1.txt");
          string B;
          B = ChitatSnova.ReadToEnd();
          ChitatSnova.Close();
          Console.WriteLine(B);
    
        }

    Метод Write добавил строку в файл (рис. 2.33):

    (рис 2.33) Чтение и запись текста из файла

    На диске, прилагаемом к книге, вы найдете приложение FileStream (Code\Glava2\ FileStream).

    Object Browser и Intel Sense

    В первой лекции мы рассматривали окно Object Browser. Использование этого окна зачастую может быть гораздо более удобным, чем поиск справочной информации в MSDN.

    В предыдущем примере, при создании экземпляра Pisat класса StreamWriter следует указывать параметры, передаваемые классу, — при использовании Intel Sense всплывающая подсказка указывает, какие именно параметры следует передавать (рис. 2.34).

    (рис 2.34) Группа из семи различных вариантов, предлагаемых Intel Sense. Для перемещения между вариантами щелкаем на кнопку со стрелкой — вариант 1 of 7

    Следуя подсказке, мы могли указать и передать такие параметры:

    StreamWriter Pisat = new StreamWriter("1.txt");

    В этом случае файл 1.txt перезаписывался и текстовая строка помещалась в пустой файл. Для добавления в файл нужно указывать булевскую переменную:

    StreamWriter Pisat = new StreamWriter("1.txt", true);

    В этом случае можно было бы добавлять в файл текст, содержащий только латинские буквы. Для корректной записи кириллицы следует указывать кодировку, например Unicode, что мы и сделали:

    StreamWriter Pisat = new StreamWriter("1.txt", true, System.Text.Encoding.Unicode);

    Возможны и другие варианты передаваемых параметров — например, указание размера передаваемого буфера в байтах.

    Предположим, мы не знаем, какую именно кодировку следует указывать. В самом деле, Intel Sense на рис. 2 не указывает, что именно кодировка Unicode принадлежит пространству имен System.Text.Encoding. Но мы можем просмотреть все доступные объекты и свойства этого пространства, используя окно Object Browser (рис. 2.35).

    (рис 2.35) Пространство имен System.Text.Encoding в окне Object Browser

    В части окна Members отображаются и другие доступные кодировки, например, ASCII.

    Проверка существования файла

    При редактировании файла требуется определять его наличие на жестком диске для выбора потом варианта сохранения — целиком или только внесенных изменений. Создайте новое консольное приложение и назовите его FileExists. Следующий код демонстрирует проверку файла на диске:

    using System.IO;
    …
    
    [STAThread]
        static void Main(string[] args)
        {
          //Выводим сообщение
          Console.WriteLine("Ведите название файла");
          // Создаем переменную nazvaniefayla, в которую
          //запишем название файла, введеное пользователем.
          string nazvaniefayla = Console.ReadLine();
          //Проверяем наличие файла (в директории bin/Debug)
          if (File.Exists(nazvaniefayla))
          {
            //Если файл существует, выводим его в консольное окно.
            StreamReader chitat = new StreamReader(nazvaniefayla);
            string A;
            A = chitat.ReadToEnd();
            chitat.Close();
            Console.WriteLine(A);
          }
          else 
          {
            //Если файл не найден, выводим окно сообщения
    Сonsole.WriteLine("Файл с указанным именем не существует. Сoздать его Y/N");
            //Считываем результат пользователя
            string rezultatvibora = Console.ReadLine();
            if ((rezultatvibora=="Y")||(rezultatvibora=="y"))
            {
              //Создаем новый файл.
              StreamWriter pisat = new StreamWriter(nazvaniefayla);
              Console.WriteLine("Введите текст");
              string vvedemtext = Console.ReadLine();
              pisat.WriteLine(vvedemtext);
              pisat.Close();
              Console.WriteLine("Файл создан");
            }
            else 
            {
              // Если пользователь отказался от создания файла, выводим сообщение.
              Console.WriteLine("Ничего не будет созданно");
            }
          }
          
    
    
        }

    На диске, прилагаемом к книге, вы найдете приложение FileExists (Code\Glava2\FileExists).

    Работа с файловой системой Windows

    В рассмотренном выше примере мы проверяли наличие файла в папке Bin/Debug, где находится само приложение. В практических случаях подобная директория не может нас устраивать — требуется просматривать заданные каталоги и папки. В C# можно работать с файлами и каталогами, используя классы DirectoryInfo и FileInfo, предоставляющие всю информацию о файлах и папках.

    Класс DirectoryInfo обычно применяется для таких операций, как копирование, перемещение, переименование, создание и удаление папок. Этот класс содержит метод GetFiles(), который возвращает список файлов из текущего каталога.

    Класс FileInfo предоставляет методы для создания, копирования, удаления, перемещения и открытия файлов, а также является вспомогательным классом при создании объекта FileStream.

    Cоздайте новое консольное приложение и назовите его DirectoryInfo. Следующий простой код выводит список всех файлов, содержащихся на диске С:

    using System;
    using System.IO;
    
    namespace DirectoryInformation
    {
      
      class Class1
      {
      
        [STAThread]
        static void Main(string[] args)
        {
          //Создаем экземпляр di класса DirectoryInfo и указываем диск С в качестве директории
          DirectoryInfo directoriya = new DirectoryInfo("C:\\");
          // Получаем список всех папок, содержащихся на диске С:\
          DirectoryInfo[]  poddirectoriya = directoriya.GetDirectories();
          
          foreach (DirectoryInfo dd in poddirectoriya)
          {
            //Проверяем наличие доступа к каталогу
            if(dd.Attributes == FileAttributes.Directory)
            {
              // Получаем список всех файлов из каталога
              FileInfo[] spisokfile= dd.GetFiles();
              foreach (FileInfo fi in spisokfile)
              {
                //Выводим список файлов
                Console.WriteLine(fi.ToString());
              }
            }
          }
        }
        }
      }

    На диске, прилагаемом к книге, вы найдете приложение DirectoryInfo — Code\Glava2\DirectoryInfo.

    Чтение и запись двоичных файлов

    Для более общего способа чтения и записи файлов используются классы BinaryReader и BinaryWriter, предназначенные для чтения двоичных данных. Любой файл в конечном итоге представляет собой набор двоичных данных, следовательно, эти данные можно считывать и записывать во внутреннем, двоичном формате. Класс BinaryWriter представляет собой оболочку для байтового потока, управляющей записью двоичных данных. Класс BinaryReader является оболочкой, содержащей байтовый поток, и выполняет чтение двоичных данных.

    Рассмотрим приложение, позволяющее считывать файл формата MP3 и записывать фрагмент этого файла. Создайте новое консольное приложение и назовите его BinaryReadWrite. В папку bin/Debug поместите любой музыкальный файл формата MP3, имеющийся на вашем компьютере.

    Подключаем пространство имен:

    using System.IO;

    Добавляем код:

    [STAThread]
        static void Main(string[] args)
        {
          //Добавляем блок обработки исключений 
          try
          {
            //Создаем новый поток Chtenie  и указываем  файл для чтения — music.mp3
            FileStream Chtenie = new FileStream("music.mp3", FileMode.Open);
    
            //Создаем экземпляр  br класса BinaryReader и 
    //связываем  его с потоком Chtenie
            BinaryReader br = new BinaryReader(Chtenie);
    
            // Создаем массив типа байтов и 
            //передаем ему размер в байтах – (например, 1000000) записываемого файла
            byte[] n = br.ReadBytes(1000000);
            //Закрываем поток
            br.Close();
            //Создаем новый поток Zapis  и указываем  название 
    //файла для записи sozdanajamuzika.mp3
            FileStream Zapis = new FileStream("newmusic.mp3", FileMode.CreateNew, FileAccess.Write);
            //Создаем экземпляр  wr класса BinaryWriter и 
    //связываем  его с потоком Zapis
            BinaryWriter wr = new BinaryWriter(Zapis);
            // Можно связывать объект wr с потоком создавая его без названия:
    //BinaryWriter wr = new BinaryWriter(new FileStream("sozdanajamuzika.mp3", //FileMode.CreateNew, FileAccess.Write));
            //Передаем в поток массив байтов n
            wr.Write(n);
            //Закрываем поток
            br.Close();
          }
          catch (Exception ex)
          {
            Console.WriteLine(ex.Message);
          }
          
        }

    Запускаем приложение. Если все сработает без ошибок — на экране появится станадртное сообщение Press any key to continue, в противном случае — поскольку мы включили блок исключений — выйдет диагностическое сообщение. Перейдем в папку bin/Debug. В ней появился файл newmusic.mp3, прослушать который мы можем с помощью любого музыкального проигрывателя.

    Рассмотрим перечисления в конструкторах FileStream. Параметр FileMode определяет, каким образом открывается файл (рис. 2.36):

    (рис 2.36) Перечисления FileMode

    Значения перечисления FileMode указаны в таблице 2.4.

    Значение перечисления FileMode
    ЗначениеОписание
    Append Если файл существует, он открывается, если нет — создается новый. Данные добавляются в конец файла
    Create Создается новый выходной файл; если файл с таким названием существует, он перезаписывается
    CreateNew Создается новый выходной файл; если файл с таким названием уже существует, генерируется исключение IOException
    Open Открывается существующий файл; если файла с таким названием не существует, генерируется исключение FileNotFoundException
    OpenOrCreate Открывается существующий файл; если файл не существует, он создается
    Truncate Открывается существующий файл, но его длина усекается до нуля

    Параметр FileAccess определяет для пользователя разрешение на доступ к файлу (рис. 2.37):

    (рис 2.37) Перечисления FileAccess

    Значения перечисления FileAccess указаны в таблице 2.5.

    Значение перечисления FileAccess
    ЗначениеОписание
    Read Доступ на чтение из файла
    ReadWrite Разрешается чтение из файла и запись данных в файл
    Write Доступ для записи в файл

    Дополнительно для управления режимом доступа при одновременной работе с файлами нескольких потоков используется перечисление FileShare (рис. 2.38). Например, при создании потока для чтения файла мы могли полностью определить все параметры следующим образом:

    FileStream Chtenie = new FileStream("music.mp3", FileMode.Open, 
    FileAccess.Read, FileShare.Read);
    (рис 2.38) Перечисления FileShare

    Значения перечисления FileShare указаны в таблице 2.6.

    Значение перечисления FileShare
    ЗначениеОписание
    Inheritable Дает доступ к файлам, образованных текущим потоком
    None Запрещает совместное использование текущего файла. Пока файл открыт, доступ к нему будет блокирован
    Read Позволяет открывать текущей файл для чтения
    ReadWrite Позволяет открывать текущий файл для чтения или записи
    Write Позволяет открывать текущий файл для записи

    На диске, прилагаемом к книге, вы найдете приложение BinaryReadWrite (Code\Glava2\ BinaryReadWrite).

    Запуск программ из приложения

    Иногда в приложениях требуется запускать программы, входящие в состав операционной системы или установленные на данном компьютере. Создайте новое Windows-приложение и назовите его RunProgramm. На появившейся форме расположите три кнопки:

    button1, СвойстваЗначения свойств
    Name btnSystemInfo
    Size 168;23
    Text Информация о системе
    button2, СвойстваЗначения свойств
    Name btnRegEdit
    Size 168;23
    Text Редактор реестра
    button3, СвойстваЗначения свойств
    Name btnExplorer
    Size 168;23
    Text Проводник

    Располагаем кнопки в центре формы и выравниваем их, используя пункт меню Format. В коде формы подключаем пространство имен Diagnostics, позволяющее запускать приложения:

    using System.Diagnostics;

    Добавляем обработчики кнопок:

    private void btnSystemInfo_Click(object sender, System.EventArgs e)
        {
          //Добавляем блок обработки исключений — по разным причинам (например, 
          //система установлена на другом диске) приложение может быть недоступным.
          try
          {
            //Создаем новый экземпляр SysInfo класса Process
            Process SysInfo = new Process();
            // Если приложение не будет работать корректно — вызываем диалоговое окно ошибок.
            SysInfo.StartInfo.ErrorDialog = true;
            //Указываем путь к  запускаемому приложению "Сведения о системе"
            SysInfo.StartInfo.FileName = "C:\\Program Files\\Common Files\\Microsoft Shared\\MSInfo\\msinfo32.exe";
            //Запускаем SysInfo
            SysInfo.Start();
          }
          catch (Exception ex)
          {
            MessageBox.Show (ex.Message);
          }
        }
    
        private void btnRegEdit_Click(object sender, System.EventArgs e)
        {
          try
          {
            Process RegEdit = new Process();
            RegEdit.StartInfo.ErrorDialog = true;
            //Указываем путь к  запускаемому приложению "Редактор реестра"
            RegEdit.StartInfo.FileName = "C:\\Windows\\regedit.exe";
            RegEdit.Start();
          }
          catch (Exception ex)
          {
            MessageBox.Show (ex.Message);
          }
        
        }
    
        private void btnExplorer_Click(object sender, System.EventArgs e)
        {
          try
          {
            Process Explorer = new Process();
            Explorer.StartInfo.ErrorDialog = true;
            //Указываем путь к  запускаемому приложению "Проводник"
            Explorer.StartInfo.FileName = "C:\\Windows\\explorer.exe";
            Explorer.Start();
          }
          catch (Exception ex)
          {
            MessageBox.Show (ex.Message);
          }
        
        }

    Запускаем приложение. При нажатии на кнопки запускаются соответствующие приложения (рис. 2.39).

    (рис 2.39) Запуск программ из приложения RunProgramm

    Для запуска других программ или отдельных исполняемых файлов вашего приложения следует указывать их название и адрес, по которому они будут отыскиваться на клиентском компьютере.

    На диске, прилагаемом к книге, вы найдете приложение RunProgramm (Code\Glava2\RunProgramm).

    Листинг форм frmmain и blank проекта NotepadCSharp

    using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Data;
    using System.IO;
    
    namespace NotepadCSharp
    {
      
      public class frmmain : System.Windows.Forms.Form
      {
        private System.Windows.Forms.MainMenu mainMenu1;
        private System.Windows.Forms.MenuItem menuItem5;
        private System.Windows.Forms.MenuItem menuItem12;
        private System.Windows.Forms.MenuItem mnuFile;
        private System.Windows.Forms.MenuItem mnuNew;
        private System.Windows.Forms.MenuItem mnuOpen;
        private System.Windows.Forms.MenuItem mnuSave;
        private System.Windows.Forms.MenuItem mnuExit;
        private System.Windows.Forms.MenuItem mnuEdit;
        private System.Windows.Forms.OpenFileDialog openFileDialog1;
        private System.Windows.Forms.MenuItem mnuCut;
        private System.Windows.Forms.MenuItem mnuCopy;
        private System.Windows.Forms.MenuItem mnuPaste;
        private System.Windows.Forms.MenuItem mnuDelete;
        private System.Windows.Forms.MenuItem mnuSelectAll;
        private System.Windows.Forms.MenuItem mnuHelp;
        private System.Windows.Forms.MenuItem mnuAbout;
      
    
        private System.Windows.Forms.MenuItem mnuArrangeIcons;
        private System.Windows.Forms.MenuItem mnuCascade;
        private System.Windows.Forms.MenuItem mnuTileHorizontal;
        private System.Windows.Forms.MenuItem mnuTileVertical;
        private System.Windows.Forms.MenuItem mnuWindow;
        private int openDocuments = 0;
        private System.Windows.Forms.MenuItem mnuFormat;
        private System.Windows.Forms.SaveFileDialog saveFileDialog1;
        private System.Windows.Forms.MenuItem mnuSaveAs;
        private System.Windows.Forms.FontDialog fontDialog1;
        private System.Windows.Forms.MenuItem mnuFont;
        private System.Windows.Forms.MenuItem mnuColor;
        private System.Windows.Forms.ColorDialog colorDialog1;
        private System.Windows.Forms.MenuItem menuItem1;
        private System.Windows.Forms.MenuItem mnuFind;
        private System.Windows.Forms.ToolBarButton tbNew;
        private System.Windows.Forms.ToolBarButton tbOpen;
        private System.Windows.Forms.ToolBarButton tbSave;
        private System.Windows.Forms.ToolBarButton tbCut;
        private System.Windows.Forms.ToolBarButton tbCopy;
        private System.Windows.Forms.ToolBarButton tbPaste;
        private System.Windows.Forms.ImageList imageList1;
        private System.Windows.Forms.ToolBar toolBarMain;
        private System.ComponentModel.IContainer components;
      
    
        public frmmain()
        {
          //
          // Required for Windows Form Designer support
          //
          InitializeComponent();
          mnuSave.Enabled = false;
    
          //
          // TODO: Add any constructor code after InitializeComponent call
          //
        }
    
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
          if( disposing )
          {
            if (components != null) 
            {
              components.Dispose();
            }
          }
          base.Dispose( disposing );
        }
    
        region Windows Form Designer generated code
        
    
        [STAThread]
        static void Main() 
        {
          Application.Run(new frmmain());
        }
    
        
        private void mnuNew_Click(object sender, System.EventArgs e)
        {
          //Создаем новый экземпляр формы  frm
          blank  frm = new blank();
    
          frm.DocName = "Untitled " + ++openDocuments;
          //Указываем, что родительским контейнером 
          //нового экземпляра будет эта, главная форма.
          frm.MdiParent = this;
          frm.Text = frm.DocName;
          //Вызываем форму
          frm.Show();
        }
    
        private void mnuArrangeIcons_Click(object sender, System.EventArgs e)
        {
          this.LayoutMdi(MdiLayout.ArrangeIcons);
        }
    
      
    
        private void mnuCascade_Click(object sender, System.EventArgs e)
        {
          this.LayoutMdi(MdiLayout.Cascade);
        }
    
        private void mnuTileHorizontal_Click(object sender, System.EventArgs e)
        {
          this.LayoutMdi(MdiLayout.TileHorizontal);
        }
    
        private void mnuTileVertical_Click(object sender, System.EventArgs e)
        {
          this.LayoutMdi(MdiLayout.TileVertical);
        }
    
        private void mnuCut_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.Cut();
        }
    
        private void mnuCopy_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.Copy();
        }
    
        private void mnuPaste_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.Paste();
        }
    
        private void mnuDelete_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.Delete();
        }
    
        private void mnuSelectAll_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.SelectAll();
        }
    
        private void mnuOpen_Click(object sender, System.EventArgs e)
        {
          //Можно программно задавать доступные для обзора расширения файлов. 
          //openFileDialog1.Filter = "Text Files (*.txt)|*.txt|All Files(*.*)|*.*";
          
          
          //Если выбран диалог открытия файла, выполняем условие
          if (openFileDialog1.ShowDialog() == DialogResult.OK) 
          {
            //Создаем новый документ
            blank frm = new blank();
            //Вызываем метод Open формы blank
            frm.Open(openFileDialog1.FileName);
            //Указываем, что родительской формой является форма frmmain
            frm.MdiParent = this;
            //Присваиваем переменной DocName имя открываемого файла
            frm.DocName = openFileDialog1.FileName;
            //Свойству Text формы присваиваем переменную DocName
            frm.Text = frm.DocName;
            //Вызываем форму frm
            frm.Show();  
            mnuSave.Enabled = true;
          }
        
    
        }
    
        private void mnuSave_Click(object sender, System.EventArgs e)
        {
          //Переключаем фокус на данную форму.
          blank frm = (blank)this.ActiveMdiChild;
          //Вызываем метод Save формы blank
          frm.Save(frm.DocName);
          frm.IsSaved = true;
          
          
        }
    
        private void mnuSaveAs_Click(object sender, System.EventArgs e)
        {
          mnuSave.Enabled = true;
          //Можно программно задавать доступные для обзора расширения файлов 
          //openFileDialog1.Filter = "Text Files (*.txt)|*.txt|All Files(*.*)|*.*";
    
          //Если выбран диалог открытия файла, выполняем условие
          if (saveFileDialog1.ShowDialog() == DialogResult.OK) 
          {
            //Переключаем фокус на данную форму.
            blank frm = (blank)this.ActiveMdiChild;
            //Вызываем метод Save формы blank
            frm.Save(saveFileDialog1.FileName);
            //Указываем, что родительской формой является форма frmmain
            frm.MdiParent = this;
            //Присваиваем переменной FileName имя сохраняемого файла
            frm.DocName = saveFileDialog1.FileName;
            //Свойству Text формы присваиваем переменную DocName
            frm.Text = frm.DocName;    
            frm.IsSaved = true;
            
        
          }
    
        }
    
        private void mnuExit_Click(object sender, System.EventArgs e)
        {
          this.Close();
        }
    
        private void mnuFont_Click(object sender, System.EventArgs e)
        {
          //Переключаем фокус на данную форму.
          blank  frm = (blank)this.ActiveMdiChild;
          //Указываем, что родительской формой является форма frmmain  
          frm.MdiParent = this;
          //Вызываем диалог
          fontDialog1.ShowColor = true;
          //Связываем свойства SelectionFont и SelectionColor элемента RichTextBox 
          //с соответствующими свойствами диалога
          fontDialog1.Font = frm.richTextBox1.SelectionFont;
          fontDialog1.Color = frm.richTextBox1.SelectionColor; 
          //Если выбран диалог открытия файла, выполняем условие
          if (fontDialog1.ShowDialog() == DialogResult.OK)
          {
            frm.richTextBox1.SelectionFont = fontDialog1.Font;
            frm.richTextBox1.SelectionColor = fontDialog1.Color; 
          }
          //Показываем форму
          frm.Show();
        }
    
        private void mnuColor_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.MdiParent = this;
          colorDialog1.Color = frm.richTextBox1.SelectionColor; 
          
          if (colorDialog1.ShowDialog() == DialogResult.OK)
          {
            frm.richTextBox1.SelectionColor = colorDialog1.Color; 
          }
    
          frm.Show();
        }
    
        
        private void mnuFind_Click(object sender, System.EventArgs e)
        {
          //Создаем новый экземпляр формы FindForm
          FindForm frm = new FindForm();
          //Если выбран результат DialogResult.Cancel, закрываем форму (до этого 
          //мы использовали DialogResult.OK
          if(frm.ShowDialog(this) == DialogResult.Cancel) return;
          ////Переключаем фокус на данную форму.
          blank form = (blank)this.ActiveMdiChild;
          ////Указываем, что родительской формой является форма frmmain  
          form.MdiParent = this;
          //Вводим переменную для поиска в определенной части текста —
          //поиск слова будет осуществляться от текущей позиции курсора
          int start = form.richTextBox1.SelectionStart;
          //Вызываем предопределенный метод Find элемента richTextBox1.
          form.richTextBox1.Find(frm.FindText, start, frm.FindCondition);
        }
    
        private void mnuAbout_Click(object sender, System.EventArgs e)
        {
          //Создаем новый экземпляр формы  About
          About  frm = new About();
          frm.Show();
        }
    
        private void toolBarMain_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e)
        {
          //New
          if (e.Button.Equals(tbNew)) 
          {
            mnuNew_Click(this, new EventArgs());
          }
          //Open
          if (e.Button.Equals(tbOpen))
          {
            mnuOpen_Click(this, new EventArgs());
          }
          //Save
          if (e.Button.Equals(tbSave))
          {
            mnuSave_Click(this, new EventArgs());
          }
          //Cut
          if (e.Button.Equals(tbCut))
          {
            mnuCut_Click(this, new EventArgs());
          }
          //Copy
          if (e.Button.Equals(tbCopy))
          {
            mnuCopy_Click(this, new EventArgs());
          }
          //Paste
          if (e.Button.Equals(tbPaste))
          {
            mnuPaste_Click(this, new EventArgs());
          }
        }
      }
    }
    using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.IO;
    
    namespace NotepadCSharp
    {
      /// <summary>
      /// Summary description for blank.
      /// </summary>
      public class blank : System.Windows.Forms.Form
      {
        public System.Windows.Forms.RichTextBox richTextBox1;
        public string DocName = "";
        private string BufferText = "";
        private System.Windows.Forms.ContextMenu contextMenu1;
        private System.Windows.Forms.MenuItem cmnuCut;
        private System.Windows.Forms.MenuItem cmnuCopy;
        private System.Windows.Forms.MenuItem cmnuPaste;
        private System.Windows.Forms.MenuItem cmnuDelete;
        private System.Windows.Forms.MenuItem menuItem6;
        private System.Windows.Forms.MenuItem cmnuSelectAll;
        public bool IsSaved = false;
        private System.Windows.Forms.StatusBar statusBar1;
        private System.Windows.Forms.StatusBarPanel sbAmount;
        private System.Windows.Forms.StatusBarPanel sbTime;
        
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;
    
    
    
        public blank()
        {
          
          InitializeComponent();
          //Свойству Text панели sbTime устанавливаем системное время, 
          // конвертировав его в тип String
          sbTime.Text = Convert.ToString(System.DateTime.Now.ToLongTimeString());
          //В тексте всплывающей подсказки  выводим текущую дату
          sbTime.ToolTipText = Convert.ToString(System.DateTime.Today.ToLongDateString());
        }
    
        
        protected override void Dispose( bool disposing )
        {
          if( disposing )
          {
            if(components != null)
            {
              components.Dispose();
            }
          }
          base.Dispose( disposing );
        }
    
        region Windows Form Designer generated code
        
    
        // Вырезание текста
        public void Cut()
        {
          this.BufferText = richTextBox1.SelectedText;
          richTextBox1.SelectedText = "";
        }
    
        // Копирование текста
        public void Copy()
        {
          this.BufferText = richTextBox1.SelectedText;
        }
    
        // Вставка
        public void Paste()
        {
          richTextBox1.SelectedText = this.BufferText;
        }
    
        // Выделение всего текста — используем свойство SelectAll элемента управления RichTextBox 
        public void SelectAll()
        {
          richTextBox1.SelectAll(); 
        }
    
        // Удаление.
        public void Delete()
        {
          richTextBox1.SelectedText = "";  
          this.BufferText = "";
        }
        //Создаем метод Open, в качестве параметра объявляем строку адреса  файла.
        public void Open(string OpenFileName)
        {
          //Если файл не выбран, возвращаемся назад (появится встроенное предупреждение)
          if (OpenFileName == "") 
          {
            return;
          }
          else
          {
            //Создаем новый объект StreamReader и передаем ему переменную OpenFileName
            StreamReader sr = new StreamReader(OpenFileName);
            //Читаем весь файл и записываем его в richTextBox1
            richTextBox1.Text = sr.ReadToEnd();
            // Закрываем поток
            sr.Close();
            //Переменной DocName присваиваем адресную строку.
            DocName = OpenFileName;
          }      
        }
    
        private void cmnuCut_Click(object sender, System.EventArgs e)
        {
          Cut();
        }
    
        private void cmnuCopy_Click(object sender, System.EventArgs e)
        {
          Copy();
        }
    
        private void cmnuPaste_Click(object sender, System.EventArgs e)
        {
          Paste();
        }
    
        private void cmnuDelete_Click(object sender, System.EventArgs e)
        {
          Delete();
        }
    
        private void cmnuSelectAll_Click(object sender, System.EventArgs e)
        {
          SelectAll();
        }
        //Создаем метод Save, в качестве параметра объявляем строку адреса  файла.
        public void Save(string SaveFileName)
        {
          //Если файл не выбран, возвращаемся назад (появится встроенное предупреждение)
          if (SaveFileName == "") 
          {
            return;
          }
          else
          {
            //Создаем новый объект StreamWriter и передаем ему переменную OpenFileName
            StreamWriter sw = new StreamWriter(SaveFileName);
            //Содержимое richTextBox1 записываем в файл
            sw.WriteLine(richTextBox1.Text);
            //Закрываем поток
            sw.Close();
            //Устанавливаем в качестве имени документа название сохраненного файла
            DocName = SaveFileName;
          }
        }
    
        private void blank_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
          //Если переменная IsSaved имеет значение true, т. е. документ новый документ 
          //был сохранен (Save As) или в открытом документе были сохранены изменения (Save), то выполняется условие
          if(IsSaved ==true)
            //Появляется диалоговое окно, предлагающее сохранить документ.
            if  (MessageBox.Show("Do you want save changes in " + this.DocName + "?", 
              "Message", MessageBoxButtons.YesNo, 
              MessageBoxIcon.Question) == DialogResult.Yes)
              //Если была нажата  кнопка Yes, вызываем метод Save
            {
              this.Save(this.DocName); 
            }    
         
        
        }
    
        private void richTextBox1_TextChanged(object sender, System.EventArgs e)
        {
          //Свойству Text панели sbAmount устанавливаем надпись "Аmount of symbols " 
          //и длину  текста в RichTextBox.
          sbAmount.Text = "Аmount of symbols " + richTextBox1.Text.Length.ToString(); 
        }      
        }
      }
    Страницы:
    Для работы с данной лекцией используйте примеры.

    Элементы управления — это компоненты, обеспечивающие взаимодействие между пользователем и программой. Среда Visual Studio.NET предоставляет большое количество элементов, которые можно сгруппировать по нескольким функциональным группам.

    Группа командных объектов

    Элементы управления Button, LinkLabel, ToolBar реагируют на нажатие кнопки мыши и немедленно запускают какое-либо действие. Наиболее распространенная группа элементов.

    Группа текстовых объектов

    Большинство приложений предоставляют возможность пользователю вводить текст и, в свою очередь, выводят различную информацию в виде текстовых записей. Элементы TextBox, RichTextBox принимают текст, а элементы Label, StatusBar выводят ее. Для обработки введенного пользователем текста, как правило, следует нажать на один или несколько элементов из группы командных объектов.

    Группа переключателей

    Приложение может содержать несколько предопределенных вариантов выполнения действия или задачи; элементы управления этой группы предоставляют возможность выбора пользователю. Это одна из самых обширных групп элементов, в которую входят ComboBox, ListBox, ListView, TreeView, NumericUpDown и многие другие.

    Группа контейнеров

    С элементами этой группы действия приложения практически никогда не связываются, но они имеют большое значение для организации других элементов управления, их группировки и общего дизайна формы. Как правило, элементы этой группы, расположенные на форме, служат подложкой кнопкам, текстовым полям, спискам — поэтому они и называются контейнерами. Элементы Panel, GroupBox, TabControl, кроме всего прочего, разделяют возможности приложения на логические группы, обеспечивая удобство работы.

    Группа графических элементов

    Даже самое простое приложение Windows содержит графику — иконки, заставку, встроенные изображения. Для размещения и отображения их на форме используются элементы для работы с графикой — Image List, Picture Box.

    Диалоговые окна

    Выполняя различные операции с документом — открытие, сохранение, печать, предварительный просмотр, — мы сталкиваемся с соответствующими диалоговыми окнами. Разработчикам .NET не приходится заниматься созданием окон стандартных процедур: элементы OpenFileDialog, SaveFile Dialog, ColorDialog, PrintDialog содержат уже готовые операции.

    Группа меню

    Многие пользователи настраивают интерфейс приложений на свой вкус: одним нравится наличие определенных панелей инструментов, другим – индивидуальное расположение окон. Но в любом приложении будет присутствовать меню, содержащее в себе доступ ко всем возможностям и настройкам приложения. Элементы MainMenu, ContextMenu представляют собой готовые формы для внесения заголовков и пунктов меню.

    Практическая работа с различными элементами управления будет рассмотрена в этой и последующих лекциях.

    Создание главного меню

    Большинство Windows-приложений оснащено главным меню, которое представляет собой иерархическую структуру выполняемых функций и команд. Практически все функции, которые можно осуществить при помощи элементов управления, имеют свой аналог в виде пункта меню.

    Для создания главного меню используется элемент управления MainMenu, расположенный на панели инструментов ToolBox. Создайте новое приложение и назовите его NotepadCSharp. Установите следующие свойства формы:

    Form1, форма, свойствоЗначение
    Name frmmain
    Icon Code\Glava2\NotepadCSharp\Icon\README.ICO
    Text Notepad C#
    WindowState Maximized

    Перетаскиваем элемент управления на форму. Нам предстоит заполнить строки меню следующими пунктами (рис. 2.1):

    (рис 2.1) Пункты главного меню приложения Notepad C#

    Каждый пункт главного меню имеет свое окно свойств, в котором, подобно другим элементам управления, задаются значения свойств Name и Text (рис. 2.2). В поле Text перед словом New стоит знак — так называемый амперсанд, указывающий, что N должно быть подчеркнута и будет частью встроенного клавиатурного интерфейса Windows. Когда пользователь на клавиатуре нажимает клавишу Alt и затем N, выводится подменю New.

    (рис 2.2) Свойства пункта меню New

    В Windows есть еще интерфейс для работы с так называемыми быстрыми клавишами, или акселераторами. Сочетание клавиш указывают из перечисления Shortcut. Следует назначать стандартным пунктам общепринятые сочетания клавиш. Горизонтальная разделительная линия используется в тех случаях, когда надо визуально отделить сходные группы задач; для ее появления в свойстве Text пункта меню просто вводим знак тире. Для использования пунктов меню в коде, им также назначают имена (свойство Name), которые особенно важны, т.к. пунктов меню обычно бывает много. Свойства пунктов меню в приложении Notepad C# приводятся в таблице 2.1.

    Пункты главного меню приложения Notepad C#
    NameTextShortcut
    mnuFile File
    mnuNew New CtrlN
    mnuOpen Open CtrlO
    mnuSave Save CtrlS
    menuItem5 -
    mnuExit Exit AltF4
    mnuEdit Edit
    mnuCut Cut CtrlX
    mnuCopy Copy CtrlC
    mnuPaste Paste CtrlV
    mnuDelete Delete Del
    mnuSelectAll SelectAll CtrlA
    mnuFormat Format
    mnuFont Font…
    mnuColor Color…
    mnuWindow Window
    mnuArrangeIcons Arrange Icons
    mnuCascade Cascade
    mnuTileHorizontal Tile Horizontal
    mnuTileVertical Tile Vertical
    mnuHelp ?
    mnuAbout About Programm...

    Вы можете самостоятельно выбрать сочетания клавиш, не указанные в табл. 2.1, для соответствующих пунктов меню.

    Создание MDI-приложений

    Такие программы, как блокнот и Microsoft Paint, относятся к SDI (Single — Document Interface) приложениям, способным работать только с одним документом. Другие, такие как Microsoft Word или Adobe Photoshop, поддерживают работу сразу с несколькими документами и называются MDI (Multiple — Document Interface) приложениями (рис. 2.3 и 2.4).

    (рис 2.4) SDI приложение Microsoft Paint(рис 2.3) MDI-приложение Adobe Photoshop

    В MDI-приложениях главная форма содержит в себе несколько документов, каждый из которых является холстом в графических программах или полем для текста в редакторах. Продолжим работу над приложением Notepad C#. В окне Solution Explorer щелкаем правой кнопкой на имени проекта и в появившемся контекстном меню выбираем Add/ Add Windows Form… . В появившемся окне называем форму – blank.cs. В нашем проекте появилась новая форма — будем называть ее дочерней. В режиме дизайна перетаскиваем на нее элемент управления RichTextBox: в отличие от элемента textBox, размер содержимого текста в нем не ограничивается 64 Кб; кроме того, RichTextBox позволяет редактировать цвет текста, добавлять изображения. Свойству Dock этого элемента устанавливаем значение Fill (рис. 2.5):

    (рис 2.5) Свойство Dock элемента RichTextBox

    Переходим в режим дизайна формы Frmmain и устанавливаем свойству IsMdiContainer значение true. Цвет формы при этом становится темно-серым. Новые документы будут у нас появляться при нажатии пункта меню New (которому мы установили сочетание клавиш Ctrl+N), поэтому дважды щелкаем в этом пункте и переходим в код:

    private void mnuNew_Click(object sender, System.EventArgs e)
      {
        //Создаем новый экземпляр формы  frm
        blank  frm = new blank();      
        //Указываем, что родительским контейнером 
        //нового экземпляра будет эта, главная форма.
        frm.MdiParent = this;      
        //Вызываем форму
        frm.Show();
      }

    Запускаем приложение. Теперь при нажатии клавиш Ctrl+N или выборе пункта меню New появляется несколько окон, расположенных каскадом. Однако заголовок у них всех одинаковый — blank. При создании нескольких документов, например в Microsoft Word, они называются ДокументN, где N — номер документа. Переключаемся в код формы blank, и в классе blank объявляем переменную DocName:

    public string DocName = "";

    Переключаемся в код формы frmmain и в классе frmmain объявляем переменную openDocuments:

    private int openDocuments = 0;

    Присваиваем переменной DocName часть названия по шаблону, в который включен счетчик числа открываемых документов, затем это значение передаем свойству Text создаваемой формы frm:

    private void mnuNew_Click(object sender, System.EventArgs e)
      {
        blank  frm = new blank();
        frm.DocName = "Untitled " + ++openDocuments;
        frm.Text = frm.DocName;
        frm.MdiParent = this;
        frm.Show();
      }

    Запускаем приложение. Теперь новые документы содержат разные заголовки (рис. 2.6).

    (рис 2.6) Новые документы имеют упорядоченные названия

    Перечисление MdiLayout

    При работе с несколькими документами в MDI-приложениях удобно упорядочивать их на экране. Можно, конечно, распределять формы вручную, но при работе с большим количеством документов это представляется затруднительным. В пункте меню Window реализуем процедуру выравнивания окон.

    Создаем обработчиков:

    private void mnuArrangeIcons_Click(object sender, System.EventArgs e)
      {
      this.LayoutMdi(MdiLayout.ArrangeIcons);
      }
    
    private void mnuCascade_Click(object sender, System.EventArgs e)
      {
        this.LayoutMdi(MdiLayout.Cascade);
      }
    
    private void mnuTileHorizontal_Click(object sender, System.EventArgs e)
      {
        this.LayoutMdi(MdiLayout.TileHorizontal);
      }
    
    private void mnuTileVertical_Click(object sender, System.EventArgs e)
      {
        this.LayoutMdi(MdiLayout.TileVertical);
      }

    Метод LayoutMdi содержит перечисление MdiLayout, содержащее четыре члена. ArrangeIcons переключает фокус на выбранную форму, в свойстве MdiList пункта меню ArrangeIcons устанавливаем также значение true. При открытии нескольких новых документов окна располагаются каскадом (рис. 2.6), их можно расположить горизонтально — значение TileHorizontal или вертикально — значение TileVertical, а затем снова вернуть каскадное расположение — Cascade (рис. 2.7):

    (рис 2.7) Расположение окон TileHorizontal и пункт меню ArrangeIcons

    Вырезание, копирование и вставка текстовых фрагментов

    С приложением работать будет удобней, если при создании нового документа он сразу будет занимать всю область главной формы. Для этого установим свойство WindowState формы blank Maximized. Теперь приступим к созданию обработчиков для стандартных операций вырезания, копирования и вставки. Элемент управления RichTextBox имеет свойство SelectedText, которое содержит выделенный фрагмент текста. На основании этого свойства и будут реализованы действия по работе с текстом. В коде формы blank объявляем переменную BufferText, в которой будет храниться буферизованный фрагмент текста:

    private string BufferText = "";

    Далее создаем соответствующие методы:

    // Вырезание текста
      public void Cut()
        {
          this.BufferText = richTextBox1.SelectedText;
          richTextBox1.SelectedText = "";
        }
    
      // Копирование текста
      public void Copy()
        {
          this.BufferText = richTextBox1.SelectedText;
        }
    
      // Вставка
      public void Paste()
        {
          richTextBox1.SelectedText = this.BufferText;
        }
    
      // Выделение всего текста — используем свойство SelectAll элемента управления RichTextBox 
      public void SelectAll()
        {
          richTextBox1.SelectAll(); 
        }
    
      // Удаление
        public void Delete()
        {
          richTextBox1.SelectedText = "";  
          this.BufferText = "";
      }

    Переключаемся в режим дизайна формы и создаем обработчиков для пунктов меню:

    private void mnuCut_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.Cut();
        }
    
    private void mnuCopy_Click(object sender, System.EventArgs e)
        {
        blank frm = (blank)this.ActiveMdiChild;
          frm.Copy();
        }
    
    private void mnuPaste_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.Paste();
        }
    
    private void mnuDelete_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.Delete();
        }
    
    private void mnuSelectAll_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.SelectAll();
        }

    Свойство ActiveMdiChild переключает фокус на текущую форму, если их открыто несколько, и вызывает один из методов, определенных в дочерней форме. Запускаем приложение. Теперь мы можем выполнять все стандартные операции с текстом.

    Контекстное меню

    Контекстное меню, дублирующее некоторые действия основного меню, — не самый быстрый способ работы с программой, но самый привычный для пользователя. Элемент управления TextBox содержит в себе простейшее контекстное меню, дублирующее действия подменю Edit. Для того чтобы убедиться в этом, достаточно нанести этот элемент управления на форму и запустить приложение (рис. 2.8):

    (рис 2.8) Контекстное меню элемента TextBox

    В нашем приложении Notepad C# в качестве текстового элемента мы используем RichTextBox. Добавим элемент управления contextMenu из окна ToolBox на форму blank. Добавляем пункты контекстного меню точно так же, как мы это делали для главного меню (рис. 2.9):

    (рис 2.9) Пункты контекстного меню

    Свойство Text и Shortcut пунктов меню оставляем прежними. Если мы установим затем для свойства ShowShortcut значение false, то сочетания клавиш будут работать, но в самом меню отображаться не будут — как на рис. рис. 2.8. Свойство Name будет формироваться следующим образом: для пункта Cut — cmnuCut, для Сopy — cmnuCopy и т.д.

    В обработчике пунктов просто вызываем соответствующие методы:

    private void cmnuCut_Click(object sender, System.EventArgs e)
      {
        Cut();
      }
    
      private void cmnuCopy_Click(object sender, System.EventArgs e)
      {
        Copy();
      }
    
      private void cmnuPaste_Click(object sender, System.EventArgs e)
      {
        Paste();
      }
    
      private void cmnuDelete_Click(object sender, System.EventArgs e)
      {
      Delete();
      }
    
      private void cmnuSelectAll_Click(object sender, System.EventArgs e)
      {
      SelectAll();
    }

    Последнее, что нам осталось сделать, — это определить, где будет появляться контекстное меню. Элемент RichTextBox, так же как и формы frmmain и blank, имеет свойство ContextMenu, где мы и указываем contextMenu1, поскольку нам нужно отображать меню именно в текстовом поле. Запускаем приложение — теперь в любой точке текста доступно меню (рис. 2.10).

    (рис 2.10) Контекстное меню

    Диалоговые окна

    Среда Visual Studio .NET содержит готовые диалоговые окна, предлагающие выбрать файл для открывания или путь на диске для сохранений текущего файла (рис. 2.11).

    (рис 2.11) Диалоговое окно открытия файла

    Мы рассмотрим одну часть диалоговых окон в этой лекции, а другую, относящуюся к подготовке документа к печати, — в лекции 6.

    OpenFileDialog

    Добавьте на форму frmmain элемент управления OpenFileDialog из окна панели инструментов ToolBox. Подобно элементу MainMenu, он будет располагаться на панели невидимых компонент (рис. 2.12):

    (рис 2.12) Добавление OpenFileDialog на форму

    Свойство FileName задает название файла, которое будет находиться в поле "Имя файла:" при появлении диалога. На рис. рис. 2.12 название в этом поле — "Текстовые файлы", поскольку на рис. рис. 2.11 был введен именно этот текст. Свойство Filter задает ограничение файлов, которые могут быть выбраны для открытия — в окне будут показываться только файлы с заданным расширением. Через вертикальную разделительную линию можно задать смену типа расширения, отображаемого в выпадающем списке "Тип файлов". Здесь введено Text Files (*.txt)|*.txt|All Files(*.*)|*.* что означает обзор либо текстовых файлов, либо всех. Свойство InitialDirectory позволяет задать директорию, откуда будет начинаться обзор. Если это свойство не установлено, исходной директорией будет рабочий стол.

    Для работы с файловыми потоками в коде формы blank подключаем пространство имен System.IO:

    using System.IO;

    В методе Open считываем содержимое файла в RichTextBox:

    //Создаем метод Open, в качестве параметра объявляем строку адреса  файла.
      public void Open(string OpenFileName)
      {
        //Если файл не выбран, возвращаемся назад (появится встроенное предупреждение)
        if (OpenFileName == "") 
        {
          return;
        }
        else
        {
    //Создаем новый объект StreamReader и передаем ему переменную //OpenFileName
          StreamReader sr = new StreamReader(OpenFileName);
          //Читаем весь файл и записываем его в richTextBox1
          richTextBox1.Text = sr.ReadToEnd();
          // Закрываем поток
          sr.Close();
          //Переменной DocName присваиваем адресную строку
          DocName = OpenFileName;
        }
      }

    Добавим обработчик пункта меню Open формы frmmain:

    private void mnuOpen_Click(object sender, System.EventArgs e)
      {
        //Можно программно задавать доступные для обзора расширения файлов 
        //openFileDialog1.Filter = "Text Files (*.txt)|*.txt|All Files(*.*)|*.*";
        
          
        //Если выбран диалог открытия файла, выполняем условие
        if (openFileDialog1.ShowDialog() == DialogResult.OK) 
        {
          //Создаем новый документ
          blank frm = new blank();
          //Вызываем метод Open формы blank
          frm.Open(openFileDialog1.FileName);
          //Указываем, что родительской формой является форма frmmain
          frm.MdiParent = this;
          //Присваиваем переменной DocName имя открываемого файла
          frm.DocName = openFileDialog1.FileName;
          //Свойству Text формы присваиваем переменную DocName
          frm.Text = frm.DocName;
          //Вызываем форму frm
          frm.Show();  
        }
      }

    Запускаем приложение и открываем текстовый файл, сохраненный в формате блокнота (рис. 2.13).

    (рис 2.13) Заголовок формы представляет собой адрес и имя открытого файла

    Для корректного чтения кириллицы текст в блокноте должен быть сохранен в кодировке Unicode. К сожалению, встроенные диалоговые окна OpenFileDialog Visual Studio .NET не содержат дополнительного поля, позволяющего выбирать кодировку файла при его открытии или сохранении, как это реализовано, например, в блокноте.

    SaveFileDialog

    Для сохранения файлов добавляем на форму frmmain элемент управления saveFileDialog1. Cвойства этого диалога в точности такие же, как у OpenFileDialog (рис. 2.12). Переходим в код формы blank:

    //Создаем метод Save, в качестве параметра объявляем строку адреса  файла.
        public void Save(string SaveFileName)
        {
          //Если файл не выбран, возвращаемся назад (появится встроенное предупреждение)
          if (SaveFileName == "") 
          {
            return;
          }
          else
          {
    //Создаем новый объект StreamWriter и передаем ему переменную //OpenFileName
            StreamWriter sw = new StreamWriter(SaveFileName);
            //Содержимое richTextBox1 записываем в файл
            sw.WriteLine(richTextBox1.Text);
            //Закрываем поток
            sw.Close();
    //Устанавливаем в качестве имени документа название сохраненного файла
            DocName = SaveFileName;
          }
        }

    Добавляем обработчик пункта меню Save формы frmmain:

    private void mnuSave_Click(object sender, System.EventArgs e)
      {
        //Можно программно задавать доступные для обзора расширения файлов 
        //openFileDialog1.Filter = "Text Files (*.txt)|*.txt|All Files(*.*)|*.*";
    
        //Если выбран диалог открытия файла, выполняем условие
        if (saveFileDialog1.ShowDialog() == DialogResult.OK) 
        {
          //Переключаем фокус на данную форму.
          blank frm = (blank)this.ActiveMdiChild;
          //Вызываем метод Save формы blank
          frm.Save(saveFileDialog1.FileName);
          //Указываем, что родительской формой является форма frmmain
          frm.MdiParent = this;
          //Присваиваем переменной FileName имя сохраняемого файла
          frm.DocName = saveFileDialog1.FileName;
          //Свойству Text формы присваиваем переменную DocName
          frm.Text = frm.DocName;      
      
        }
      }

    Запускаем приложение. Теперь файлы можно открывать, редактировать и сохранять. Однако, при сохранении внесенных изменений в уже сохраненном файле вместо его перезаписи вновь появляется окно SaveFileDialog. Изменим нашу программу так, чтобы можно было сохранять и перезаписывать файл. В конструкторе формы frmmain после InitializeComponent отключим доступность пункта меню Save:

    mnuSave.Enabled = false;

    Переключаемся в режим дизайна формы frmmain и добавляем пункт меню Save As после пункта Save. Устанавливаем следующие свойства этого пункта: Name — mnuSaveAs, Shortcut — CtrlShiftS, Text Save As. В обработчике Save As вставляем вырезанный обработчик пункта Save и добавляем включение доступности Save:

    mnuSave.Enabled = true;

    Сохранять изменения требуется как в только что сохраненных документах, так и в документах, созданных ранее и открытых для редактирования. Поэтому добавим в метод Open включение доступности пункта меню Save:

    private void mnuOpen_Click(object sender, System.EventArgs e)
    {
      mnuSave.Enabled = true;
    }

    В обработчике пункта Save добавим простую перезапись файла — вызов метода Save формы blank:

    private void mnuSave_Click(object sender, System.EventArgs e)
      {
        //Переключаем фокус на данную форму.
        blank frm = (blank)this.ActiveMdiChild;
        //Вызываем метод Save формы blank
        frm.Save(frm.DocName);
        
          
      }

    Запускаем приложение. Теперь, если мы работаем с несохраненным документом, пункт Save неактивен (рис. 2.14), после сохранения он становится активным (рис. 2.15) и, кроме того, работает сочетание клавиш Ctrl+S. Можно сохранить копию текущего документа, вновь воспользовавшись пунктом меню Save As (рис. 2.16).

    (рис 2.15) Новый документ(рис 2.14) Сохраненный документ(рис 2.16) Сохранение копии

    Конечно, эту задачу можно решить более элегантно — совместив оба пункта сохранения документа в один. Но мне хотелось показать разницу между простым сохранением и перезаписью файлов, кроме того, мы научились отключать доступность пунктов меню.

    Сохранение файла при закрытии формы

    Всякий раз, когда мы закрываем документ Microsoft Word, в который внесли изменения, появляется окно предупреждения, предлагающее сохранить документ. Добавим аналогичную функцию в наше приложение. В классе blank : System.Windows.Forms.Form формы blank создаем переменную, которая будет фиксировать сохранение документа:

    public bool IsSaved = false;

    В обработчик методов Save и Save As формы frmmain добавляем изменение значения этой переменной:

    private void mnuSave_Click(object sender, System.EventArgs e)
        {
    …
    frm.IsSaved = true;
        } 
    
      private void mnuSaveAs_Click(object sender, System.EventArgs e)
        {
    …
    frm.IsSaved = true;
        }

    Переходим в режим дизайна формы blank и в окне свойств переключаемся на события формы, щелкнув на значок с молнией. В поле события Closing дважды щелкаем и переходим в код:

    private void blank_Closing(object sender, System.ComponentModel.CancelEventArgs e)
      {
      //Если переменная IsSaved имеет значение true, т. е.  новый документ 
    //был сохранен (Save As) или в открытом документе были сохранены изменения (Save), то //выполняется условие
     if(IsSaved ==true)
      //Появляется диалоговое окно, предлагающее сохранить документ.
      if(MessageBox.Show("Do you want save changes in " + this.DocName + "?", 
          "Message", MessageBoxButtons.YesNo, 
          MessageBoxIcon.Question) == DialogResult.Yes)
          //Если была нажата  кнопка Yes, вызываем метод Save
        {
          this.Save(this.DocName); 
        }    
         
        
      }

    Запускаем приложение. При закрытии формы blank или frmmain появляется окно предупреждения (рис. 2.17):

    (рис 2.17) Окно предупреждения при закрытии формы

    OpenFileDialog и SaveFileDialog для SDI-приложений

    При создании MDI-приложений приходится разделять код для открытия и сохранения файлов, как мы делали для приложения Notepad C#. В случае SDI-приложений весь код будет находиться в одном обработчике. Cоздаем новое приложение, называем его TextEditor. На форме размещаем элемент управления TextBox и устанавливаем следующие свойства:

    TextBox, свойствоЗначение
    Name txtBox
    Dock Fill
    Multiline true
    Text Да

    Добавляем на форму элемент MainMenu, в котором будет всего три пункта — File, Open и Save (свойства этих пунктов см. в таблице 2.1). Из окна ToolBox перетаскиваем элементы OpenFileDialog и SaveFileDialog — свойства этих элементов в точности такие же, как и у диалогов приложения Notepad C#. Переходим в код формы. Подключаем пространство имен для работы с файловыми потоками:

    using System.IO;

    Добавляем обработчик для пункта меню Open:

    private void mnuOpen_Click(object sender, System.EventArgs e)
      {
        //Показываем диалог выбора файла
        openFileDialog1.ShowDialog();
        // Переменной fileName присваиваем имя открываемого файла
        string fileName = openFileDialog1.FileName;
        //Создаем поток fs и открываем файл для чтения.
        FileStream filestream= File.Open(fileName, FileMode.Open, FileAccess.Read);
        //Проверяем,  открыт ли поток,  и если открыт, выполняем условие
        if(filestream != null)
        {
          //Создаем объект streamreader и связываем его с потоком filestream
          StreamReader streamreader = new StreamReader(filestream);
          //Считываем весь файл и записываем его в TextBox
          txtBox.Text = streamreader.ReadToEnd();
          //Закрываем поток.
          filestream.Close();
        }
      }

    Добавляем обработчик для пункта меню Save:

    private void mnuSave_Click(object sender, System.EventArgs e)
      {
        //Показываем диалог выбора файла
        saveFileDialog1.ShowDialog();
        // В качестве имени сохраняемого файла устанавливаем переменную fileName
        string fileName=saveFileDialog1.FileName;
        //Создаем поток fs и открываем файл для записи.
        FileStream filestream = File.Open(fileName, FileMode.Create, FileAccess.Write);
          //Проверяем,  открыт ли поток,  и если открыт, выполняем условие
        if(filestream != null)
        {
          //Создаем объект streamwriter и связываем его с потоком filestream
          StreamWriter streamwriter = new StreamWriter(filestream);
          //Записываем данные из TextBox в файл
          streamwriter.Write(txtBox.Text);
          //Переносим данные из потока в файл
          streamwriter.Flush();
          //Закрываем поток
          filestream.Close();
        }
      }

    Запускаем приложение. Как уже отмечалось, элемент управления TextBox имеет встроенное контекстное меню, поддерживает сочетание клавиш для редактирования, а диалоговые окна открытия и сохранения позволяют этому простейшему приложению работать с внешними файлами.

    На диске, прилагаемом к книге, вы найдете приложение TextEditor (Code\Glava2\TextEditor).

    FontDialog

    Продолжим работу над приложением Notepad C#. Добавим теперь возможность выбирать шрифт, его размер и начертание. В режиме дизайна перетащим на форму frmmain из окна ToolBox элемент управления FontDialog. Не изменяя ничего в свойствах этого элемента, переходим в обработчик пункта Font главного меню:

    private void mnuFont_Click(object sender, System.EventArgs e)
      {
        //Переключаем фокус на данную форму.
        blank  frm = (blank)this.ActiveMdiChild;
        //Указываем, что родительской формой является форма frmmain  
        frm.MdiParent = this;
        //Вызываем диалог
        fontDialog1.ShowColor = true;
        //Связываем свойства SelectionFont и SelectionColor элемента RichTextBox 
        //с соответствующими свойствами диалога
        fontDialog1.Font = frm.richTextBox1.SelectionFont;
        fontDialog1.Color = frm.richTextBox1.SelectionColor; 
        //Если выбран диалог открытия файла, выполняем условие
        if (fontDialog1.ShowDialog() == DialogResult.OK)
        {
          frm.richTextBox1.SelectionFont = fontDialog1.Font;
          frm.richTextBox1.SelectionColor = fontDialog1.Color; 
        }
        //Показываем форму
        frm.Show();
      }

    Запускаем приложение, и в окне Output появляется список ошибок (рис. 2.18).

    (рис 2.18) Список ошибок — NotepadCSharp.blank.richTextBox1 недоступен из-за своего уровня защиты

    В чем же дело? При расположении на форме blank элемента управления RichTextBox среда Visual Studio.NET сгенерировала экземпляр richTextBox1 класса System.Windows.Forms.RichTextBox с модификатором доступа private, из-за чего при обращении к нему и возникает исключение:

    private System.Windows.Forms.RichTextBox richTextBox1;

    Изменим модификатор на public и снова запустим приложение. При выборе пункта меню Font теперь можно менять параметры текущего текста.

    ColorDialog

    Диалоговое окно FontDialog содержит список цветов, которые могут быть применены к тексту, но предлагаемый список ограничен. Более интересной представляется возможность назначать пользовательский цвет, который может быть определен в большом диапазоне (рис. 2.19):

    (рис 2.19) Диалоговое окно ColorDialog

    Из окна ToolBox добавляем элемент управления ColorDialog и, вновь не изменяя его свойств, переходим к обработчику пункта Color главного меню формы frmmain:

    private void mnuColor_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.MdiParent = this;
          colorDialog1.Color = frm.richTextBox1.SelectionColor; 
          
          if (colorDialog1.ShowDialog() == DialogResult.OK)
          {
            frm.richTextBox1.SelectionColor = colorDialog1.Color; 
          }
    
          frm.Show();
        }

    Обратите внимание на то, что код для ColorDialog в точности такой же, как и часть кода для свойства Color диалога FontDialog. Это и не удивительно: ведь мы связываем эти диалоги со свойствами одного и того же объекта — RichTextBox.

    StatusBar

    Элемент управления StatusBar применяется в программах для вывода информации в строку состояния — небольшую полоску, расположенную внизу приложения. В Microsoft Word, например, на ней отображается количество страниц, язык ввода, состояние проверки правописания и другие параметры. Добавим к приложению Notepad C# строку состояния, на которой осуществляется подсчет вводимых символов и выводится системное время.

    Добавляем на форму blank элемент управления StatusBar. Удаляем содержимое поля свойства Text. В поле свойства Panels щелкаем на кнопку (…). Открывается StatusBarCollectionEditor, в котором мы создаем панели для отображения. Создайте две панели, дважды щелкая на кнопке Add, и установите им следующие свойства (измененные значения выделяются жирным шрифтом) (рис. 2.20 и рис. 2.21):

    (рис 2.21) Свойства панели sbAmount(рис 2.20) Свойства панели sbTime

    Значения некоторых свойств панели приводятся в таблице 2.2.

    СвойствоЗначение
    Alignment Выравнивание содержимого свойства Text на панели
    AutoSize Изменение размеров панели по содержимому
    BorderStyle Внешний вид панели – утопленная, приподнятая или без выделения
    Icon Добавление иконки
    Style Стиль панели
    Text Текст, располагаемый на панели
    ToolTipText Всплывающая подсказка — появляется при наведении курсора на панель
    Width Ширина панели в пикселях
    Name Название панели для обращения к ней в коде

    Свойства панели, назначаемые в окне редактора StatusBarCollectionEditor, можно изменять в коде —именно так мы сейчас и поступим.

    После завершения работы над панелями закрываем редактор. Свойству ShowPanels элемента управления StatusBar устанавливаем значение True. На форме немедленно отображаются две панели.

    Выделяем элемент управления RichTextBox, в окне его свойств переключаемся на события и создаем обработчик для события TextChanged:

    private void richTextBox1_TextChanged(object sender, System.EventArgs e)
      {
        //Свойству Text панели sbAmount устанавливаем надпись "Аmount of symbols" 
        //и длину  текста в RichTextBox.
        sbAmount.Text = "Аmount of symbols" + richTextBox1.Text.Length.ToString(); 
      }

    Свойство Text панели sbAmount мы изменяем программно: даже если бы мы ничего не написали в окне редактора StatusBarCollectionEditor, при возникновении события TextChanged на панели появится надпись.

    Займемся теперь второй панелью — той, на которую будем выводить системное время. В конструкторе формы blank добавляем код:

    public blank()
      {
        InitializeComponent();
        //Свойству Text панели sbTime устанавливаем системное время, 
        // конвертировав его в тип String
        sbTime.Text = Convert.ToString(System.DateTime.Now.ToLongTimeString());
        //В тексте всплывающей подсказки  выводим текущую дату
        sbTime.ToolTipText = Convert.ToString(System.DateTime.Today.ToLongDateString());
      }

    Запускаем приложение. Панель с выводимым временем располагается достаточно необычно (рис. 2.22), при необходимости для вывода времени в привычном правом нижнем углу можно добавить третью пустую панель.

    (рис 2.22) Строка состояния с двумя панелями

    Закрывание формы

    При запуске приложения Notepad С# на форме имеются три стандартные кнопки — "Свернуть", "Развернуть" и "Закрыть". Большинство пользователей предпочитают использовать именно кнопку "Закрыть" для выхода из приложения. Тем не менее принято дублировать кнопку формы пунктом меню Exit. В обработчике этой кнопки добавляем код

    private void mnuExit_Click(object sender, System.EventArgs e)
        {
          this.Close();
      }

    Метод Close закрывает форму и может быть назначен другим элементам управления — например, кнопке.

    CheckBox

    Чекбоксы относятся к так называемым кнопкам отложенного действия, т. е. их нажатие не запускает немедленно какого-либо процесса. С их помощью пользователь устанавливает определенные параметры, результат действия которых скажется после запуска других элементов управления. Добавим в приложение Notepad C# форму для поиска заданного текста, в которой будут использованы элементы управления CheckBox. В окне Solution Explorer щелкаем правой кнопкой мыши на названии проекта — NotepadCSharp — и в появившемся меню выбираем пункт Add Windows Form. Называем новую форму FindForm.cs и устанавливаем следующие свойства:

    FindForm, форма, свойствоЗначение
    Name FindForm
    FormBorderStyle FixedToolWindow
    Size 328; 112
    Text Find

    Располагаем на форме TextBox, две кнопки и два элемента CheckBox так, как показано на рис. рис. 2.23:

    (рис 2.23) Форма FindForm в режиме дизайна

    Устанавливаем следующие свойства элементам управления:

    TextBox, свойствоЗначение
    Name txtFind
    Size 192; 20
    Text
    checkBox1, свойствоЗначение
    Name cbMatchCase
    Text Match Case
    checkBox2, свойствоЗначение
    Name cbMatchWhole
    Text Match whole word
    button1, свойствоЗначение
    Name btnOK
    DialogResult OK
    Text OK
    button2, свойствоЗначение
    Name btnCancel
    DialogResult Cancel
    Text Cancel

    Выбор первого чекбокса — cbMatchCase — будет устанавливать поиск слова с учетом регистра, второго – cbMatchWhole — поиск по целому слову. Обратите внимание на свойство кнопок DialogResult — выбирая соответствующие свойства, мы устанавливаем обработчики для кнопок без обращения к коду. Значение ОК закрывает форму, выполняя установленное действие, — в данном случае — поиск; значение Cancel просто закрывает форму. В главном меню формы frmmain добавляем разделительную линию и пункт Find и устанавливаем значения свойств: Name — mnuFind, Shortcut — CtrlF, Text — Find. Переходим в обработчик этого пункта:

    private void mnuFind_Click(object sender, System.EventArgs e)
      {
        //Создаем новый экземпляр формы FindForm
        FindForm frm = new FindForm();
        //Если выбран результат DialogResult.Cancel, закрываем форму (до этого 
        //мы использовали DialogResult.OK)
        if(frm.ShowDialog(this) == DialogResult.Cancel) return;
        ////Переключаем фокус на данную форму.
        blank form = (blank)this.ActiveMdiChild;
        ////Указываем, что родительской формой является форма frmmain  
        form.MdiParent = this;
        //Вводим переменную для поиска в определенной части текста —
        //поиск слова будет осуществляться от текущей позиции курсора
        int start = form.richTextBox1.SelectionStart;
        //Вызываем предопределенный метод Find элемента richTextBox1.
        form.richTextBox1.Find(frm.FindText, start, frm.FindCondition);
      }

    Основой поиска будет метод Find элемента управления RichTextBox. Нам остается лишь указать параметры поиска, которые могут принимать различные значения (рис. 2.24):

    (рис 2.24) Параметры метода Find. Щелкая на маленькую кнопку всплывающей подсказки Intel Sense, выбираем разные подсказки для групп передаваемых параметров

    В коде формы FindForm осталось реализовать логику работы, зависящую от положения элементов CheckBox:

    // Создаем перечисление, возвращающее параметр FindCondition
        public RichTextBoxFinds FindCondition
        {
          get
          {
            //Выбраны два чекбокса
            if (cbMatchCase.Checked  cbMatchWhole.Checked)
            {
              // Возвращаем свойство MatchCase и WholeWord 
              return RichTextBoxFinds.MatchCase| RichTextBoxFinds.WholeWord;
            }
            //Выбран один чекбокс
            if (cbMatchCase.Checked )
            {
              // Возвращаем свойство MatchCase
              return RichTextBoxFinds.MatchCase;
            }
            //Выбран другой чекбокс
            if (cbMatchWhole.Checked )
            {
              // Возвращаем свойство WholeWord
              return RichTextBoxFinds.WholeWord;
            }
            //Не выбран ни один чекбокс
            return RichTextBoxFinds.None;
          }
        }

    И наконец, создаем свойство FindText, возвращающее в качестве переменной поиска введенный текст в текстовое поле формы FindForm:

    public string FindText
        {
          get{return txtFind.Text;}
          set{txtFind.Text = value;}
        }

    Запускаем приложение. Выбирая различные варианты, можно однократно искать заданное слово (рис. 2.25):

    (рис 2.25) Поиск слова в тексте

    Свойство TabIndex элементов управления

    Многие пользователи предпочитают работать по большей части с клавиатурой и прибегать к помощи мыши только там, где без нее нельзя обойтись. При работе с формой Find было бы удобно ввести слово в текстовое поле, затем, нажав клавишу Tab и Enter, осуществить поиск, — или, дважды нажав клавишу Tab, переключить фокус ввода на чекбокс Match Case. Для этого в режиме дизайна формы Find следует установить свойство TabIndex элементов управления в виде последовательной нумерации, начиная с нуля:

    Элемент управленияTabIndex
    TxtFind 0
    BtnOK 1
    CbMatchCase 2
    CbMatchWhole 3
    BtnCancel 4

    При такой нумерации фокус ввода будет последовательно переходить в порядке возрастания — от элемента txtFind до btnCancel. Свойство TabIndex имеет особенно большое значение при создании ряда текстовых полей, где пользователю будет предлагаться ввести информацию.

    Label, LinkLabel и PictureBox

    Программы, как правило, содержат пункт главного меню "О программе", где в отдельном окне помещается логотип компании, лицензионное соглашение, гиперссылки на сайт разработчика и другая информация. Создадим подобную форму, используя новые элементы управления — Label, LinkLabel и PictureBox.

    Итак, добавим в наш проект новую форму и назовем ее About.cs. Установим следующие свойства формы:

    About, форма, свойствоЗначение
    Name About
    FormBorderStyle FixedSingle
    MaximizeBox False
    MinimizeBox False
    Size 318; 214
    Text About Notepad C#

    Добавим на форму элемент управления PictureBox — он представляет собой подложку, размещаемую на форме, которая может содержать в себе рисунки для отображения. В поле свойства Image щелкаем на кнопку (…) и выбираем рисунок по адресу Code\Glava2\NotepadCSharp\Icon\logo.gif. Поскольку logo.gif является анимированным рисунком, элемент PictureBox начинает воспроизводить анимацию сразу же, даже в режиме дизайна.

    Из окна ToolBox перетащим на форму кнопку, Label и LinkLabel. В поле свойства Text кнопки введем OK. Элемент Label предназначен для размещения на форме надписей, которые в готовом приложении будут доступны только для чтения. В поле свойства Text введем Notepad C# 2006 All rights reserved. Элемент LinkLabel отображает текст на форме в стиле web-ссылок и обычно используется для создания навигации между формами или ссылки на сайт. В поле Text этого элемента вводим адрес гипотетического сайта — www.notepadcsharp.com. Пользователь будет переходить на сайт, нажимая на эту ссылку, поэтому реализуем переход по гиперссылке для события Click. В окне Properties щелкаем дважды на событии Click и добавляем обработчик:

    private void linkLabel1_Click(object sender, System.EventArgs e)
        {
        //Добавляем блок для обработки исключений — по разным причинам 
        //пользователь может не получить доступа к ресурсу.
          try
          {
            //Вызываем метод VisitLink, определенный ниже
            VisitLink();
          }
          catch (Exception ex )
          {
            MessageBox.Show(ex +"Unable to open link that was clicked.");
          }
          
        }
        //Создаем метод VisitLink
          private void VisitLink()
          {
            // Изменяем цвет посещенной ссылки, программно 
            //обращаясь к свойству LinkVisited элемента LinkLabel 
            linkLabel1.LinkVisited = true;
            //Вызываем метод Process.Start method  для запуска браузера, 
            //установленного по умолчанию, и открытия ссылки
            System.Diagnostics.Process.Start("http://www.notepadcsharp.com");
          }

    Кнопка OK просто будет закрывать форму:

    private void button1_Click(object sender, System.EventArgs e)
        {
          this.Close();
        }

    В пункте главного меню About Programm… формы frmmain добавляем процедуру вызова формы About:

    private void mnuAbout_Click(object sender, System.EventArgs e)
        {
          //Создаем новый экземпляр формы  About
          About  frm = new About();
          frm.Show();
        }

    Запускаем приложение (рис. 2.26):

    (рис 2.26) Окно About Notepad C#

    Режимы запуска окон

    В приложении Notepad C# с открытой формой About без труда можно вернуться к другим пунктам меню программы или документу, т. е. форма About позволяет переключать фокус. Такое поведение дочерних форм характерно для панелей инструментов в программах и называется немодальным. Наоборот, форма, запущенная в модальном режиме, не позволит работать с приложением до тех пор, пока мы не закроем эту форму. Для открытия формы в модальном режиме используется метод ShowDialog:

    private void mnuAbout_Click(object sender, System.EventArgs e)
        {
          //Создаем новый экземпляр формы  About
          About  frm = new About();
          frm.ShowDialog();
        }

    Модификаторы доступа и наследование форм

    С модификаторами доступа вы сталкивались, когда изучали синтаксис языка C#. Используя их, можно определить область видимости, иначе говоря, уровень доступности типов и членов классов, — под этим понимается участок кода, из любой точки которого будет доступен запрашиваемый тип или член класса. Технология Common Type System (CTS, система общих типов) — одна из технологий, обеспечивающих функционирование среды .NET, определяет правила для видимости типов и доступа к их членам. Различные типы модификаторов и их описание приводятся в таблице 2.3.

    МодификаторОпределение
    public (общественный, открытый) Объект доступен любому коду из любой сборки
    private (частный, закрытый) Объект может быть вызван другими методами только из того же типа
    internal (внутренний, сборочный) Объект может быть вызван любым кодом из той же сборки
    protected (защищенный, родовой) Объект может быть вызван производными типами независимо от их сборки
    protected internal (родовой сборочный) Объект может быть вызван производными типами, определенными в той же самой сборке

    В качестве объекта могут выступать методы, свойства, классы и, как мы далее увидим, — формы. Под сборкой здесь понимается функциональная единица кода (строгое определение сборок см. в лекции 9).

    Наследование классов — достаточно простое и чрезвычайно удобное понятие синтаксиса С#. Формы, которые содержат большую часть методов и свойств, определенных как public, тоже можно наследовать — для получения доступа к свойствам, определенным как protected, и для ускорения работы. В самом деле, если у нас имеется уже готовая форма (например, About Notepad C#), которую мы собираемся использовать в нескольких приложениях, удобно просто создать наследуемую форму.

    Запускаем приложение Notepad C# и в окне Solution Explorer щелкаем правой кнопкой на названии проекта. В появившемся контекстном меню выбираем Add\Add Inherited Form… . В окне Add New Item задаем название формы — IAbout.cs. Далее появляется окно Inheritance Picker, в котором отображаются все формы текущего проекта (рис. 2.27).

    (рис 2.27) Окно выбора формы для наследования Inheritance Picker

    В этом окне мы выбираем форму About. Обратите внимание на кнопку Browse, позволяющую искать формы для наследования в библиотеках dll. Нажимаем кнопку OK — в проекте появилась новая форма, содержащая все элементы управления формы About (рис. 2.28). Для завершения связей необходимо скомпилировать проект cнова — выбрав пункт главного меню Build/Rebuild Solution.

    (рис 2.28) Форма IAbout, наследованная от формы About

    При редактировании родительской формы изменения также касаются дочерней — добавьте, например, кнопку на форму About, и она появится на форме IAbout. Изменения же, производимые с дочерней формой, не затрагивают родительскую форму.

    ToolBar и ImageList

    Панели инструментов ToolBar содержат наборы кнопок, как правило, дублирующих пункты главного меню. В графических программах панели инструментов — основное средство работы. Откроем снова приложение Notepad C# и перетащим из окна ToolBox элемент управления ToolBar. На кнопках панели обычно располагаются иконки, поэтому, прежде чем мы начнем заниматься ими, нам следует подумать о рисунках. Добавим на форму элемент управления ImageList, применяемый для хранения рисунков, которые могут быть использованы для оформления приложения. Щелкнем в поле Images элемента ImageList (рис. 2.29).

    (рис 2.29) Cвойство Images элемента ImageList

    Добавим файлы иконок, последовательно нажимая кнопку Add и выбирая директорию — Code\Glava2\NotepadCSharp\Icon. Мы создадим кнопки панели инструментов, дублирующие действия пунктов меню New, Open, Save, Cut, Copy и Paste, поэтому вы можете выбрать подходящие изображения на своем компьютереДля создания иконки подходит даже редактор Paint. Для иконки панели инструментов размер рисунка должен быть 16х16 пикселей, сохраненный как 256-цветный рисунок. Для преобразования сохраненного рисунка, например, 1.bmp, просто смените его расширение на 1.ico.. В любом случае, подобрав иконки, завершаем работу с редактором ImageCollectionEditor, нажимая OK. Займемся теперь элементом ToolBar. Свойству Name установим значение toolBarMain, а в поле свойства ImageList выберем imageList1. Запустим редактор ToolBarButton Collection Editor для создания кнопок, щелкнув кнопку (…) в поле свойства Buttons (рис. 2.30).

    (рис 2.30) Редактор ToolBarButton Collection Editor

    Для добавления кнопок в этом редакторе тоже следует нажимать клавишу Add. Создадим шесть кнопок, установив для них следующие свойства:

    NameImage IndexToolTipText
    tbNew Create New
    tbOpen Open
    tbSave Save
    tbCut Cut
    tbCopy Copy
    tbPaste Paste

    Свойство Name устанавливает название кнопки для обращения к ней в коде. Свойство Image Index определяет изображение на кнопке, а в поле ToolTipText вводим текст всплывающей подсказки, которая будет появляться при наведении курсора на кнопку. Завершив работу с редактором кнопок в режиме дизайна формы frmmain, дважды щелкаем на ToolBar и переходим в код. Для создания функциональности кнопок связываем событие Click заданной кнопки c соответствующим обработчиком пунктов меню:

    private void toolBarMain_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e)
        {
          //New
          if (e.Button.Equals(tbNew)) 
          {
            mnuNew_Click(this, new EventArgs());
          }
          //Open
          if (e.Button.Equals(tbOpen))
          {
            mnuOpen_Click(this, new EventArgs());
          }
          //Save
          if (e.Button.Equals(tbSave))
          {
            mnuSave_Click(this, new EventArgs());
          }
          //Cut
          if (e.Button.Equals(tbCut))
          {
            mnuCut_Click(this, new EventArgs());
          }
          //Copy
          if (e.Button.Equals(tbCopy))
          {
            mnuCopy_Click(this, new EventArgs());
          }
          //Paste
          if (e.Button.Equals(tbPaste))
          {
            mnuPaste_Click(this, new EventArgs());
          }
        }

    Запускаем приложение. Кнопки панели инструментов дублируют пункты меню, а при наведении на них появляются подсказки (рис. 2.31).

    (рис 2.31) Панель инструментов Toolbar

    На диске, прилагаемом к книге, вы найдете приложение NotepadCSharp (Code\Glava2\NotepadCSharp). В конце этой лекции приводится листинг форм frmamin и blank.

    Чтение и запись файлов

    При изучении синтаксиса языка С# вы сталкивались с чтением и записью файлов. Если при рассмотрении обработчиков OpenFileDialog и SaveFileDialog вы не до конца поняли, как .NET Framework работает с файлами, давайте еще раз разберемся с этим понятием.

    Файл — это совокупность данных, сохраненных на диске под общим названием. Когда мы открываем файл, он становится потоком, с которым мы можем осуществлять следующие операции:

  • считывать данные из потока;
  • записывать данные в поток;
  • производить поиск в потоке.
  • В С# все классы, которые представляют потоки, наследуются от абстрактного класса Stream. Класс Stream вместе со своими подклассами обеспечивает универсальное представление источников данных и архивов, абстрагированное от подробностей операционной системы. Классы потоков определены в пространстве имен System.IO.

    Чтение и запись текстовых файлов

    Для чтения и записи файлов, содержащих только текст, используются классы StreamReader и StreamWriter. Класс Stream Reader наследуется от класса TextReader, который считывает строки из стандартного текстового файла. Класс StreamWriter наследуется от класса TextWriter, записывающего символы в поток в специальном кодировании.

    Создайте новое консольное приложение и назовите его FileStream. Скомпилируйте приложение (Ctrl+F5). В папке проекта создайте текстовый документ 1.txt. Откройте его в блокноте (или в приложении Notepad C#) и напишите в нем любой текст на русском и английском языках, сохранив затем его в кодировке Unicode. Выведем теперь записанный текст в окно консольного приложения. Переходим в код FileStream и подключаем пространство имен:

    using System.IO;

    В методе Main добавляем код:

    [STAThread]
      static void Main(string[] args)
      {
    //Cоздаем экземпляр Chitat  класса StreamReader и указываем путь к файлу 
    и его название
        StreamReader Chitat = new StreamReader("1.txt");
        // Объявляем переменную A.
        string A;
        //В переменную А считываем содержимое файла
        A = Chitat.ReadToEnd();
        //Закрываем поток
        Chitat.Close();
        //Выводим переменную A в окно консольного приложения.
        Console.WriteLine(A);
      }

    Когда вы работаете с файлом, например, редактируете документ Microsoft Word, вы не сможете удалить его или переместить — потому что приложение занято и поток открыт. Только завершив работу с документом и, следовательно, закрыв поток, можно перемещать его или открывать с помощью других приложений. Аналогично, в этом примере мы закрываем поток, вызывая метод Close, освобождая тем самым задействованные системные ресурсы.

    Запускаем приложение. В консольном окне появляется текст, считанный из файла (рис. 2.32 ).

    (рис 2.32) Чтение текста из файла

    Добавим теперь объект класса StreamWriter, который будет записывать текст в файл:

    [STAThread]
      static void Main(string[] args)
        {
          //Cоздаем экземпляр Chitat  класса StreamReader и указываем путь к файлу и его название
          StreamReader Chitat = new StreamReader("1.txt");
          // Объявляем переменную A.
          string A;
          //В переменную А считываем содержимое файла
          A = Chitat.ReadToEnd();
          //Закрываем поток
          Chitat.Close();
          //Выводим переменную A в окно консольного приложения.
          Console.WriteLine(A);
          //Cоздаем экземпляр Pisat  класса StreamWriter
          StreamWriter Pisat = new StreamWriter("1.txt", true, System.Text.Encoding.Unicode);
          //Вызываем метод Write экземпляра Pisat
          Pisat.Write("Добавляем текст");
          //Закрываем поток
          Pisat.Close();
    
          // Для того, чтобы не переходить в директорию 
          //для просмотра файла вручную, выведем его еще раз
          StreamReader ChitatSnova = new StreamReader("1.txt");
          string B;
          B = ChitatSnova.ReadToEnd();
          ChitatSnova.Close();
          Console.WriteLine(B);
    
        }

    Метод Write добавил строку в файл (рис. 2.33):

    (рис 2.33) Чтение и запись текста из файла

    На диске, прилагаемом к книге, вы найдете приложение FileStream (Code\Glava2\ FileStream).

    Object Browser и Intel Sense

    В первой лекции мы рассматривали окно Object Browser. Использование этого окна зачастую может быть гораздо более удобным, чем поиск справочной информации в MSDN.

    В предыдущем примере, при создании экземпляра Pisat класса StreamWriter следует указывать параметры, передаваемые классу, — при использовании Intel Sense всплывающая подсказка указывает, какие именно параметры следует передавать (рис. 2.34).

    (рис 2.34) Группа из семи различных вариантов, предлагаемых Intel Sense. Для перемещения между вариантами щелкаем на кнопку со стрелкой — вариант 1 of 7

    Следуя подсказке, мы могли указать и передать такие параметры:

    StreamWriter Pisat = new StreamWriter("1.txt");

    В этом случае файл 1.txt перезаписывался и текстовая строка помещалась в пустой файл. Для добавления в файл нужно указывать булевскую переменную:

    StreamWriter Pisat = new StreamWriter("1.txt", true);

    В этом случае можно было бы добавлять в файл текст, содержащий только латинские буквы. Для корректной записи кириллицы следует указывать кодировку, например Unicode, что мы и сделали:

    StreamWriter Pisat = new StreamWriter("1.txt", true, System.Text.Encoding.Unicode);

    Возможны и другие варианты передаваемых параметров — например, указание размера передаваемого буфера в байтах.

    Предположим, мы не знаем, какую именно кодировку следует указывать. В самом деле, Intel Sense на рис. 2 не указывает, что именно кодировка Unicode принадлежит пространству имен System.Text.Encoding. Но мы можем просмотреть все доступные объекты и свойства этого пространства, используя окно Object Browser (рис. 2.35).

    (рис 2.35) Пространство имен System.Text.Encoding в окне Object Browser

    В части окна Members отображаются и другие доступные кодировки, например, ASCII.

    Проверка существования файла

    При редактировании файла требуется определять его наличие на жестком диске для выбора потом варианта сохранения — целиком или только внесенных изменений. Создайте новое консольное приложение и назовите его FileExists. Следующий код демонстрирует проверку файла на диске:

    using System.IO;
    …
    
    [STAThread]
        static void Main(string[] args)
        {
          //Выводим сообщение
          Console.WriteLine("Ведите название файла");
          // Создаем переменную nazvaniefayla, в которую
          //запишем название файла, введеное пользователем.
          string nazvaniefayla = Console.ReadLine();
          //Проверяем наличие файла (в директории bin/Debug)
          if (File.Exists(nazvaniefayla))
          {
            //Если файл существует, выводим его в консольное окно.
            StreamReader chitat = new StreamReader(nazvaniefayla);
            string A;
            A = chitat.ReadToEnd();
            chitat.Close();
            Console.WriteLine(A);
          }
          else 
          {
            //Если файл не найден, выводим окно сообщения
    Сonsole.WriteLine("Файл с указанным именем не существует. Сoздать его Y/N");
            //Считываем результат пользователя
            string rezultatvibora = Console.ReadLine();
            if ((rezultatvibora=="Y")||(rezultatvibora=="y"))
            {
              //Создаем новый файл.
              StreamWriter pisat = new StreamWriter(nazvaniefayla);
              Console.WriteLine("Введите текст");
              string vvedemtext = Console.ReadLine();
              pisat.WriteLine(vvedemtext);
              pisat.Close();
              Console.WriteLine("Файл создан");
            }
            else 
            {
              // Если пользователь отказался от создания файла, выводим сообщение.
              Console.WriteLine("Ничего не будет созданно");
            }
          }
          
    
    
        }

    На диске, прилагаемом к книге, вы найдете приложение FileExists (Code\Glava2\FileExists).

    Работа с файловой системой Windows

    В рассмотренном выше примере мы проверяли наличие файла в папке Bin/Debug, где находится само приложение. В практических случаях подобная директория не может нас устраивать — требуется просматривать заданные каталоги и папки. В C# можно работать с файлами и каталогами, используя классы DirectoryInfo и FileInfo, предоставляющие всю информацию о файлах и папках.

    Класс DirectoryInfo обычно применяется для таких операций, как копирование, перемещение, переименование, создание и удаление папок. Этот класс содержит метод GetFiles(), который возвращает список файлов из текущего каталога.

    Класс FileInfo предоставляет методы для создания, копирования, удаления, перемещения и открытия файлов, а также является вспомогательным классом при создании объекта FileStream.

    Cоздайте новое консольное приложение и назовите его DirectoryInfo. Следующий простой код выводит список всех файлов, содержащихся на диске С:

    using System;
    using System.IO;
    
    namespace DirectoryInformation
    {
      
      class Class1
      {
      
        [STAThread]
        static void Main(string[] args)
        {
          //Создаем экземпляр di класса DirectoryInfo и указываем диск С в качестве директории
          DirectoryInfo directoriya = new DirectoryInfo("C:\\");
          // Получаем список всех папок, содержащихся на диске С:\
          DirectoryInfo[]  poddirectoriya = directoriya.GetDirectories();
          
          foreach (DirectoryInfo dd in poddirectoriya)
          {
            //Проверяем наличие доступа к каталогу
            if(dd.Attributes == FileAttributes.Directory)
            {
              // Получаем список всех файлов из каталога
              FileInfo[] spisokfile= dd.GetFiles();
              foreach (FileInfo fi in spisokfile)
              {
                //Выводим список файлов
                Console.WriteLine(fi.ToString());
              }
            }
          }
        }
        }
      }

    На диске, прилагаемом к книге, вы найдете приложение DirectoryInfo — Code\Glava2\DirectoryInfo.

    Чтение и запись двоичных файлов

    Для более общего способа чтения и записи файлов используются классы BinaryReader и BinaryWriter, предназначенные для чтения двоичных данных. Любой файл в конечном итоге представляет собой набор двоичных данных, следовательно, эти данные можно считывать и записывать во внутреннем, двоичном формате. Класс BinaryWriter представляет собой оболочку для байтового потока, управляющей записью двоичных данных. Класс BinaryReader является оболочкой, содержащей байтовый поток, и выполняет чтение двоичных данных.

    Рассмотрим приложение, позволяющее считывать файл формата MP3 и записывать фрагмент этого файла. Создайте новое консольное приложение и назовите его BinaryReadWrite. В папку bin/Debug поместите любой музыкальный файл формата MP3, имеющийся на вашем компьютере.

    Подключаем пространство имен:

    using System.IO;

    Добавляем код:

    [STAThread]
        static void Main(string[] args)
        {
          //Добавляем блок обработки исключений 
          try
          {
            //Создаем новый поток Chtenie  и указываем  файл для чтения — music.mp3
            FileStream Chtenie = new FileStream("music.mp3", FileMode.Open);
    
            //Создаем экземпляр  br класса BinaryReader и 
    //связываем  его с потоком Chtenie
            BinaryReader br = new BinaryReader(Chtenie);
    
            // Создаем массив типа байтов и 
            //передаем ему размер в байтах – (например, 1000000) записываемого файла
            byte[] n = br.ReadBytes(1000000);
            //Закрываем поток
            br.Close();
            //Создаем новый поток Zapis  и указываем  название 
    //файла для записи sozdanajamuzika.mp3
            FileStream Zapis = new FileStream("newmusic.mp3", FileMode.CreateNew, FileAccess.Write);
            //Создаем экземпляр  wr класса BinaryWriter и 
    //связываем  его с потоком Zapis
            BinaryWriter wr = new BinaryWriter(Zapis);
            // Можно связывать объект wr с потоком создавая его без названия:
    //BinaryWriter wr = new BinaryWriter(new FileStream("sozdanajamuzika.mp3", //FileMode.CreateNew, FileAccess.Write));
            //Передаем в поток массив байтов n
            wr.Write(n);
            //Закрываем поток
            br.Close();
          }
          catch (Exception ex)
          {
            Console.WriteLine(ex.Message);
          }
          
        }

    Запускаем приложение. Если все сработает без ошибок — на экране появится станадртное сообщение Press any key to continue, в противном случае — поскольку мы включили блок исключений — выйдет диагностическое сообщение. Перейдем в папку bin/Debug. В ней появился файл newmusic.mp3, прослушать который мы можем с помощью любого музыкального проигрывателя.

    Рассмотрим перечисления в конструкторах FileStream. Параметр FileMode определяет, каким образом открывается файл (рис. 2.36):

    (рис 2.36) Перечисления FileMode

    Значения перечисления FileMode указаны в таблице 2.4.

    Значение перечисления FileMode
    ЗначениеОписание
    Append Если файл существует, он открывается, если нет — создается новый. Данные добавляются в конец файла
    Create Создается новый выходной файл; если файл с таким названием существует, он перезаписывается
    CreateNew Создается новый выходной файл; если файл с таким названием уже существует, генерируется исключение IOException
    Open Открывается существующий файл; если файла с таким названием не существует, генерируется исключение FileNotFoundException
    OpenOrCreate Открывается существующий файл; если файл не существует, он создается
    Truncate Открывается существующий файл, но его длина усекается до нуля

    Параметр FileAccess определяет для пользователя разрешение на доступ к файлу (рис. 2.37):

    (рис 2.37) Перечисления FileAccess

    Значения перечисления FileAccess указаны в таблице 2.5.

    Значение перечисления FileAccess
    ЗначениеОписание
    Read Доступ на чтение из файла
    ReadWrite Разрешается чтение из файла и запись данных в файл
    Write Доступ для записи в файл

    Дополнительно для управления режимом доступа при одновременной работе с файлами нескольких потоков используется перечисление FileShare (рис. 2.38). Например, при создании потока для чтения файла мы могли полностью определить все параметры следующим образом:

    FileStream Chtenie = new FileStream("music.mp3", FileMode.Open, 
    FileAccess.Read, FileShare.Read);
    (рис 2.38) Перечисления FileShare

    Значения перечисления FileShare указаны в таблице 2.6.

    Значение перечисления FileShare
    ЗначениеОписание
    Inheritable Дает доступ к файлам, образованных текущим потоком
    None Запрещает совместное использование текущего файла. Пока файл открыт, доступ к нему будет блокирован
    Read Позволяет открывать текущей файл для чтения
    ReadWrite Позволяет открывать текущий файл для чтения или записи
    Write Позволяет открывать текущий файл для записи

    На диске, прилагаемом к книге, вы найдете приложение BinaryReadWrite (Code\Glava2\ BinaryReadWrite).

    Запуск программ из приложения

    Иногда в приложениях требуется запускать программы, входящие в состав операционной системы или установленные на данном компьютере. Создайте новое Windows-приложение и назовите его RunProgramm. На появившейся форме расположите три кнопки:

    button1, СвойстваЗначения свойств
    Name btnSystemInfo
    Size 168;23
    Text Информация о системе
    button2, СвойстваЗначения свойств
    Name btnRegEdit
    Size 168;23
    Text Редактор реестра
    button3, СвойстваЗначения свойств
    Name btnExplorer
    Size 168;23
    Text Проводник

    Располагаем кнопки в центре формы и выравниваем их, используя пункт меню Format. В коде формы подключаем пространство имен Diagnostics, позволяющее запускать приложения:

    using System.Diagnostics;

    Добавляем обработчики кнопок:

    private void btnSystemInfo_Click(object sender, System.EventArgs e)
        {
          //Добавляем блок обработки исключений — по разным причинам (например, 
          //система установлена на другом диске) приложение может быть недоступным.
          try
          {
            //Создаем новый экземпляр SysInfo класса Process
            Process SysInfo = new Process();
            // Если приложение не будет работать корректно — вызываем диалоговое окно ошибок.
            SysInfo.StartInfo.ErrorDialog = true;
            //Указываем путь к  запускаемому приложению "Сведения о системе"
            SysInfo.StartInfo.FileName = "C:\\Program Files\\Common Files\\Microsoft Shared\\MSInfo\\msinfo32.exe";
            //Запускаем SysInfo
            SysInfo.Start();
          }
          catch (Exception ex)
          {
            MessageBox.Show (ex.Message);
          }
        }
    
        private void btnRegEdit_Click(object sender, System.EventArgs e)
        {
          try
          {
            Process RegEdit = new Process();
            RegEdit.StartInfo.ErrorDialog = true;
            //Указываем путь к  запускаемому приложению "Редактор реестра"
            RegEdit.StartInfo.FileName = "C:\\Windows\\regedit.exe";
            RegEdit.Start();
          }
          catch (Exception ex)
          {
            MessageBox.Show (ex.Message);
          }
        
        }
    
        private void btnExplorer_Click(object sender, System.EventArgs e)
        {
          try
          {
            Process Explorer = new Process();
            Explorer.StartInfo.ErrorDialog = true;
            //Указываем путь к  запускаемому приложению "Проводник"
            Explorer.StartInfo.FileName = "C:\\Windows\\explorer.exe";
            Explorer.Start();
          }
          catch (Exception ex)
          {
            MessageBox.Show (ex.Message);
          }
        
        }

    Запускаем приложение. При нажатии на кнопки запускаются соответствующие приложения (рис. 2.39).

    (рис 2.39) Запуск программ из приложения RunProgramm

    Для запуска других программ или отдельных исполняемых файлов вашего приложения следует указывать их название и адрес, по которому они будут отыскиваться на клиентском компьютере.

    На диске, прилагаемом к книге, вы найдете приложение RunProgramm (Code\Glava2\RunProgramm).

    Листинг форм frmmain и blank проекта NotepadCSharp

    using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Data;
    using System.IO;
    
    namespace NotepadCSharp
    {
      
      public class frmmain : System.Windows.Forms.Form
      {
        private System.Windows.Forms.MainMenu mainMenu1;
        private System.Windows.Forms.MenuItem menuItem5;
        private System.Windows.Forms.MenuItem menuItem12;
        private System.Windows.Forms.MenuItem mnuFile;
        private System.Windows.Forms.MenuItem mnuNew;
        private System.Windows.Forms.MenuItem mnuOpen;
        private System.Windows.Forms.MenuItem mnuSave;
        private System.Windows.Forms.MenuItem mnuExit;
        private System.Windows.Forms.MenuItem mnuEdit;
        private System.Windows.Forms.OpenFileDialog openFileDialog1;
        private System.Windows.Forms.MenuItem mnuCut;
        private System.Windows.Forms.MenuItem mnuCopy;
        private System.Windows.Forms.MenuItem mnuPaste;
        private System.Windows.Forms.MenuItem mnuDelete;
        private System.Windows.Forms.MenuItem mnuSelectAll;
        private System.Windows.Forms.MenuItem mnuHelp;
        private System.Windows.Forms.MenuItem mnuAbout;
      
    
        private System.Windows.Forms.MenuItem mnuArrangeIcons;
        private System.Windows.Forms.MenuItem mnuCascade;
        private System.Windows.Forms.MenuItem mnuTileHorizontal;
        private System.Windows.Forms.MenuItem mnuTileVertical;
        private System.Windows.Forms.MenuItem mnuWindow;
        private int openDocuments = 0;
        private System.Windows.Forms.MenuItem mnuFormat;
        private System.Windows.Forms.SaveFileDialog saveFileDialog1;
        private System.Windows.Forms.MenuItem mnuSaveAs;
        private System.Windows.Forms.FontDialog fontDialog1;
        private System.Windows.Forms.MenuItem mnuFont;
        private System.Windows.Forms.MenuItem mnuColor;
        private System.Windows.Forms.ColorDialog colorDialog1;
        private System.Windows.Forms.MenuItem menuItem1;
        private System.Windows.Forms.MenuItem mnuFind;
        private System.Windows.Forms.ToolBarButton tbNew;
        private System.Windows.Forms.ToolBarButton tbOpen;
        private System.Windows.Forms.ToolBarButton tbSave;
        private System.Windows.Forms.ToolBarButton tbCut;
        private System.Windows.Forms.ToolBarButton tbCopy;
        private System.Windows.Forms.ToolBarButton tbPaste;
        private System.Windows.Forms.ImageList imageList1;
        private System.Windows.Forms.ToolBar toolBarMain;
        private System.ComponentModel.IContainer components;
      
    
        public frmmain()
        {
          //
          // Required for Windows Form Designer support
          //
          InitializeComponent();
          mnuSave.Enabled = false;
    
          //
          // TODO: Add any constructor code after InitializeComponent call
          //
        }
    
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
          if( disposing )
          {
            if (components != null) 
            {
              components.Dispose();
            }
          }
          base.Dispose( disposing );
        }
    
        region Windows Form Designer generated code
        
    
        [STAThread]
        static void Main() 
        {
          Application.Run(new frmmain());
        }
    
        
        private void mnuNew_Click(object sender, System.EventArgs e)
        {
          //Создаем новый экземпляр формы  frm
          blank  frm = new blank();
    
          frm.DocName = "Untitled " + ++openDocuments;
          //Указываем, что родительским контейнером 
          //нового экземпляра будет эта, главная форма.
          frm.MdiParent = this;
          frm.Text = frm.DocName;
          //Вызываем форму
          frm.Show();
        }
    
        private void mnuArrangeIcons_Click(object sender, System.EventArgs e)
        {
          this.LayoutMdi(MdiLayout.ArrangeIcons);
        }
    
      
    
        private void mnuCascade_Click(object sender, System.EventArgs e)
        {
          this.LayoutMdi(MdiLayout.Cascade);
        }
    
        private void mnuTileHorizontal_Click(object sender, System.EventArgs e)
        {
          this.LayoutMdi(MdiLayout.TileHorizontal);
        }
    
        private void mnuTileVertical_Click(object sender, System.EventArgs e)
        {
          this.LayoutMdi(MdiLayout.TileVertical);
        }
    
        private void mnuCut_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.Cut();
        }
    
        private void mnuCopy_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.Copy();
        }
    
        private void mnuPaste_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.Paste();
        }
    
        private void mnuDelete_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.Delete();
        }
    
        private void mnuSelectAll_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.SelectAll();
        }
    
        private void mnuOpen_Click(object sender, System.EventArgs e)
        {
          //Можно программно задавать доступные для обзора расширения файлов. 
          //openFileDialog1.Filter = "Text Files (*.txt)|*.txt|All Files(*.*)|*.*";
          
          
          //Если выбран диалог открытия файла, выполняем условие
          if (openFileDialog1.ShowDialog() == DialogResult.OK) 
          {
            //Создаем новый документ
            blank frm = new blank();
            //Вызываем метод Open формы blank
            frm.Open(openFileDialog1.FileName);
            //Указываем, что родительской формой является форма frmmain
            frm.MdiParent = this;
            //Присваиваем переменной DocName имя открываемого файла
            frm.DocName = openFileDialog1.FileName;
            //Свойству Text формы присваиваем переменную DocName
            frm.Text = frm.DocName;
            //Вызываем форму frm
            frm.Show();  
            mnuSave.Enabled = true;
          }
        
    
        }
    
        private void mnuSave_Click(object sender, System.EventArgs e)
        {
          //Переключаем фокус на данную форму.
          blank frm = (blank)this.ActiveMdiChild;
          //Вызываем метод Save формы blank
          frm.Save(frm.DocName);
          frm.IsSaved = true;
          
          
        }
    
        private void mnuSaveAs_Click(object sender, System.EventArgs e)
        {
          mnuSave.Enabled = true;
          //Можно программно задавать доступные для обзора расширения файлов 
          //openFileDialog1.Filter = "Text Files (*.txt)|*.txt|All Files(*.*)|*.*";
    
          //Если выбран диалог открытия файла, выполняем условие
          if (saveFileDialog1.ShowDialog() == DialogResult.OK) 
          {
            //Переключаем фокус на данную форму.
            blank frm = (blank)this.ActiveMdiChild;
            //Вызываем метод Save формы blank
            frm.Save(saveFileDialog1.FileName);
            //Указываем, что родительской формой является форма frmmain
            frm.MdiParent = this;
            //Присваиваем переменной FileName имя сохраняемого файла
            frm.DocName = saveFileDialog1.FileName;
            //Свойству Text формы присваиваем переменную DocName
            frm.Text = frm.DocName;    
            frm.IsSaved = true;
            
        
          }
    
        }
    
        private void mnuExit_Click(object sender, System.EventArgs e)
        {
          this.Close();
        }
    
        private void mnuFont_Click(object sender, System.EventArgs e)
        {
          //Переключаем фокус на данную форму.
          blank  frm = (blank)this.ActiveMdiChild;
          //Указываем, что родительской формой является форма frmmain  
          frm.MdiParent = this;
          //Вызываем диалог
          fontDialog1.ShowColor = true;
          //Связываем свойства SelectionFont и SelectionColor элемента RichTextBox 
          //с соответствующими свойствами диалога
          fontDialog1.Font = frm.richTextBox1.SelectionFont;
          fontDialog1.Color = frm.richTextBox1.SelectionColor; 
          //Если выбран диалог открытия файла, выполняем условие
          if (fontDialog1.ShowDialog() == DialogResult.OK)
          {
            frm.richTextBox1.SelectionFont = fontDialog1.Font;
            frm.richTextBox1.SelectionColor = fontDialog1.Color; 
          }
          //Показываем форму
          frm.Show();
        }
    
        private void mnuColor_Click(object sender, System.EventArgs e)
        {
          blank frm = (blank)this.ActiveMdiChild;
          frm.MdiParent = this;
          colorDialog1.Color = frm.richTextBox1.SelectionColor; 
          
          if (colorDialog1.ShowDialog() == DialogResult.OK)
          {
            frm.richTextBox1.SelectionColor = colorDialog1.Color; 
          }
    
          frm.Show();
        }
    
        
        private void mnuFind_Click(object sender, System.EventArgs e)
        {
          //Создаем новый экземпляр формы FindForm
          FindForm frm = new FindForm();
          //Если выбран результат DialogResult.Cancel, закрываем форму (до этого 
          //мы использовали DialogResult.OK
          if(frm.ShowDialog(this) == DialogResult.Cancel) return;
          ////Переключаем фокус на данную форму.
          blank form = (blank)this.ActiveMdiChild;
          ////Указываем, что родительской формой является форма frmmain  
          form.MdiParent = this;
          //Вводим переменную для поиска в определенной части текста —
          //поиск слова будет осуществляться от текущей позиции курсора
          int start = form.richTextBox1.SelectionStart;
          //Вызываем предопределенный метод Find элемента richTextBox1.
          form.richTextBox1.Find(frm.FindText, start, frm.FindCondition);
        }
    
        private void mnuAbout_Click(object sender, System.EventArgs e)
        {
          //Создаем новый экземпляр формы  About
          About  frm = new About();
          frm.Show();
        }
    
        private void toolBarMain_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e)
        {
          //New
          if (e.Button.Equals(tbNew)) 
          {
            mnuNew_Click(this, new EventArgs());
          }
          //Open
          if (e.Button.Equals(tbOpen))
          {
            mnuOpen_Click(this, new EventArgs());
          }
          //Save
          if (e.Button.Equals(tbSave))
          {
            mnuSave_Click(this, new EventArgs());
          }
          //Cut
          if (e.Button.Equals(tbCut))
          {
            mnuCut_Click(this, new EventArgs());
          }
          //Copy
          if (e.Button.Equals(tbCopy))
          {
            mnuCopy_Click(this, new EventArgs());
          }
          //Paste
          if (e.Button.Equals(tbPaste))
          {
            mnuPaste_Click(this, new EventArgs());
          }
        }
      }
    }
    using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.IO;
    
    namespace NotepadCSharp
    {
      /// <summary>
      /// Summary description for blank.
      /// </summary>
      public class blank : System.Windows.Forms.Form
      {
        public System.Windows.Forms.RichTextBox richTextBox1;
        public string DocName = "";
        private string BufferText = "";
        private System.Windows.Forms.ContextMenu contextMenu1;
        private System.Windows.Forms.MenuItem cmnuCut;
        private System.Windows.Forms.MenuItem cmnuCopy;
        private System.Windows.Forms.MenuItem cmnuPaste;
        private System.Windows.Forms.MenuItem cmnuDelete;
        private System.Windows.Forms.MenuItem menuItem6;
        private System.Windows.Forms.MenuItem cmnuSelectAll;
        public bool IsSaved = false;
        private System.Windows.Forms.StatusBar statusBar1;
        private System.Windows.Forms.StatusBarPanel sbAmount;
        private System.Windows.Forms.StatusBarPanel sbTime;
        
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;
    
    
    
        public blank()
        {
          
          InitializeComponent();
          //Свойству Text панели sbTime устанавливаем системное время, 
          // конвертировав его в тип String
          sbTime.Text = Convert.ToString(System.DateTime.Now.ToLongTimeString());
          //В тексте всплывающей подсказки  выводим текущую дату
          sbTime.ToolTipText = Convert.ToString(System.DateTime.Today.ToLongDateString());
        }
    
        
        protected override void Dispose( bool disposing )
        {
          if( disposing )
          {
            if(components != null)
            {
              components.Dispose();
            }
          }
          base.Dispose( disposing );
        }
    
        region Windows Form Designer generated code
        
    
        // Вырезание текста
        public void Cut()
        {
          this.BufferText = richTextBox1.SelectedText;
          richTextBox1.SelectedText = "";
        }
    
        // Копирование текста
        public void Copy()
        {
          this.BufferText = richTextBox1.SelectedText;
        }
    
        // Вставка
        public void Paste()
        {
          richTextBox1.SelectedText = this.BufferText;
        }
    
        // Выделение всего текста — используем свойство SelectAll элемента управления RichTextBox 
        public void SelectAll()
        {
          richTextBox1.SelectAll(); 
        }
    
        // Удаление.
        public void Delete()
        {
          richTextBox1.SelectedText = "";  
          this.BufferText = "";
        }
        //Создаем метод Open, в качестве параметра объявляем строку адреса  файла.
        public void Open(string OpenFileName)
        {
          //Если файл не выбран, возвращаемся назад (появится встроенное предупреждение)
          if (OpenFileName == "") 
          {
            return;
          }
          else
          {
            //Создаем новый объект StreamReader и передаем ему переменную OpenFileName
            StreamReader sr = new StreamReader(OpenFileName);
            //Читаем весь файл и записываем его в richTextBox1
            richTextBox1.Text = sr.ReadToEnd();
            // Закрываем поток
            sr.Close();
            //Переменной DocName присваиваем адресную строку.
            DocName = OpenFileName;
          }      
        }
    
        private void cmnuCut_Click(object sender, System.EventArgs e)
        {
          Cut();
        }
    
        private void cmnuCopy_Click(object sender, System.EventArgs e)
        {
          Copy();
        }
    
        private void cmnuPaste_Click(object sender, System.EventArgs e)
        {
          Paste();
        }
    
        private void cmnuDelete_Click(object sender, System.EventArgs e)
        {
          Delete();
        }
    
        private void cmnuSelectAll_Click(object sender, System.EventArgs e)
        {
          SelectAll();
        }
        //Создаем метод Save, в качестве параметра объявляем строку адреса  файла.
        public void Save(string SaveFileName)
        {
          //Если файл не выбран, возвращаемся назад (появится встроенное предупреждение)
          if (SaveFileName == "") 
          {
            return;
          }
          else
          {
            //Создаем новый объект StreamWriter и передаем ему переменную OpenFileName
            StreamWriter sw = new StreamWriter(SaveFileName);
            //Содержимое richTextBox1 записываем в файл
            sw.WriteLine(richTextBox1.Text);
            //Закрываем поток
            sw.Close();
            //Устанавливаем в качестве имени документа название сохраненного файла
            DocName = SaveFileName;
          }
        }
    
        private void blank_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
          //Если переменная IsSaved имеет значение true, т. е. документ новый документ 
          //был сохранен (Save As) или в открытом документе были сохранены изменения (Save), то выполняется условие
          if(IsSaved ==true)
            //Появляется диалоговое окно, предлагающее сохранить документ.
            if  (MessageBox.Show("Do you want save changes in " + this.DocName + "?", 
              "Message", MessageBoxButtons.YesNo, 
              MessageBoxIcon.Question) == DialogResult.Yes)
              //Если была нажата  кнопка Yes, вызываем метод Save
            {
              this.Save(this.DocName); 
            }    
         
        
        }
    
        private void richTextBox1_TextChanged(object sender, System.EventArgs e)
        {
          //Свойству Text панели sbAmount устанавливаем надпись "Аmount of symbols " 
          //и длину  текста в RichTextBox.
          sbAmount.Text = "Аmount of symbols " + richTextBox1.Text.Length.ToString(); 
        }      
        }
      }
    Вернуться к учебному плану