Простое Windows-приложение — текстовый редактор
Рассмотрим пример проектирования стандартного оконного приложения. Простейшая последовательность действий:
1) визуальное проектирование интерфейса (перенос на форму с Панели элементов необходимых визуальных и не визуальных элементов);
2) генерация заготовок методов обработки событий, связанных с элементами управления;
3) программирование методов обработки событий.
Постановка задачи
Создать текстовый редактор с обязательными функциями работы с файлами «Открыть» и «Сохранить как», а также функциями редактирования текста. Выбор действий с файлами осуществлять через главное меню.
Реализация
Разместим на форме визуальный элемент textBox1 класса TextBox. Размер элемента сделайте чуть меньше размера формы, сместив его вниз от заголовка на 30-40 пикселей. Задайте свойство textBox1.MultiLine = true (для редактирования текста в несколько строк).
Перетащите с Панели элементов компонент menuStrip1 класса MenuStrip для создания меню.В левом верхнем углу рабочей области формы появится кнопка «Введите здесь» , а на панели невизульных компонентов отобразится элемент menuStrip1.
Для выбора имен файлов для их чтения и записи перетащим на эту же панель элементы openFileDialog1 (класс OpenFileDialog) и saveFileDialog1 (класс SaveFileDialog).
Кликнув по кнопке «Введите здесь», введите имя раздела меню «Файл» и добавьте ниже следующие пункты меню работы с файлами «Открыть», «Сохранить как» и «Выход». Ваша форма (вместе с панелью невизуальных элементов) будет выглядеть примерно так:
Примечание: для наглядности изменено свойство формы BackColor = Color.Peru. Первая группа действий закончена.
Вторая группа действий обеспечивает генерацию заголовков методов обработки событий, связанных к кнопками меню. Для этого дважды нажмите каждую из трех позиций меню, а также событию Load формы Form1 на закладке «События» панели «Свойства» поставьте в соответствие метод Form1_Load (двойной клик справа от в строке Load).
Откроем форму в режиме Кода (файл Form1.cs):
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace ТекстовыйРедактор < public partial class Form1 : Form < public Form1() < InitializeComponent(); >private void открытьToolStripMenuItem_Click(object sender, EventArgs e) < >private void сохранитьКакToolStripMenuItem_Click(object sender, EventArgs e) < >private void выходToolStripMenuItem_Click(object sender, EventArgs e) < >private void Form1_Load(object sender, EventArgs e) < >> >
Перейдем к третьей группе действий — написанию кода для этих четырех методов.
Метод Form1_Load( ) используем для очистки поля компонента textBox1, для задания форматов файловых диалогов и имени файла — контрольного примера при его открытии:
private void Form1_Load(object sender, EventArgs e)
Комментарий. При загрузке формы мы задаем свойство FileName объекта openFileDialog1 указанием имени файла для открытия, а также задаем фильтры для диалогов открытия и сохранения файлов. Сравните работу программы без использования этого метода.
В методе открытьToolStripMenuItem_Click( ) используется компонент openFileDialog1 для выбора имени файла для чтения. Если имя не выбрано (FileName = String.Empty), то работа метода завершается. Иначе создается новый экземпляр класса System.IO.StreamReader (var Читатель) с указанием имени файла и кодировки, данные из текстового файла переносятся в textBox1, объект Читатель закрывается. Добавлена обработка исключений, которые могут возникнуть при открытии файла:
private void открытьToolStripMenuItem_Click(object sender, EventArgs e) < openFileDialog1.ShowDialog(); if (openFileDialog1.FileName == String.Empty) return; // Чтение текстового файла try < var Читатель = new System.IO.StreamReader( openFileDialog1.FileName, Encoding.GetEncoding(1251)); textBox1.Text = Читатель.ReadToEnd(); Читатель.Close(); >catch (System.IO.FileNotFoundException Ситуация) < MessageBox.Show(Ситуация.Message + "\nНет такого файла", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); >catch (Exception Ситуация) < // отчет о других ошибках MessageBox.Show(Ситуация.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); >>
Аналогично выполняется запись (сохранение) текстового файла:
private void сохранитьКакToolStripMenuItem_Click(object sender,EventArgs e) < saveFileDialog1.FileName = openFileDialog1.FileName; if (saveFileDialog1.ShowDialog() == DialogResult.OK) < try < var Писатель = new System.IO.StreamWriter( saveFileDialog1.FileName, false, System.Text.Encoding.GetEncoding(1251)); Писатель.Write(textBox1.Text); Писатель.Close(); >catch (Exception Ситуация) < // отчет о других ошибках MessageBox.Show(Ситуация.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); >> >
Последний метод — закрытие формы — реализуется одним оператором:
private void выходToolStripMenuItem_Click(object sender, EventArgs e)
Подготовим в блокноте текстовый файл Text2.txt и разместим его в подкаталоге data папки, где будет размещено ваше приложение. Запустим программу на выполнение. Добавьте в окне редактора несколько строк, сохраните файл с другим именем. Откройте новый файл и удалите часть текста в окне редактора.
Заметим, что работают стандартные операции выделения текста, копирования, вставки и удаления части текста с использованием мыши и комбинаций управляющих клавиш, используемых в известных текстовых редакторах.
ВЫВОД: В первом приближении поставленная задача решена. Данный пример приведен всего лишь для понимания:
во-первых, алгоритма действий разработчика визуального приложения «интерфейс, события-методы, реализация»;
во-вторых, удобства использования готовых компонентов (нам не пришлось программировать операции редактирования текста — стандартные события уже привязаны к компоненту textBox1);
в-третьих, целесообразности использования готовых компонентов (файловых диалогов) с точки зрения стандартизации интерфейса пользователя и программы.
Напомним, что в статье про классы мы отмечали, что в языке C# предусмотрено несколько разновидностей данных-членов и функций-членов. Пока мы подробно рассмотрели только поля и константы — как данные-члены, а также методы — как функции-члены класса. В следующих статьях мы рассмотрим события, как данные-члены класса и трехзвенную цепочку события-делегаты-методы. После чего вернемся к член-функциям класса: свойствам, конструкторам, финализаторам, операциям и индексаторам.
NEW: Наш Чат, в котором вы можете обсудить любые вопросы, идеи, поделиться опытом или связаться с администраторами.
Как создать насыщенный текстовый редактор
Вашему вниманию представляется еще одна статья, вдохновленная тем, насколько крутым является элемент управления RichTextBox и как легко он позволяет создавать небольшой, но достаточно мощный текстовый редактор, наподобие Windows Wordpad! WPF создает все условия для легкой реализации приложения, но, несмотря на это, будет немного больше кода XAML и C#, чем обычно и это нормально. Мы пройдемся по каждому разделу в отдельности, а в конце, я покажу вам весь код целиком.
В этой статье мы будем использовать множество элементов управления и методов, которые мы использовали в других руководствах, поэтому объяснения не будут чересчур подробными. В случае, если вам нужно освежить некоторые знания, то вы всегда можете вернуться к подробным описаниям.
Для начала давайте посмотрим, к чему мы стремимся. Таков должен быть конечный результат:
Интерфейс
Интерфейс состоит из панели инструментов (ToolBar) с кнопками (Button) и полями со списком (ComboBox). Есть кнопки для загрузки и сохранения документа, кнопки для управления толщиной шрифта и его стилем написания, а также два поля со списком для управления семейством и размером шрифта.
Под панелью инструментов (ToolBar) находится элемент управления RichTextBox, в котором будут выполняться все текстовые изменения.
Команды
Первое, что вы можете заметить, это использование команд WPF, которые мы уже обсуждали ранее в этой статье. Мы используем элемент Open (Открыть) и Save (Сохранить) от класса ApplicationCommands, чтобы загрузить и сохранить документ, и мы используем ToggleBold, ToggleItalic и ToggleUnderline из класса EditingCommands для редактирования кнопок.
Преимущество использования команд очевидно, так как элемент управления RichTextBox уже реализует команды ToggleBold, ToggleItalic и ToggleUnderline. Это означает, что нам не нужно писать код, чтобы они работали. Просто подключите их к назначенной кнопке, и всё:
Таким образом, после подключения команд, мы активировали сочетание клавиш для быстрого редактирования текста. К примеру, Ctrl+B активирует жирный шрифт, Ctrl+I — курсив, а Ctrl+U, подчеркнутый текст.
Обратите внимание, что я использую ToggleButton вместо обычного элемента Button. Я хочу, чтобы кнопка была «проверяемой» — если выбрано выделение полужирным шрифтом, то кнопка будет активна (свойство IsChecked) у ToggleButton. К сожалению, WPF не приспособлен к такому, поэтому нам нужно написать немного кода для обновления состояний кнопки и Combobox. Более подробно мы поговорим об этом чуть позднее.
Команды Open и Save не могут быть обработаны автоматически, поэтому нам придется сделать это, как обычно, с привязкой CommandBinding для окна, а затем обработчиком событий в коде:
Чуть позднее я покажу реализацию этих команд.
Стиль и размер шрифта
Для того, чтобы изменить шрифт и его размер, у нас есть несколько полей со списком (ComboBox). Они заполняются системными шрифтами, а также выбором возможных размеров, например:
public RichTextEditorSample() < InitializeComponent(); cmbFontFamily.ItemsSource = Fonts.SystemFontFamilies.OrderBy(f =>f.Source); cmbFontSize.ItemsSource = new List() < 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72 >; >
Благодаря тому, что WPF включает в себя простое создание списка возможных шрифтов, с помощью свойства SystemFontFamilies, мы может с лёгкостью реализовать это. Поскольку список размеров является скорее рекомендацией, то мы делаем этот элемент управления ComboBox редактируемым, чтобы пользователь мог ввести желаемый размер самостоятельно:
Также это означает, что мы будем обрабатывать изменения по-разному. Для изменения шрифта в ComboBox мы можем просто обрабатывать событие SelectionChanged, подключаясь к событию TextBoxBase.TextChanged, чтобы обработать, введенный пользователем размер шрифта и его стиль.
WPF обрабатывает команды Bold, Italic и Underline самостоятельно, но для стиля шрифта и его размера нам придется вручную изменить некоторые значения. К счастью, это довольно легко сделать, используя метод ApplyPropertyValue(). Вышеупомянутые обработчики событий выглядят следующим образом.
private void cmbFontFamily_SelectionChanged(object sender, SelectionChangedEventArgs e) < if(cmbFontFamily.SelectedItem != null) rtbEditor.Selection.ApplyPropertyValue(Inline.FontFamilyProperty, cmbFontFamily.SelectedItem); >private void cmbFontSize_TextChanged(object sender, TextChangedEventArgs e)
Тут нет ничего особенного. Мы просто передаем выбранное или введенное значение методу ApplyPropertyValue() вместе со свойством, которое мы хотим изменить.
Обновление состояния
Как упоминалось ранее, WPF обрабатывает команды Bold, Italic и Underline самостоятельно, но мы должны вручную обновить состояние этих кнопок, так как команды не могут это осуществить. Поэтому мы также должны обновить два поля со списком (ComboBox), чтобы отразить текущее шрифты и размеры.
Нам необходимо обновить состояние, как только курсор перемещается и/или изменяется выделение, и для этого событие SelectionChanged в RichTextBox идеально подходит. Таким образом мы получаем:
private void rtbEditor_SelectionChanged(object sender, RoutedEventArgs e)
По факту, совсем немного строк, но самую главную работу совершают лишь пара из них. Нам необходимо слегка изменить их, чтобы обновить каждую из трёх кнопок и два поля со списком.
Принцип работы довольно прост. Для кнопок мы используем метод GetPropertyValue(), чтобы получить текущее значение для текстового свойства, например FontWeight, а затем обновляем свойство IsChecked в зависимости от того, является ли возвращаемое значение таким же, как то, что мы хотим или нет.
Для полей со списком (ComboBox) мы делаем то же самое, но вместо установки свойства IsChecked мы задаем свойства SelectedItem или Text, с возвращаемыми значениями.
Загрузка и сохранение файла
При обработке команд Open и Save мы используем очень похожий код:
private void Open_Executed(object sender, ExecutedRoutedEventArgs e) < OpenFileDialog dlg = new OpenFileDialog(); dlg.Filter = "Rich Text Format (*.rtf)|*.rtf|All files (*.*)|*.*"; if(dlg.ShowDialog() == true) < FileStream fileStream = new FileStream(dlg.FileName, FileMode.Open); TextRange range = new TextRange(rtbEditor.Document.ContentStart, rtbEditor.Document.ContentEnd); range.Load(fileStream, DataFormats.Rtf); >> private void Save_Executed(object sender, ExecutedRoutedEventArgs e) < SaveFileDialog dlg = new SaveFileDialog(); dlg.Filter = "Rich Text Format (*.rtf)|*.rtf|All files (*.*)|*.*"; if(dlg.ShowDialog() == true) < FileStream fileStream = new FileStream(dlg.FileName, FileMode.Create); TextRange range = new TextRange(rtbEditor.Document.ContentStart, rtbEditor.Document.ContentEnd); range.Save(fileStream, DataFormats.Rtf); >>
OpenFileDialog или SaveFileDialog используется для указания местоположения и имени файла, а затем текст загружается или сохраняется с помощью объекта TextRange, который мы получаем непосредственно из RichTextBox, в сочетании с FileStream, который обеспечивает доступ к физическому файлу. Файл загружается и сохраняется в формате RTF, но вы можете указать любой другой текстовый формат на выбор, к примеру обычный текст (txt).
Полный пример
Ниже представлен код всего приложения, первоначально на XAML, а затем на C#:
using System; using System.Linq; using System.Collections.Generic; using System.IO; using System.Windows; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using Microsoft.Win32; using System.Windows.Controls; namespace WpfTutorialSamples.Rich_text_controls < public partial class RichTextEditorSample : Window < public RichTextEditorSample() < InitializeComponent(); cmbFontFamily.ItemsSource = Fonts.SystemFontFamilies.OrderBy(f =>f.Source); cmbFontSize.ItemsSource = new List() < 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72 >; > private void rtbEditor_SelectionChanged(object sender, RoutedEventArgs e) < object temp = rtbEditor.Selection.GetPropertyValue(Inline.FontWeightProperty); btnBold.IsChecked = (temp != DependencyProperty.UnsetValue) && (temp.Equals(FontWeights.Bold)); temp = rtbEditor.Selection.GetPropertyValue(Inline.FontStyleProperty); btnItalic.IsChecked = (temp != DependencyProperty.UnsetValue) && (temp.Equals(FontStyles.Italic)); temp = rtbEditor.Selection.GetPropertyValue(Inline.TextDecorationsProperty); btnUnderline.IsChecked = (temp != DependencyProperty.UnsetValue) && (temp.Equals(TextDecorations.Underline)); temp = rtbEditor.Selection.GetPropertyValue(Inline.FontFamilyProperty); cmbFontFamily.SelectedItem = temp; temp = rtbEditor.Selection.GetPropertyValue(Inline.FontSizeProperty); cmbFontSize.Text = temp.ToString(); >private void Open_Executed(object sender, ExecutedRoutedEventArgs e) < OpenFileDialog dlg = new OpenFileDialog(); dlg.Filter = "Rich Text Format (*.rtf)|*.rtf|All files (*.*)|*.*"; if(dlg.ShowDialog() == true) < FileStream fileStream = new FileStream(dlg.FileName, FileMode.Open); TextRange range = new TextRange(rtbEditor.Document.ContentStart, rtbEditor.Document.ContentEnd); range.Load(fileStream, DataFormats.Rtf); >> private void Save_Executed(object sender, ExecutedRoutedEventArgs e) < SaveFileDialog dlg = new SaveFileDialog(); dlg.Filter = "Rich Text Format (*.rtf)|*.rtf|All files (*.*)|*.*"; if(dlg.ShowDialog() == true) < FileStream fileStream = new FileStream(dlg.FileName, FileMode.Create); TextRange range = new TextRange(rtbEditor.Document.ContentStart, rtbEditor.Document.ContentEnd); range.Save(fileStream, DataFormats.Rtf); >> private void cmbFontFamily_SelectionChanged(object sender, SelectionChangedEventArgs e) < if(cmbFontFamily.SelectedItem != null) rtbEditor.Selection.ApplyPropertyValue(Inline.FontFamilyProperty, cmbFontFamily.SelectedItem); >private void cmbFontSize_TextChanged(object sender, TextChangedEventArgs e) < rtbEditor.Selection.ApplyPropertyValue(Inline.FontSizeProperty, cmbFontSize.Text); >> >
Ниже представлен скриншот, где мы выбрали текст. Обратите внимание, как элементы управления панели инструментов отражают состояние текущего выделения:
Итог
Как вы можете видеть, создать текстовый редактор в WPF очень просто, особенно из-за отличного элемента управления RichTextBox. Если вы хотите, вы можете легко расширить этот пример такими вещами, как выравнивание текста, выбор цвета, списки и даже таблицы.
Имейте в виду, что приведенный выше пример должен работать нормально, нет абсолютно никакой обработки исключений или какой-либо проверки кода. Существуют несколько мест, которые могут легко вызвать ошибки, например, размер шрифта в поле со списком (ComboBox) может вызвать ошибку, если ввести в него не числовое значение. Поэтому, если вы хотите доработать функциональность для дальнейшей работы с этим примером, то вы должны проверить его на ошибки.
This article has been fully translated into the following languages:
Is your preferred language not on the list? Click here to help us translate this article into your language!
Проект не для начинающих: пишем свой текстовый редактор с поиском и подсветкой синтаксиса на C
Предлагаем вашему вниманию серию англоязычных статей, в которой подробно разбирается процесс разработки собственного текстового редактора на языке C.
Предлагаем вашему вниманию серию англоязычных статей, опубликованную в блоге Джереми Рутена. В ней подробно разбирается процесс разработки собственного текстового редактора на языке C.
Проект занимает примерно тысячу строк кода и не использует никаких зависимостей. Для удобства и лучшего понимания весь процесс разбит на 184 шага. По прохождении каждого этапа вы сможете скомпилировать проект и увидеть все изменения. Исходный код каждого шага доступен на GitHub.
Статьи написаны простым, понятным даже начинающим программистам языком. Все термины либо объясняются в статье, либо снабжены ссылками на другие Интернет-ресурсы.
Реализация этого проекта однозначно будет вам полезна: во-первых, вы сможете освоить или подтянуть знания по языку C, а во-вторых, разберётесь в основных принципах работы текстовых редакторов.
Итак, вот список статей:
- Настройка.
- Переход в «сырой» режим.
- «Сырой» ввод и вывод.
- Отображение текста.
- Редактор текста.
- Поиск.
- Подсветка синтаксиса.
- Примечания.
Если вам хочется написать ещё какой-нибудь проект самостоятельно, советуем обратить внимание на серию статей, посвящённую созданию ОС на ассемблере и Rust.
Следите за новыми постами по любимым темам
Подпишитесь на интересующие вас теги, чтобы следить за новыми постами и быть в курсе событий.
Делаем свой текстовый редактор с автосохранением
Сделать собственный текстовый редактор гораздо проще, чем кажется. Сейчас мы соорудим такой, и вы сами в этом убедитесь.
В этой статье мы соберем текстовый редактор, который будет работать в браузере. В помощь нам три технологии:
- JavaScript — язык, на котором всё будет работать. Это не лучший язык для полноценных приложений, но с ним вы сможете запрограммировать текстовый редактор прямо в браузере.
- Contenteditable — возможность языка HTML делать какие-то части страницы редактируемыми. Обычно со страницы можно только читать, но благодаря этому свойству можно еще и писать.
- Localstorage — особая область памяти, которая позволяет сохранить что-нибудь для конкретной страницы в браузере. Ваш браузер будет помнить, что вы ввели конкретно в этой странице. Это самая интересная часть.
Общая идея
У нас будет HTML-страница, на ней будет блок, похожий на лист бумаги. У него будет включен content editable, то есть внутри этого блока можно будет что-то писать. После каждого нажатия клавиши содержимое этого блока будет записываться во внутреннюю память браузера.
Вторая часть алгоритма — при загрузке страницы взять из памяти тот текст, что там был раньше, и показать его в нашей текстовой области. Страницу можно обновлять как угодно и даже выключать компьютер — текст всё равно останется в памяти.
Если записать алгоритм кратко, то он будет выглядеть так:
- Достаём из памяти тот текст, который там был
- Выводим его в нашу область для редактирования
- Постоянно смотрим, нажата ли какая-нибудь клавиша
- Если нажата — сразу записываем изменения в память.
Пункты 3 и 4 выполняются непрерывно до тех пор, пока вы не закроете страницу.
Готовим каркас
Пойдём уже привычным способом и создадим новый HTML-файл, в котором будут прописаны все нужные блоки. Мы так уже делали в статьях про генератор паролей и спортивный таймер.
Сохраняем как html-файл, открываем его в браузере и видим пустой экран. Это нормально, сейчас будем наполнять.
Расставляем содержимое
Нам нужен только заголовок, который объяснит нам, где мы находимся, и большое пространство для ввода текста. За текстовое поле будет отвечать блок со свойством contenteditable. Это свойство разрешает редактировать текст в блоке как угодно.
Разместим это в разделе :
Текстовый редактор с автосохранением
Настраиваем стили
Стили задают внешний вид страницы и любых элементов на ней. Сделаем наш заголовок опрятнее:
/*задаём общие параметры для всей страницы: шрифт и отступы*/ body < text-align: center; margin: 10; font-family: Verdana, Arial, sans-serif; font-size: 16px; >/*закончили со стилями*/
Сохраняем, обновляем и смотрим на результат:
Пишем скрипт
Теперь нужно научить редактор сначала извлекать из памяти прошлый текст, а потом снова запоминать каждое нажатие клавиши. Всё будем делать через localStorage, как с ним работать — рассказываем в статье про список задач.
// если в нашем хранилище уже что-то есть… if (localStorage.getItem('text_in_editor') !== null) < // …то отображаем его содержимое в нашем редакторе document.getElementById('editor').innerHTML = localStorage.getItem('text_in_editor'); >// отслеживаем каждое нажатие клавиши и при каждом нажатии выполняем команду document.addEventListener('keydown', function (e) < // записываем содержимое нашего редактора в хранилище localStorage.setItem('text_in_editor', document.getElementById('editor').innerHTML); >);
Кладём это в раздел и смотрим, что получилось:
Общий код страницы
Текстовый редактор Текстовый редактор с автосохранением
В следующих сериях
Сделаем нашему редактору приличный внешний вид — чтобы был похож на лист бумаги с тенью.
Добавим возможность менять документы и создавать новые.
Добавим каждой заметке заголовок.
Подписывайтесь на наши соцсети, и как только выйдет новая версия, мы вам расскажем.
Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.