Предложено написать программу на C# , повторяющую функционал стандартного блокнота Windows. В дальнейшем дополним его своим функционалом. В работе используется MS Visual Studio 2017 (C# Windows Forms).

Основной функционал:
- Работа с файлами (создание, открытие, сохранение, параметры страницы печати, печать)
- Правка текста (вырезать, вставить, копировать, отменить действие, удалить текст, выделить весь текст)
- Поиск и замена текста
- Переход к указанной строке
- Вставка текущих даты и времени
- Форматирование текста (выбор шрифта, размера текста, его стиля)
- Настройки приложения (сохранение измененных параметров)
Сперва создадим внешнюю оболочку программы. Добавим в проект следующие формы:
- MainForm — Основная форма нашего блокнота (Создана изначально как Form1, для удобства переименуем её в MainForm)
- GoToForm — Форма для перехода к указанной пользователем строке
- SearchForm — Форма для поиска и замены текста
- AboutForm — Форма с информацией о программе
В процессе работы с программой мы будем изменять названия добавляемых элементов со стандартных (toopStripMenuItem1. toopStripMenuItem10) на более удобные нам. Такие имена я буду помещать в скобки напротив названия элементов.
Как использовать ваш смартфон вместо записной книжки
Работаем с MainForm
Добавим на форму элемент menuStrip1 (menuStrip).

Теперь создадим подменю для этих элементов и в их свойствах сразу укажем горячие клавиши (поле «ShortcutKeys»). Добавляем элементы в меню «Файл»:
- «Создать» (mFileNew): Ctrl + N
- «Открыть. » (mFileOpen): Ctrl + O
- «Сохранить» (mFileSave): Ctrl + S
- «Сохранить как. » (mFileSaveAs): Ctrl + Alt + S
- «Параметры страницы» (mFilePageParam)
- «Печать. » (mFilePrint): Ctrl + P
- «Выход» (mFileExit): Alt + F4
Элементы меню «Правка»:
- «Отменить» (mEditCancel): Ctrl + Z
- «Вырезать» (mEditCut): Ctrl + X
- «Копировать» (mEditCopy): Ctrl + C
- «Вставить» (mEditPaste): Ctrl + V
- «Удалить» (mEditDel): Del
- «Найти и заменить» (mEditFind): Ctrl + F
- «Перейти» (mEditGo): Ctrl + G
- «Выделить всё» (mEditGiveAll): Ctrl + A
- «Время и дата» (mEditTime): F5
Элементы меню «Формат»:
- «Перенос по словам» (mFormatTransfer). В свойствах этого элемента изменим значение поля «CheckOnClick» на True.
- «Шрифт» (mFormatFont)
Элементы меню «Вид»:
Программа Onenote — записная книжка
- «Строка состояния» (mViewStatusStrip). В свойствах этого элемента изменим параметр «CheckState» на Checked и «CheckOnClick» на True.
Элементы меню «Справка»:
- «О программе» (mHelpAboutProgram)

Формирование меню завершено. Теперь добавим на форму элемент StatusStrip1 (StatusStrip). Это будет строка состояния нашего блокнота.
![]() |
| statusStrip |
В неё мы добавим 8 элементов StatusLabel:
- «Количество строк:» (statusLab1)
- «0» (statusLinesCount)
- «Количество слов:» (statusLab2)
- «0» (statusWordsCount)
- «Символов с пробелами:» (statusLab3)
- «0» (statusCharSpaceCount)
- «Символов без пробелов:» (statusLab4)
- «0» (statusCharCount)
Теперь перейдем к главному. Добавим на форму элемент textBox1 (notebox). В его свойствах необходимо установить значение True для поля «Multiline». Для поля «Dock» установим значение Fill, чтобы наш textBox заполнил собой всё свободное пространство формы. Для поля «Scrollbars» выбираем значение Both.
Если после установки значения Fill в поле «Dock» ваш textBox закрыл собой строку состояния или меню, то нужно просто клинкуть на него правой кнопкой мыши и нажимать пункт «На передний план» пока исчезнувшие элементы не станут видимыми.
Заранее стоит установить модификатор доступа Public для поля «Modifers», чтобы позднее можно было взаимодействовать с нашим textBox через другие формы.

Работа с внешним видом основной формы завершена.
Работаем с GoToForm
Добавим в проект новую форму для перехода к указанной пользователем строке. Выбираем Проект — Добавить форму Windows. — Форма Windows Forms. Указываем имя формы «GoToForm.cs».
На форму мы добавляем элементы:

