Для начинающих знакомство с WinForms рано или поздно появляется ситуация, когда нужно пользователю показывать разные "страницы" приложения, в зависимости от его действий. Банальны пример: список и возможность редактирования отдельной записи. Самое первое что приходит на ум -- просто переключатся между окнами. Вот только такой подход имеет определенные проблемы:
главная проблема -- в Application.Run уже находится главная форма, которая определяет всю работу приложения. Её либо скрывать и хранить где-то в закромах для доступа, либо постоянно лезть в ApplicationContext и переназначать MainForm. Последний подход опасен тем что забыв переназначить, мы либо тупо грохним приложение послез закрытия текущей формы, либо оставим висеть программу в процессах.
визуально окна будут мигать, появляться в рандомных местах, разных размеров и прочее. Да, можно написать код, который будет получать размеры и позицию закрываемого окна и назначать новому, но это дополнительный простор для багов.
Чтобы упростить себе жизнь, можно не прыгать по формам, а просто оперировать UserControl. Вообще новички почему-то очень редко юзают этот инструмент, который очень и очень упрощает проектирование приложения. В частности с помощью него легко можно:
показывать кастомный список с любым дизайнерским оформлением, а не ограничиваться exel-подобным гридом
дробить сложный UI на отдельные части
переиспользовать в качестве partial
в целом строить логику на оперировании контролами (в том числе и навигации по приложению)
Для начала разберем самый примитивный пример: одна форма, два юзер-контрола и переключения между ними. Создаем два UserControl, на которых будет по Label (чтобы различать их) и кнопка перехода на другой контрол.
Далее, создаем класс, который будет управлять размещение контролов на главной форме. Он будет стоять из следующих частей:
метод регистрации главного окна
метод, позволяющий зачистить текущий контрол на форме (для простоты примера я выбрал грубый подход, когда тупо сносим все контролы)
методы, размещающие требуемые новые контролы.
для доступа из любой точки приложения -- singelton в виде статического свойства.
Билдим проект. Теперь у нас в toolbox появились наши UserControl. Открываем дизайн главной формы и размещаем на ней один из контролов. Запускаем и проверяем что все работает.
Если глянуть использование ОЗУ при очень частых переключениях, то можно заметить что память всё равно растет. Связано это с тем что GC не вызывается. И его принудительное пинание не особо меняет погоду. Причина в том что при наличии свободных 10+Гб clr не видит смысла лишний раз прелопачивать ОЗУ. Лично на моем ноуте я мог добить ОЗУ до целых 2ГБ и только тогда память начинала переиспользоваться.
Окей, в целом этого примера уже достаточно для освоения и разработки условного singel-form приложения. Правда прям в таком виде использовать будет не удобно по многим причинам:
нет "выравнивания" размера окна, если контрол имеет каки-либо ограничения
"закрытие" старого окна может сопровождаться определеными проверками (например мы в разделе редактирования и у нас есть не сохраненные данные). Это стоит учитывать.
класс навигации также знает про создание компонентов. По хорошему это нужно вынести в отдельную фабрику.
не плохо бы иметь возможность задавать заголовок окна относительно содержимого.
Окей, сделаем новый более сложный пример и дополним этими фичами.
полное ТЗ
Сделаем следующее приложение:
на старте показываем окно авторизации
далее мы показываем список неких Items, которые вычитываются из mock данных в ОЗУ
каждый Item имеет кнопку "редактировать", которая перекидывает на форму редактирования конкретной записи
также на главной форме со списком есть кнопка "добавить", которая позволяет открыть форму добавления (по сути таже форма, что и для редактирования, только без заполненных полей)
Для начала введем не обязательный интерфейс для контролов
C#
1
2
3
4
5
6
internalinterface IViewUserControl
{publicstring? Title {get;}publicbool Closing();publicbool Close();}
Title позволит указать в заголовке окна текст, Closing -- выполнить проверку перед переключением или закрытием. Насчет метода Close -- он 100% должен быть, но есть подозрение что всё таки в качестве void при отработке Closing.
Далее -- главное окно. Его создаем как отдельный класс, не через дизайнер.
Тут всё просто: можем задать текущий внутренний контрол (учитывая ситуацию, когда главное окно должно содержать какое-то количество других контролов. Например общего меню. По сути это аналог Layout). Также переопределяем закрытие: предварительно спрашиваем внутренний контрол на возможность закрыть.
Теперь займемся внутренней логикой приложения: объявим класс отвечающий за текущую авторизацию пользователя
Добавим сервис с mock данными. В нем предусмотрим возможность вычитки, обновления и удаления Items. Так же он будет содержать проверку логин-пароль. (По сути мы эмулируем БД)
Ещё один сервис, выполняющий "упаковку" изображения в разрешение 200х200. Этот сервис исключительно для визуала + далее показать в фабрике определенный момент по тому как можно такие не глобальные сервисы создавать только по необходимости.
Создадим контрол для отбражения отдельной записи. На контроле будет две функциональные кнопки: "редактировать" и "удалить". По их нажатию мы просто будем пинать ивенты, а обработку вынесем наружу. В самом конструкторе зададим установку отображения значений входящей записи (текст, изображение и прочее).
Основное представление. Тут просто список в виде FlowLayoutPanel, а также кнопка "добавить". Для наглядности добавил отображение текущего профиля и строку для фильтрации наших записей. LoadData - загрузка данных из "БД" и закидываем в виде юзер-контролов на панель. Так как контролы могут только информировать через ивенты об попытке редактирования/удаления, не забываем подвязать обработчики (в идеале эту логику стоит вынести в сами DataItemUserControl, но без подвязки данных очень трудно пропихнуть удаление на главное представление, а также их тоже придется запихнуть в фабрику. Мне лень было дополнительно тратить время на продумывание всего этого, потому оставил так). Также реализуем интерфейс IViewUserControl, чтобы можно было задать заголовок окну.
Форма (точнее контрол) редактирования. Также реализуем IViewUserControl, но в нашем случае главное метод Closing -- нужно выполнить проверку, что данные (не) были изменены, и соответственно вызвать уведомление. В остальном всё просто: распихиваем данные (при наличии) по нужным контролам, подвязываем обработку "сохранить" и "назад". Валидация и прочие проверки по вкусу.
Фабрика. Как можно заметить -- всё написано с подходом Dependency Injection. Можно и без него, но это более "взрослый" подход, о котором нужно читать отдельную лекцию (не в данном разборе). Просто смиритесь что "так надо". По всему приложение есть два основных сервиса, которые используются постоянно: UserContext и DataService (далее ещё добавится UINavigation). Зашьем прям в нашей фабрике эти инстанцы, причем ленивой инициализацией. И надобавляем методов инициализации наших UI компонентов.
Предположим что редактирование записей происходит не часто, поэтому в инициализации EditDataItemUserControl сервис ImageService будет создавать каждый раз по новой.
Далее добавляем в саму фабрику также инициализацию и для UINavigation (может немного хрустнуть в мозге от этой рекурсии в конструкторах, но изначальная у нас именно фабрика).
Распихиваем навигацию по конструкторам UI элементов и заменяем TODO: %name control% на нужные методы. Остается только подправить старт всего приложения, и готово.
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
На странице:
https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/
нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов.
. . .
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Статья исключительно для начинающих. Подходы оригинальностью не блещут.
В век Веб все очень привыкли к дизайну Single-Page-Application .
Быстренько разберем подход "на фреймах".
Мы делаем одну. . .
— Расскажи мне о Мире, бродяга,
Ты же видел моря и метели.
Как сменялись короны и стяги,
Как эпохи стрелою летели.
- Этот мир — это крылья и горы,
Снег и пламя, любовь и тревоги,
И бескрайние. . .
Модуль PowerShell 5. 1+ : Snippets. psm1
У меня модуль расположен в пользовательской папке модулей, по умолчанию: \Documents\WindowsPowerShell\Modules\Snippets\
А в самом низу файла-профиля. . .