Приветствую, Дорогие друзья! Сегодня мы с Вами, как я и обещал, будем добавлять в нашу программу, которую мы пишем на WinApi C++, строку меню. Помимо этого, мы рассмотрим некоторые способы записи информации из рабочей области программы в файл. И, возможно даже, способы чтения информации из файла. Напоминаю, наша программа сейчас выглядит вот так:
Перед тем, как приступить, непосредственно к работе, давайте порассуждаем и определимся с категориями меню, подкатегориями и их названиями. Итак, традиционная категория «Файл», мне кажется, в данном случае, малоприменима, по той причине, что наша программка вполне возможно будет подгружать информацию не из одного, а из нескольких файлов сразу и, скорее всего у этих файлов будут разные расширения. Наиболее приемлемым будет название для первого раздела меню, такое, как «Карточка». Да-да, прямо как в поликлинике: карточка пациента. Только в данном конкретном случае, пациент – это не человек, а его автомобиль. Под карточкой мы будем понимать совокупность всех данных на ремонтируемое авто: электрические схемы, схемы электропроводки, прошивки, артикулированные схемы запчастей, перечни проделанных ранее манипуляций с авто (ремонты, перепрошивки, ТО) и т.д. Так как у нас уже есть поля для идентификации, сохранения и открытия (ну над открытием мы поработаем в этой статье), мы не будем «перегружать» пункт меню «Карточка» такими подпунктами, как «Открыть», «Сохранить» и «Новая карточка». Что тогда мы можем сюда поместить? Предположим, что у нас есть база карточек на авто из другого автосервиса и нам нужно добавить ее к своей базе. Значит нам нужены подпункты «Прикрепить карточку» и «Прикрепить несколько». При обслуживании авто в сервисе, между сервисом и клиентом будет заключаться договор. Добавляем соответствующий подпункт в меню «Карточка». Ну и, как это ни прискорбно, автомобили стареют. Если обслуживаемый ранее авто будет утилизирован, зачем нам информация о нем? Верно, не за чем! Добавляем подпункт меню «Удалить». Данный подпункт будет нужен также в том случае, если оператор при заполнении карточки допустит критичные ошибки. Ну и куда же без подпункта «Выход». Добавим и его.
С первым пунктом меню разобрались. Переходим ко второму. Пусть вторым пунктом меню будут «Настройки». Что мы будем настраивать? Права доступа к программе! Нам совсем не нужно, чтобы кто-то «левый» мог узнать личные данные… автомобиля. Первый подпункт меню, который мы поместим сюда, будет называться «Пользователь». Далее, что нам еще будет нужно настроить? Правильно: директории, где будут храниться файлы программы, карточки, схемы, прошивки и т.д. По умолчанию мы будем скидывать их туда, где будет храниться само приложение. Но вдруг для кого-то это будет неудобно? Добавляем подпункт «Изменить локацию файлов». Далее. Предположим, что с программой будут работать три человека: механик, автоэлектрик и ресэпшионист. Первый будет проводить осмотр-дефектовку-ремонт узлов авто, второй – диагностику или ремонт электронных блоков или проводки, а третий – всего-навсего составлять договор с клиентом. Первым двум программа нужна в ее полном формате, а вот третьему – в урезанном, где только будут отображены названия операций, проделанных с автомобилем. И напротив, механику совсем необязательно знать стоимость ремонта или стоимость запасных частей, потому как поиск их в автомагазинах и последующее приобретение – не его проблема. Его задачи: дефектовать деталь, определить по базе артикул заменяемого узла, отправить заявку секретарю на приобретение его в магазине (ну и согласование с клиентом само-собой), и установить запасную часть вместо поврежденной, когда ему ее выдадут. Добавляем в меню подпункт «Формат программы». Здесь мы также сделаем подпункты. Первый – «Упрощенный», второй – «Развернутый», третий – «Смешанный». Третий подпункт выберет тот, кто работает сам на себя, то есть тот, кто и механик, и электрик, и секретарь, и директор сразу. С пунктом «Настройки» более-менее разобрались. Остался еще один очень важный пункт меню. «Справка». Здесь мы должны поместить сведения «О программе», «Лицензионное соглашение» и, конечно же «Разработчик».
Открываем наш программный код. Пролистываем файл до конца, отступаем пару строчек и создаем новую функцию. Назовем ее просто и понятно: Menu:
В этой функции мы поместим код, с помощью которого будет создаваться наше меню, а вызывать эту функцию мы будем из WM_CREATE. Для того, чтобы создать пункт меню используем функцию AppendMenu(). Найдем ее в справочнике:
Здесь, как видим, всего четыре параметра:
1. hMenu– то есть дескриптор нашего пункта меню;
2. uFlags – флаг. В справочнике таблица флагов немного большая, поэтому тут помещу ее упрощенную версию:
3. uIDNewItem– идентификатор пункта меню, с помощью которого он будет взаимодействовать с программой;
4. lpNewItem– текст, отображаемый в пункте меню.
Для того, чтобы добавить в программу новый пункт меню нам нужно:
- Создать новую переменную типа HMENU;
- Создать идентификатор пункта меню.
Первое условие выполнить достаточно просто: придумываем имя для hMenu и используем функцию CreateMenu(). В нее не нужно передавать никаких параметров. Она просто создает пустой пункт меню. Пусть наш hMenu называется MainMenu:
Теперь создадим идентификатор для первого пункта меню. Ага, попались?! Для чего нам нужен идентификатор? Правильно: для того, чтобы что-то случилось, когда мы нажмем на пункт меню. А что должно случиться? Должны появиться подпункты. То есть меню должно раскрыться. Для этого в качестве идентификатора в uIDNewItem нужно передать дескриптор подпунктов меню, то есть их hMenu. Создадим этот дескриптор. Назовем его SubMenu. При вызове функции AppendMenu() не забудем привести его к нужному типу данных (в скобочках укажем его, смотрите на картинку):
Теперь самое важное! Чтобы наше меню отображалось мы должны сказать программе где его отобразить. Для этого применим функцию SetMenu(). Найдем ее в справочнике:
Как видим, ничего сложного: первый параметр – hwnd (это дескриптор нашего главного окна, передадим его в нашу функцию через параметр hwndParrent), а второй параметр – MainMenu. Добавляем SetMenu() в нашу функцию:
Объявляем нашу функцию Menu() в файле resources.h и вызываем из WM_CREATE:
Компилируем, запускаем? Вот что мы получим:
При нажатии на пункт меню Карточка ничего не происходит. Исправим это! Переходим в нашу функцию Menu() и добавляем несколько подпунктов в меню. Важно! Функция SetMenu() должна по-прежнему располагаться в конце функции Menu(), иначе у нас что-то может не отобразиться! В файл resources.h добавляем новую константу. Это будет идентификатор первого подпункта меню. Назовем его просто: IDMenu1 и присвоим значение 200. Так и пишем: «#define IDMenu1 200». Указываем этот идентификатор в качестве параметра uIDNewItem функции AppendMenu():
Обратите внимание: в качестве второго параметра мы указали флаг MF_STRING, потому как данный пункт меню – обычная текстовая строка. Компилируем и смотрим, что получится:
Другое дело! Теперь при нажатии на «Карточка» появляется выпадающий список. Состоит он, правда, пока что из одного пункта… Исправляем это!
Хммм… Чего-то всё равно не хватает… Давайте, добавим разделительную линию между пунктами «Удалить» и «Выход». Чтобы это сделать также можно применить функцию AppendMenu(). Первый ее параметр мы знаем – это родительское окно, второй параметр – флаг, в нашем случае MF_SEPARATOR, третий и четвертый параметр – NULL, так как линии не нужны ни идентификатор, ни текстовое обозначение:
Компилируем и смотрим, что получилось (не забываем добавить в файл resources.h новые идентификаторы: IDMenu2, IDMenu3, IDMenu4 и IDMenu5):
Отлично! Как и задумывалось. Теперь, по аналогии с пунктом меню «Карточка» создадим остальные и добавим в них требуемые подпункты.
В файл resources.h тем временем нужно добавить идентификаторы для новых пунктов и подпунктов (и для подпунктов подпунктов!):
Компилируем программу:
Заполнять обработчик команд для меню мы будем постепенно. Для того, чтобы показать Вам пример, напишем скрипт, создающий информационное окошко при выборе пункта меню Справка ->Лицензионное соглашение. Это будет обычное диалоговое окно с текстом и кнопочкой ОК внизу (ну может еще иконку с восклицательным знаком добавим).
Переходим в подраздел WM_COMMAND обработчика.
Обратите внимание: в качестве второго параметра функции MessageBox() мы указали несколько строк текста. Между собой они никак не соединены (никаких знаков конкатенации, то есть объединения). Проверим, будет ли это работать:
При нажатии на пункт меню Справка -> Лицензионное соглашение открывается наша заготовка лицензии. Также на данном этапе написания программы мы можем обработать еще один пункт меню, а именно: Карточка -> Выход. Сделаем case аналогичный WM_DESTROY:
Компилируем, проверяем.
При выборе пункта меню Выход окно закрывается. Всё работает. Внимание! Важно понимать, что дескриптор окна, в котором мы создаем меню нужно передавать в функцию именно в WM_CREATE. Из функции вызывать его бесполезно. Если Вы так сделаете, то не удивляйтесь, что меню в окне не отобразится!
Теперь, как и обещал, перейдем к сохранению информации в файл. В прошлой статье мы уже рассмотрели основные принципы записи данных. Сейчас же просто немного (ага, совсем немного!) расширим код, реализующий эту операцию. А именно: информация будет сохраняться в отдельной папке, которая также будет содержать директории с файлами. Вот она, функция, которая занимается записью текста в файл:
Не обращайте внимание на смесь немецкого и английского языков в именах функций, которые я придумываю: у каждого свой почерк в программировании, и это, уж извините, мой. Мы уже рассматривали какая строчка в этой функции за что отвечает, поэтому повторяться «в полном объеме» не буду. Сначала мы считываем данные из текстового окошка в буфер данных Buf[32]. Дескриптор этого окошка мы передаем в функцию в качестве параметра HWND hWnd. Затем мы создаем файл: указываем путь к нему (в кавычках – первый параметр функции CreateFile, строка 362), указываем его атрибуты, то есть основные свойства. После чего записываем данные из буфера в этот файл. Предположим, что структура у нашей базы данных клиентов будет следующей: общая папка, в ней лежат папки, в которых хранится информация о каждом автомобиле, а в тех папках также имеются папки со схемами, прошивками, договорами и прочим. То есть наш файл будет сохраняться не в «file.txt», а, скажем, в «Databank\VIN\file.txt». Как это сделать? Давайте для начала просто впишем эту строчку вместо «file.txt» в строке 362 и посмотрим, что у нас получится:
Компилируем, запускаем, жмем кнопку сохранить и видим следующую картину в папке проекта:
Но ведь мы все верно сделали: указали путь от директории, в которой лежит наше приложение! А вот и не верно. Для того, чтобы наш файл был сохранен правильно, нам нужно указать полный путь, от литеры диска, до имени файла и его расширения. У нас есть два варианта как это сделать. Первый: прописываем путь ручками. То есть смотрим где лежит наше приложение и создаем рядышком с ним папки с вложенными директориями, а затем вписываем всё это в CreateFile(). Сами понимаете, звучит абсурдно. И второй, наиболее приемлемый: мы спросим у системы, где лежит наше приложение, запишем путь к нему в какой-нибудь небольшой буфер, «приклеим» к нему немного текста, а именно нашу строчку «Databank\VIN\file.txt», и используем этот адрес при сохранении.
Местонахождение приложения нам поможет узнать функция GetCurrentDirectory(). Ищем ее в справочнике:
Простыми словами, эта функция показывает откуда запустилась программа. Первый ее параметр – длина буфера, в который мы записываем путь к программе, второй параметр – сам буфер. Давайте проверим как работает эта функция. В файле resources.h создадим массив char-ов. Пусть в нем будет 124 символа, думаю, столько нам вполне хватит для того, чтобы вписать путь к директории приложения. Назовем этот массив ApplicDir.
Путь к нашему приложению, на самом деле, будет нужен нам не только при сохранении файлов, поэтому создадим отдельную функцию, которая при запуске приложения будет записывать текущую директорию программы в ApplicDir[]. Назовем эту функцию чем-то вроде «Где приложение?», то есть WhereIsApp():
Именно отсюда мы будем вызывать функцию GetCurrentDirectory(). Для того, чтобы указать размер буфера в качестве первого параметра этой функции, используем оператор sizeof(), который показывает размер массива в байтах:
Объявляем функцию WhereIsApp() в файле resources.h и вызываем ее из WM_CREATE.
Проверим, считывается ли директория программы в массив. Для этого вызовем диалоговое окошко внутри нашей функции. Пусть оно показывает что лежит у нас в массиве:
Компилируем, проверяем:
Как видите, не успело еще отрисоваться главное окно приложения, а функция уже отработала и выдала нам адрес папки, в которой лежит программа (всё верно, ведь мы вызвали функцию перед всеми остальными). Это значит, что функция WhereIsApp() работает как нам надо. Многие скажут, «Зачем выводить сообщение? Нужно в отладчике смотреть!». Как по мне, такой способ более наглядный, а по времени занимает куда меньше времени, чем работа в отладчике, если, конечно, печатать быстро умеете (и вообще: это мой обучающий курс, так что, что хочу, то и ворочу!). Затираем строчку с вызовом сообщения и радуемся, что всё работает.
Возвращаемся в функцию TextInFile(). Здесь нам нужно сделать так, чтобы путь к файлу состоял из того, что хранится в массиве ApplicDir[] и «Databank\VIN\file.txt». Используем для этого функции strcpy() и strcat(). Первая копирует данные из одной строки в другую, вторая – объединяет две строки. Создадим массив char-ов внутри функции. В него мы поместим полный адрес файла. Назовем мы его AppAddres и выделим под него 256 байт.
Вызовем диалоговое окно с сообщением и посмотрим, что мы «наколдовали»:
Компилируем, смотрим, что получилось:
Отлично! Часть дела сделана. Почему часть? Потому что мы создаем файл в несуществующей папке. Для того, чтобы создать папку используем функцию CreateDirectory(). Найдем ее в справочнике:
Первый параметр здесь – путь к папке, то есть место, где мы ее создаем. Второй параметр – (скажу простыми словами) дескриптор безопасности, если указать тут NULL, что мы и сделаем, все права доступа к создаваемой папке останутся такими же, как и у той директории, в которой она будет лежать (в нашем случае, как у C_Project).
Для удобства и экономии места сделаем следующее: разделим путь к создаваемому файлу на куски, а затем последовательно наращивая эти куски начнем создавать директории:
Компилируем, запускаем приложение, жмем кнопку сохранить (именно к ней в обработчике привязана функция TextInFile) и смотрим, что у нас лежит в папке проекта:
Вот она: наша заветная папочка Databank! И, как видим, она не пустая. Перейдем в нее:
Перейдем в папку VIN?
И здесь лежит тот самый файл file.txt, внутри которого хранится считанный из поля editтекст (фамилия клиента). Идем дальше. Одной фамилии для нормальной работы с приложением, нам, разумеется, будет мало. Поступим следующим образом: разобьем наш файл на строки. Пусть в первой строке у нас будет лежать фамилия клиента, во второй строке имя, в третьей – отчество, в четвертой номер телефона, в пятой – электронная почта. На данный момент этого будет достаточно для понимания принципов чтения-записи. Итак: записываем эти данные в файл! Ну не все, конечно, а только те, что у нас имеются на данный момент. Чтобы разбить строку… на строки, как бы это ни звучало странно, мы будем использовать специальный операнд «\n». То есть, вот такая запись в файле:
«Фамилия
Имя
Отчество»
будет выглядеть вот так: «Фамилия\nИмя\nОтчество». Любые пробелы, которые мы отобразим тут, отобразятся в файле, и, как следствие, если они будут лишними, то начнут нам мешать при считывании. Операнд «\n», конечно же известен людям, которые уже писали что-то на Си (даже консольки считаются). Так зачем же я тогда затрагиваю эту тему? Написал бы просто строку в программе и не вдавался в подробности. А затем, что вдаться в подробности тут нужно! Не любому компилятору понравится запись, подобная «Фамилия\nИмя\nОтчество». Нет, ругаться он на нее не будет, съест и даже слова не скажет. Только вот в файле мы получим не
«Фамилия
Имя
Отчество»
а «ФамилияИмяОтчество». Чтобы этого не произошло, нужно не только переносить курсор на новую строку, но и устанавливать его в ее начало. Для этого применяется оператор «\r». То есть в программе наша строка должна выглядеть вот так: «Фамилия\r\nИмя\r\nОтчество». Кто-то может сказать Вам, что это лишнее. Не верьте ему! При написании приложений на WinApi подобная конструкция лишней быть не может, даже наоборот: при записи в файл, она скорее необходима! Если мы выводим текст в какое-нибудь диалоговое окно и делаем в нем переход на новую строку, вот там да, без «\r» обойтись можно (и то не всегда, поэтому мой Вам совет: всегда используйте эти операторы вместе!). Ну что, модернизируем наш код, чтобы он мог считывать не только фамилию, но и имя с отчеством? Создадим в функции записи в файл массив char-ов. Назовем его, скажем, MegaBuf и выделим ему место в памяти… пусть будет 256 байт (для ФИО вполне хватит и еще даже останется). До «Мега» он конечно не дотягивает, но название это чисто символическое. В него, посредством функций strcpy() и strcat() мы будем помещать текст, считанный из соответствующих текстовых полей программы.
Пока что у нас следующий алгоритм записи: мы считываем текст из окошка в массив Buf (строка 369) и записываем его в файл (строка 371). Нам же нужно поступить следующим образом: считать текст из первого поля (Фамилия) в массив Buf, записать его из массива Bufв массив MegaBuf, добавить к нему операторы переноса на новую строку, считать текст из второго текстового поля (Имя) в массив Buf, записать его в массив MegzBufпосле уже присутствующего там текста, вновь добавить операторы переноса «\r\n», считать текст из третьего поля (Отчество) в массив Bufи снова записать его в MegaBufпосле предыдущего. Почему мы просто не можем копировать текст из одного массива в другой или же напрямую считывать его из текстовых полей в MegaBuf? Потому что и в первом, и во втором случае, текст, помещенный ранее в MegaBuf будет затираться при записи нового. Именно поэтому без второго массива никак не обойтись! Что ж, приступим. Из первого поля наша программа текст считывать уже умеет. Остается только перенести его в MegaBufи добавить в конце «\r\n». Ничего сложного:
Теперь нам нужно применить функцию GetWindowText(), чтобы считать имя клиента из соответствующего окошка. Ага! А в функцию мы передаем только один параметр HWND. Как же быть? Просто: добавим еще два параметра:
Обратите внимание на заголовок функции: там мы добавили еще два параметра, а первый переименовали. Также посмотрите на строку 369. Здесь мы также внесли небольшие изменения. Данный код считывает фамилию и имя из двух текстовых окошек. Сделаем так, чтобы он считывал еще и отчество:
Посмотрите на строку 378. При текущей записи, в файл у нас попадет только отчество. Думаю, Вы и сами поняли, что нужно сделать, чтобы в файл попал весь текст, который мы хотим там видеть. Вот это:
Теперь поднимаемся в обработчик команд, находим идентификатор кнопки «Сохранить» и вносим небольшие изменения в код:
Перед тем как компилировать программу не забываем заглянуть в файл resources.h и отредактировать описание функции TextInFile(). Вместо «void TextInFile(HWND hWnd);» нужно указать «void TextInFile(HWND hWnd_1, HWND hWnd_2, HWND hWnd_3);».Компилируем, запускаем, жмем сохранить и смотрим, что в конечном файле file.txt у нас получилось:
Отлично! Всё как мы и задумывали! Теперь займемся папочкой VIN.Ведь это не настоящее ее название, а временное! Название у нее должно становиться таким, какое мы введем в соответствующее текстовое поле программы. Ну тут уж ничего сложного. Считывать данные из окошка editмы уже умеем, как и сращивать их с текстом. Считываем:
Внимание на строки 357, 363, 367 и 368. Мы считываем текст из окошка, записываем его в буфер Buf, затем прописываем этот текст в путь к создаваемому файлу заменяя им ранее стоявший там текст «VIN». Снова правим код в обработчике команд и описание функции в файле resources.h:
Компилируем, запускаем приложение. В графе «VIN» введем самый простой код, который можно придумать: ABCDEFG0123456789. Жмем кнопку «Сохранить» и смотрим, что у нас получилось:
В папке Databank, помимо находившейся в ней ранее папки VIN, появилась еще одна папка с именем «ABCDEFG0123456789».
В ней же лежит файл file.txt, а в нем – считанный из окошек текст.
Не помню, говорил ли я уже или еще нет, программист – это человек, который просчитывает все возможные проблемы наперед. Если программа работает криво – это не вина «железа», это кривые руки и спешка! К чему это я? Закроем наше приложение и запустим его заново, чтобы оно открылось по умолчанию. Вот так:
Нажмем кнопку «Сохранить» и посмотрим, что получится.
Как видим, программа не создает папку с именем «*****************» и вполне понятно почему: звездочки в пути к файлу разместятся между косыми чертами, а следом встанет расширение «*.txt». С одной стороны это хорошо: мы не создадим файл с «мусорным названием» в директории данных. С другой стороны – плохо. Вдруг мы не случайно нажали на «Сохранить», а намеренно, просто забыли ввести ВИН-код? Как быть? Добавим в программу простое условие, которое будет сравнивать введенный ВИН со звездочками и в случае совпадения (полного или частичного) выдаст нам ошибку, а точнее выведет на экран диалоговое окно с сообщением «Внимание! Введен некорректный ВИН-код!». Переходим в функцию TextInFile() и после считывания ВИН-кода добавляем проверку на правильность:
В строчке 364 мы создали булеву переменную VIN_Z и присваиваем ей значение «ЛОЖЬ». Затем в цикле forперебираем посимвольно считанный из поля VIN в буфер Buf текст и ищем среди символов звездочки. Если мы звездочек не находим (строка 367), то оставляем VIN_Z равной «ЛОЖЬ» и продолжаем выполнение цикла. Если звездочка находится(строка 368), то выполнение цикла прерывается и переменной VIN_Z присваивается значение «ПРАВДА». Затем, в зависимости от значения VIN_Z мы либо записываем данные из программы в файл, либо выводим сообщение об ошибке.
Компилируем, запускаем, жмем «Сохранить»:
Отлично! Сообщение об ошибке выскочило, как и должно было! При вводе же корректного ВИН-кода все сохраняется:
Считывать информацию из файла мы будем в следующей статье цикла, так как там всё будет не так просто и быстро, как может показаться на первый взгляд. Какой у нас будет план: во-первых, мы внесем существенные коррективы в работу кнопки «Сохранить». Пока поле ВИН-кода не будет заполнено, кнопка будет деактивирована. Во-вторых мы сделаем так, чтобы, если введенный пользователем ВИН-код обнаружится в базе программы, кнопка изменит надпись на «Открыть» или «Загрузить» (при нажатии считается информация из файла, а затем она будет записана в соответствующие графы программы), ну и, наконец, в-третьих, мы зашифруем информацию в файле и придумаем для него собственное расширение и, возможно даже, иконку.
А пока что заглянем вот в этот пункт меню:
При выборе формата, рядом с соответствующим пунктом должна стоять галочка. По умолчанию формат – смешанный. Поставим галочку на «законное место». Для этого воспользуемся функцией CheckMenuItem().Найдем ее в справочнике:
Итак, первый параметр тут – идентификатор нашего меню (не цифровой, а именно HMENU), второй параметр – идентификатор (уже цифровой) пункта меню, третий параметр – флаг, состояние, в которое нужно установить пункт, то есть выбран или не выбран. Существует всего три флага: MF_BYCOMMAND (Используется по умолчанию, если не задано другого. Говорит о том, что второй параметр содержит идентификатор пункта меню), MF_BYPOSITION (Говорит о том, что второй параметр содержит позицию строки меню, отсчет ведется с нуля), MF_CHECKED (Выбрать элемент), MF_UNCHECKED (Деактивировать галочку у элемента). Что ж, пока что мы просто зададим выбранным по умолчанию пункт меню «Смешанный». Функцию CheckMenuItem() мы вызовем из функции Menu() сразу после функции SetMenu():
В обратном порядке располагать данные функции не стоит, так как сначала нам всё же лучше отобразить меню в окне, а уже потом и выбрать один из его пунктов.
Также можно вызвать функцию CheckMenuItem() из WM_CREATE, но тогда нам придется сделать переменную HMENU SSubMenu глобальной.
Компилируем, запускаем приложение и смотрим, что у нас получилось:
Давайте все же сделаем все переменные HMENU глобальными. Вынесем их из функции Menu() в файл resources.h:
В работе приложения при этом ничего не изменится, зато мы получим возможность изменять состояния пунктов меню из абсолютно любой точки программы. Зачем нам это? А вот хотя бы за этим: пусть при нажатии на пункт меню «Упрощенный» на нем активируется галочка, а на пункте «Смешанный» деактивируется. Через функцию Menu() мы такой операции сделать не сможем, зато вот так, с глобальными переменными HMENU – сколько угодно и когда угодно. Пролистываем код до обработчика команд и пишем условие для команды «IDMenu8», именно такой идентификатор мы присвоили пункту меню «Упрощенный»:
Для начала просто активируем галочку на пункте меню «Упрощенный» при нажатии на него. Компилируем, запускаем, заходим в «Настройки -> Формат программы» и выбираем тут «Упрощенный»:
Как видите, всё работает. Теперь уберем галочку с выбранного ранее пункта меню, то есть со «Смешанный». Так как мы не знаем, что выбирал ранее пользователь, то нам нужно будет погасить оба ненужных нам варианта: и «Смешанный» и «Расширенный». Делаем это: применяем функцию CheckMenuItem(), поочередно передавая в нее идентификаторы этих пунктов меню. При этом, в качестве третьего параметра используем MF_UNCHECKED:
Компилируем и проверяем что у нас получилось:
Как видим, при активации первого подпункта меню, последний деактивировался. Пропишем такие же условия и для остальных подпунктов, то есть для «Расширенный» и для «Смешанный»:
После компиляции получаем следующее:
На этом, пожалуй, данную статью закончим (пусть она и вышла не очень большой по моим привычным меркам, но в ней предоставлена важная информация). Надеюсь, она была интересной и хоть чем-нибудь, да помогла Вам! Если это так, не забудьте поставить «большой пальчик»: Вам не сложно, а я буду знать, что старался не зря. Спасибо, что читаете! Удачи в учебе и труде!
< К предыдущей статье цикла К следующей статье цикла >