В свойствах формы указываем следующие значения для полей:
- FormBorderStyle — «FixedSingle» — Запрет изменения размеров окна формы
- MinimizeBox — «False» — Запрет сворачивания окна формы
- MaximizeBox — «False» — Запрет разворачивания окна формы
- ShowIcon — «False» — Запрет отображения собственной иконки окна формы
- ShowInTaskbar — «False» — Запрет отображения окна формы на панели задач
- Text — «Перейти» — Название формы
- TopMost — «True» — Окно формы будет расположено поверх остальных окон
Для элемента textBox (tbGo) указываем в поле «Modifers» значение Public.
Работаем с SearchForm
Добавим в проект новую форму для перехода к указанной пользователем строке. Выбираем Проект — Добавить форму Windows. — Форма Windows Forms. Указываем имя формы «SearchForm.cs».
Добавим на форму элементы:
- label (2 шт.) — в свойстве «Text» этих элементов напишем «Найти:» и «Заменить на:»
- textBox1 (tbFind)
- textBox2 (tbReplace)
- checkBox1 (cbReg) — в свойстве «Text» напишем «Учитывать регистр»
- button1 (btFind) — в свойстве «Text» напишем «Найти далее»
- button2 (btReplace) — в свойстве «Text» напишем «Заменить»
- button3 (btReplaceAll) — в свойстве «Text» напишем «Заменить всё»
- button4 (btCancel) — в свойстве «Text» напишем «Отмена»
![]() |
| SearchForm.cs |
Также в свойствах формы укажем значения аналогичные форме GoToForm.
Работаем с AboutForm
Добавим в проект новую форму для перехода к указанной пользователем строке. Выбираем Проект — Добавить форму Windows. — Окно «О программе». Указываем имя формы «AboutForm.cs». В вашем проекте появится такая форма:

По-умолчанию значения полей в форме заполняются автоматически и берутся из сведений о проекте: «Проект» — «Свойства» — «Приложение» — «Сведения о сборке. «. Вы можете изменять эти сведения на своё усмотрение.
![]() |
| Сведения о сборке |
На этом работа с внешним видом нашего блокнота завершена. Уже сейчас вы можете запустить проект и посмотреть как всё выглядит. Однако, пока это лишь «бездушная» оболочка. В следующих частях мы наполним её всеми функциями стандартного блокнота Windows.
Источник: vshatunoff.blogspot.com
Пишем приложение для заметок на JavaScript

