При нажатии на Сумма в примере ArbitraryMethod из Лекция 7, VS 2013 выдается ошибка: Необработанное исключение типа "System.InvalidOperationException" в System.Windows.Forms.dll Дополнительные сведения: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления "lblResult" не из того потока, в котором он был создан. Затем: Необработанное исключение типа "System.InvalidOperationException" в mscorlib.dll Дополнительные сведения: Для каждой асинхронной операции метод EndInvoke может вызываться только один раз. |
Работа с элементами управления
Контекстное меню
Контекстное меню, дублирующее некоторые действия основного меню, — не самый быстрый способ работы с программой, но самый привычный для пользователя. Элемент управления TextBox содержит в себе простейшее контекстное меню, дублирующее действия подменю Edit. Для того чтобы убедиться в этом, достаточно нанести этот элемент управления на форму и запустить приложение (рис. 2.8):
В нашем приложении Notepad C# в качестве текстового элемента мы используем RichTextBox. Добавим элемент управления contextMenu из окна ToolBox на форму blank. Добавляем пункты контекстного меню точно так же, как мы это делали для главного меню (рис. 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(); }Листинг 2.3.
Последнее, что нам осталось сделать, — это определить, где будет появляться контекстное меню. Элемент RichTextBox, так же как и формы frmmain и blank, имеет свойство ContextMenu, где мы и указываем contextMenu1, поскольку нам нужно отображать меню именно в текстовом поле. Запускаем приложение — теперь в любой точке текста доступно меню (рис. 2.10).
Диалоговые окна
Среда Visual Studio .NET содержит готовые диалоговые окна, предлагающие выбрать файл для открывания или путь на диске для сохранений текущего файла (рис. 2.11).
Мы рассмотрим одну часть диалоговых окон в этой лекции, а другую, относящуюся к подготовке документа к печати, — в "Работа с печатью и изображениями" .
OpenFileDialog
Добавьте на форму frmmain элемент управления OpenFileDialog из окна панели инструментов ToolBox. Подобно элементу MainMenu, он будет располагаться на панели невидимых компонент (рис. 2.12):
Свойство 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; } }2.4.
Добавим обработчик пункта меню 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.5.
Запускаем приложение и открываем текстовый файл, сохраненный в формате блокнота (рис. 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; } }2.6.
Добавляем обработчик пункта меню 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).
Конечно, эту задачу можно решить более элегантно — совместив оба пункта сохранения документа в один. Но мне хотелось показать разницу между простым сохранением и перезаписью файлов, кроме того, мы научились отключать доступность пунктов меню.