Сегодня мы с вами, как следует из названия, напишем простое приложение для формирования и хранения заметок.
Возможности нашего приложения будут следующими:
- Создание заметки.
- Хранение заметок.
- Удаление заметки.
- Отметка о выполнении задачи.
- Информация о дате выполнения задачи.
- Напоминание о необходимости выполнения задачи.
Заметки будут храниться в индексированной базе данных (IndexedDB). Для облегчения работы с IndexedDB будет использована эта библиотека. Как заявляют разработчики данной библиотеки, она представляет собой «тоже самое, что и IndexedDB, но с промисами».
Предполагается, что вы знакомы с азами IndexedDB. Если нет, то прежде чем продолжить рекомендую прочитать эту статью.
Я понимаю, что для решения такой задачи, как хранение заметок, вполне достаточно LocalStorage. Однако, мне хотелось исследовать некоторые возможности IndexedDB. Таким образом, выбор в пользу последней был сделан исключительно из гносеологических соображений. В конце будут приведены ссылки на похожее приложение, где хранение данных реализовано с помощью LocalStorage.
Итак, поехали.
Наша разметка выглядит так:
- Поля для ввода можно было создать с помощью тегов «figure» и «figcaption». Это было бы так сказать «семантичнее».
- Как впоследствии оказалось, выбор тега «input» с типом «date», был не лучшим решением. Об этом ниже.
- В одном из приложений напоминания (уведомления) реализованы с помощью Notifications API. Однако мне показалось странным запрашивать у пользователя разрешение на показ уведомлений и добавлять возможность их отключения, поскольку, во-первых, когда мы говорим о приложении для заметок (задач), напоминания подразумеваются, во-вторых, их можно реализовать так, чтобы они не раздражали пользователя при многократном появлении, т.е. ненавязчиво.
- Изначально в приложении предусматривалась возможность указывать не только дату, но и время напоминания. Впоследствии я решил, что даты достаточно. Впрочем, при желании ее легко добавить.
Подключаем стили:
* < margin: 0; padding: 0; box-sizing: border-box; >body < height: 100vh; background: radial-gradient(circle, skyblue, steelblue); display: flex; flex-wrap: wrap; justify-content: center; align-items: center; font-family: ‘Stylish’, sans-serif; font-size: 1.2em; >.box, .list < margin: 0 .4em; width: 320px; display: flex; flex-direction: column; justify-content: center; align-items: center; background: linear-gradient(lightyellow, darkorange); border-radius: 5px; padding: .6em; box-shadow: 0 0 4px rgba(0, 0, 0, .6) >img < padding: .4em; width: 100%; >h3 < user-select: none; >p < margin: .2em 0; font-size: 1.1em; >textarea < width: 300px; height: 80px; padding: .4em; border-radius: 5px; font-size: 1em; resize: none; margin-bottom: .7em; >input[type=»date»] < width: 150px; text-align: center; margin-bottom: 3em; >button < width: 140px; padding: .4em; margin: .4em 0; cursor: pointer; border: none; background: linear-gradient(lightgreen, darkgreen); border-radius: 5px; font-family: inherit; font-size: .8em; text-transform: uppercase; box-shadow: 0 2px 2px rgba(0, 0, 0, .5); >button:active < box-shadow: 0 1px 1px rgba(0, 0, 0, .7); >button:focus, textarea:focus, input:focus < outline: none; >.note < display: flex; flex-wrap: wrap; justify-content: center; align-items: center; font-style: italic; user-select: none; word-break: break-all; position: relative; >.note p < width: 240px; font-size: 1em; >.note span < display: block; cursor: pointer; font-weight: bold; font-style: normal; >.info < color: blue; >.notify < color: #ddd; font-size: .9em; font-weight: normal !important; text-align: center; line-height: 25px; border-radius: 5px; width: 130px; height: 25px; position: absolute; top: -10px; left: -65px; background: rgba(0, 0, 0, .6); transition: .2s; opacity: 0; >.show < opacity: 1; >.info.null, .notify.null < display: none; >.complete < padding: 0 .4em; color: green; >.delete < padding-left: .4em; color: red; >.line-through
Пока не обращайте на них много внимания.
Переходим к скрипту.
Находим поля для ввода и создаем контейнер для заметок:
let textarea = document.querySelector(‘textarea’) let dateInput = document.querySelector(‘input[type=»date»]’) let list = document.createElement(‘div’) list.classList.add(‘list’) document.body.appendChild(list)
Создаем базу данных и хранилище:
let db; // IIFE (async () => < // создаем базу данных // название, версия. db = await idb.openDb(‘db’, 1, db =>< // создаем хранилище db.createObjectStore(‘notes’, < keyPath: ‘id’ >) >) // формируем список createList() >)();
Рассмотрим функцию добавления заметки, чтобы понимать, что из себя представляет или, точнее, что содержит одна заметка. Это поможет понять, как формируется список:
// добавляем к кнопке для добавления заметки обработчик события «клик» document.querySelector(‘.add-btn’).onclick = addNote const addNote = async () => < // если поле для ввода текста пустое, ничего не делаем if (textarea.value === ») return // получаем значение этого поля let text = textarea.value // объявляем переменную для даты напоминания // с помощью тернарного оператора // присваиваем этой переменной null или значение соответствующего поля let date dateInput.value === » ? date = null : date = dateInput.value // заметка представляет собой объект let note = < id: id, text: text, // дата создания createdDate: new Date().toLocaleDateString(), // индикатор выполнения completed: », // дата напоминания notifyDate: date >// пробуем записать данные в хранилище try < await db.transaction(‘notes’, ‘readwrite’) .objectStore(‘notes’) .add(note) // формируем список await createList() // обнуляем значения полей .then(() =>< textarea.value = » dateInput.value = » >) > catch < >>
Теперь займемся формированием списка:
let id const createList = async () => < // добавляем заголовок // дату формируем с помощью API интернационализации list.innerHTML = `
Today is $).format()>
` // получаем заметки из базы данных let notes = await db.transaction(‘notes’) .objectStore(‘notes’) .getAll() // массив для дат напоминаний let dates = [] // если в базе имеются данные if (notes.length) < // присваиваем переменной «id» номер последней заметки // итерация по массиву notes.map(note =>< // добавляем заметки в список list.insertAdjacentHTML(‘beforeend’, // добавляем заметке атрибут «data-id» ` // дата уведомления $ // значок (кнопка) отображения уведомления // обратите внимание, что в качестве дополнительного класса // мы добавляем тексту и значку уведомления дату напоминания // если дата не указана // текст и значок уведомления не отображаются (CSS: .info.null, .notify.null) ? // значок (кнопка) выполнения задачи V // в качестве класса к тексту заметки добавляется индикатор выполнения
Text: $,
created: $ // значок (кнопка) удаления заметки X `) // заполняем массив с датами напоминаний // если дата не указана if (note.notifyDate === null) < return // если дата указана >else < // массив объектов dates.push(< id: note.id, date: note.notifyDate.replace(/(d+)-(d+)-(d+)/, ‘$3.$2.$1’) >) > >) // если в базе не имеется данных > else < // присваиваем переменной «id» значение 0 // выводим в список текст об отсутствии заметок list.insertAdjacentHTML(‘beforeend’, ‘
empty
‘) > // . to be continued
Массив объектов для хранения дат напоминаний имеет два поля: «id» для идентификации заметки и «date» для сравнения дат. Записывая значение даты напоминания в поле «date», мы вынуждены это значение преобразовывать, поскольку inputDate.value возвращает данные в формате «гггг-мм-дд», а мы собираемся сравнивать эти данные с данными в привычном нам формате, т.е. «дд.мм.гггг». Поэтому мы используем метод «replace» и регулярное выражение, где с помощью группировки инвертируем блоки и заменяем дефисы точками. Возможно, существует более универсальное или элегантное решение.
Далее работаем с заметками:
// . // находим все заметки и добавляем к каждой обработчик события «клик» // мы делаем это внутри функции формирования списка // поскольку наш список при добавлении/удалении заметки формируется заново document.querySelectorAll(‘.note’).forEach(note => note.addEventListener(‘click’, event => < // если целью клика является элемент с классом «complete» (кнопка выполнения задачи) if (event.target.classList.contains(‘complete’)) < // добавляем/удаляем у следующего элемента (текст заметки) класс «line-through», отвечающий за зачеркивание текста event.target.nextElementSibling.classList.toggle(‘line-through’) // меняем значение индикатора выполнения заметки // в зависимости от наличия класса «complete» note.querySelector(‘p’).classList.contains(‘line-through’) ? notes[note.dataset.id].completed = ‘line-through’ : notes[note.dataset.id].completed = » // перезаписываем заметку в хранилище db.transaction(‘notes’, ‘readwrite’) .objectStore(‘notes’) .put(notes[note.dataset.id]) // если целью клика является элемент с классом «delete» (кнопка удаления заметки) >else if (event.target.classList.contains(‘delete’)) < // вызываем соответствующую функцию со значением идентификатора заметки в качестве параметра // обратите внимание, что нам необходимо преобразовать id в число deleteNote(+note.dataset.id) // если целью клика является элемент с классом «info» (кнопка отображения даты напоминания) >else if (event.target.classList.contains(‘info’)) < // добавляем/удаляем у предыдущего элемента (дата напоминания) класс «show», отвечающий за отображение event.target.previousElementSibling.classList.toggle(‘show’) >>)) // запускаем проверку напоминаний checkDeadline(dates) >
Функция удаления заметки из списка и хранилища выглядит так:
const deleteNote = async key => < // открываем транзакцию и удаляем заметку по ключу (идентификатор) await db.transaction(‘notes’, ‘readwrite’) .objectStore(‘notes’) .delete(key) await createList() >
В нашем приложении отсутствует возможность удаления базы данных, но соответствующая функция могла бы выглядеть следующим образом:
document.querySelector(‘.delete-btn’).onclick = async () => < // удаляем базу данных await idb.deleteDb(‘dataBase’) // перезагружаем страницу .then(location.reload()) >
Функция проверки напоминаний сравнивает текущую дату и даты напоминаний, введенные пользователем:
const checkDeadline = async dates => < // получаем текущую дату в формате «дд.мм.гггг» let today = `$` // итерация по массиву dates.forEach(date => < // если текущая дата и одна из дат напоминаний совпадают if (date.date === today) < // меняем кнопку отображения напоминания с «?» на «!» document.querySelector(`div[data-id=»$»] .info`).textContent = ‘!’ > >) >
В завершение добавляем к объекту Window обработчик ошибок, которые не были обработаны в соответствующих блоках кода:
window.addEventListener(‘unhandledrejection’, event => < console.error(‘error: ‘ + event.reason.message) >)
Результат выглядит так:
Источник: habr.com
Записная книжка
В этом уроке мы рассмотрим создание бота, который записывает данные в Google Таблицу и читает ранее записанные заметки.
В этом уроке мы покажем вам отрезок сценария, который поможет:* поочередно выводить данные из строк таблицы;* выводить значения определенной строки и определенного столбца;* сообщать, если данные из таблицы прочитаны и больше данных нет.
Полностью сценарий выглядит так:


Шаг 1. Экран приветствия
Добавьте приветствие и предложение читать или записать заметки:
Привет! Я могу прочитать твои заметки или сделать запись.
Также добавьте две кнопки «читать» и «писать».

Шаг 2. Добавьте реплику
Добавьте рядом еще один текстовый блок с текстом:
Добавьте к этому экрану блок фраз с примером:

Шаг 3. Создайте HTTP-запрос
От $TEXT протяните связь и создайте HTTP-запрос.
Перейдите к этой инструкции для создания апплета IFTTT для записи в таблицу.
Оформите запрос следующим образом:
- поле URL оформите так, как описано в инструкции по ссылке.
- метод запроса POST
- на вкладку BODY скопируйте следующее:
- на вкладке HEADERS:

Шаг 4. Добавьте реакцию на успешное завершение
От варианта Завершен успешно Http-запроса протяните связь и создайте текстовый блок, на котором разместите сообщение об успешной записи: Записал. Будем читать или записать еще одну?
Также, на этот экран добавьте кнопки Читать и Записать еще.

Шаг 5. Протестируйте запись
Соедините кнопку Писать со стартового экрана с экраном Что записать.

Нажмите Тестировать. Попробуйте сделать запись в вашу таблицу с помощью кнопки Писать.

Перейдите в таблицу и проверьте, появилась ли запись.

Если запись не появилась, то обратитесь к этой статье, чтобы проверить, какие могут быть неполадки.
Шаг 6. Оформите таблицу
На этом этапе в вашей Google Таблице уже должны быть записи, которые вы сделали с помощью бота в процессе тестирования записи. В первой строчке таблицы поместите названия столбцов. Например, назовем столбцы так: date — дата, которую IFTTT прописывает автоматически event — название вашего события IFTTT (вы можете в одну таблицу делать запись с помощью разных событий, и названия событий могут говорить вам о содержании или значении заметок) note – сам текст заметки.

Шаг 7. Извлеките данные из Google Таблицы
Создайте еще один блок HTTP-запрос.

Обратитесь к этой статье для оформления HTTP-запроса для чтения данных из Google Таблиц. Не забудьте опубликовать таблицу, как описано в статье по ссылке.
- метод GET
- на вкладке RESPONSE назовите переменную items и запишите значение $httpResponse
- Вкладки BODY и HEADERS заполнять не нужно.

От варианта Завершен успешно протяните связь и создайте блок условий с текстом:

От $items.first() сделайте связь и создайте блок Текст с текстом:
заметка $items.current().note была сделана в следующую дату: $items.current().date
Таким образом, мы будем доставать из переменной item элементы note и date .

Создайте на этом экране блок Переход.

Протяните от него связь и создайте новый блок условий с текстом: $items.next()

От $items.next() сделайте связь с новым текстовым блоком: Читать дальше? и добавьте на него кнопки Читать дальше и Записать.

Соедините кнопку Читать дальше с блоком, на котором есть следующий текст:
заметка `$items.current().note` была сделана в следующую дату: `$items.current().date`.
Шаг 8. Создайте глобальный блок фраз
Создайте новый экран, поместите на него блок Интенты.
Обязательно поставьте галочку «Глобальный блок».
Добавьте следующие шаблоны фраз:
- * *чит* * – свяжите этот вариант с блоком чтения из Google Таблиц
- * *пис* * – свяжите этот вариант с блоком, на котором есть текст Что записать?
- Можете также добавить другие подобные шаблоны.


Таким образом, вы можете из любого места сценария попросить бота записать или прочитать заметки, и он среагирует соответствующе на разные формулировки. После добавления глобального блока фраз вам больше не нужно связывать кнопки, на которых есть текст, попадающий в шаблон.
Шаг 9. Завершите сценарий
Добавьте в конец сценария последний текстовый блок:
Больше заметок нет! Записать еще одну или читать с начала?
Добавьте на этот экран кнопки Писать и Читать.
На обоих блоках условий у нас есть вариант else . Соедините оба этих варианта с этим финальным экраном.

Все! Сценарий готов. Теперь можете дополнить его добавлением других экранов-реакций.
Источник: help.aimylogic.com


