Создание профессионально выглядящего дизайнера форм - PullRequest
27 голосов
/ 04 апреля 2011

Когда я начал программировать (около 10+ лет назад), меня поразили три вещи:

  • Компиляторы / интерпретаторы (тогда я знал их как «программы, которые заставляют мои программы работать», за которыми часто следует квалификатор «кем бы они ни были»)
  • Редакторы кодов
  • Дизайнеры форм

Тогда я принял их всех как факты жизни. Я мог создавать свои собственные специализированные программы, но «программы, которые заставляли мои программы работать», редакторы кода и редакторы форм были созданы богами, и я никак не мог связываться с ними.

Затем я пошел в университет и взял курс по обработке формального языка. После изучения формальных грамматик, парсеров, абстрактных синтаксических деревьев и т. Д .; вся магия о компиляторах, интерпретаторах и редакторах кода вскоре исчезла. Компиляторы и интерпретаторы могут быть написаны в здравом и простом виде, и единственной несвойственной вещью, которая может потребоваться редактору кода с подсветкой синтаксиса, были хаки Windows API.

Однако до сих пор редакторы форм остаются для меня загадкой. Либо мне не хватает технических знаний, необходимых для создания дизайнера форм, либо у меня есть такие знания, но я не могу найти способ использовать их для реализации дизайнера форм.

Используя Visual C ++ и MFC, я хотел бы реализовать дизайнер форм, вдохновленный лучшим из когда-либо создателей форм:

Visual Basic 6's form designer

В частности, я хотел бы имитировать две его особенности, которые мне нравятся больше всего:

  • Проектируемая форма находится внутри контейнера. Таким образом, можно создать произвольно большую форму, не тратя слишком много места на экране, просто изменив размер контейнера до соответствующего размера.

  • Опция «Выровнять по сетке» значительно упрощает разработку профессионально выглядящих пользовательских интерфейсов. расстраивает. На самом деле, я бы сказал, что создание профессионально выглядящих пользовательских интерфейсов с помощью дизайнера форм Visual Basic на самом деле просто, весело и приятно. Даже для таких программистов, как я.

Итак, у меня есть следующие вопросы:

  1. Как мне создать конструктор форм, в котором разрабатываемая форма находится внутри контейнера? Является ли проектируемая форма фактическим окном, содержащимся внутри другого окна? Или это просто макет "вручную", нарисованный дизайнером форм?

  2. Содержат ли Windows API и / или MFC функции, классы, что угодно, что позволяет легко создавать «выбираемые» элементы (окруженные маленькими белыми или синими прямоугольниками, когда они выделены, изменяемого размера, когда они «захвачены» "одним из этих" ребер ")?

  3. Как реализовать функцию «Выровнять по сетке»?

Ответы [ 3 ]

30 голосов
/ 04 апреля 2011

Оба ответа здесь хороши, но не учли того, что я считаю действительно интересными (включая пару, которую вы не спрашивали напрямую, но вы могли бы найти интерес в любом случае), так что вот мой 2c:

Рисование элементов управления

В идеале, вы просто идете вперед и создаете обычный экземпляр элемента управления. Вы хотите что-то похожее на кнопку? Создать настоящую кнопку. Хитрость заключается в том, что он перестает вести себя как кнопка: вы хотите, чтобы щелчки активировали его для перемещения, а не «нажимали» на него.

Один из способов справиться с этим - предполагая, что рассматриваемые элементы управления «основаны на HWND» (например, стандартный набор окон для кнопки, редактирования, статики, списка, древовидного отображения и т. Д.) - это создать элемент управления, а затем подкласс это - т.е. переопределите wndproc с помощью SetWindowLongPtr (GWLP_WNDPROC, ...), чтобы код конструктора мог перехватывать ввод с помощью мыши и клавиатуры и использовать его, например, для инициирования перемещения, вместо того, чтобы ввод мыши осуществлялся до действительного кода кнопки, который вместо этого будет интерпретировать его как событие «щелчка».

Альтернативный подход к созданию подклассов заключается в размещении невидимого окна над кнопкой для ввода ввода. Та же идея перехвата ввода, просто другая реализация.

Вышесказанное относится как к управляемым (VB.Net, C #), так и к неуправляемым (C / C ++) элементам управления; они оба по сути стоковые окна HWND; у управляемых версий просто есть код управляемой оболочки, передаваемый базовому неуправляемому элементу управления.

Старые элементы управления ActiveX (с предварительно заданным кодом), используемые в pre-.Net VB, представляли собой совершенно другую игру с мячом. Существует довольно сложная взаимосвязь между контейнером ActiveX и элементами управления ActiveX внутри него, причем многие COM-интерфейсы обрабатывают такие вещи, как согласование свойств, события, рисование и т. Д. (Существует набор интерфейсов, который позволяет элементу управления ActiveX получать входные данные и рисовать себя, не имея собственного HWND.) Однако одно преимущество, которое вы получаете от этой сложности, заключается в том, что элементы управления ActiveX имеют явный «режим разработки»; поэтому элемент управления знает, как правильно реагировать в этом случае, и может взаимодействовать со всей процедурой.

Сама форма ...

Так что в основном элементы управления - это обычные элементы управления. Таким образом, вы ожидаете, что сама форма будет обычной формой? - Почти. Насколько я знаю, это просто еще одно окно, основанное на HWND, это дочерний элемент дизайнера (поэтому оно обрезается и может прокручиваться внутри него); но я думаю, что дизайнер немного обманывает, потому что обычно Windows рисует только такие кадры, как - с заголовками кнопок и мин / макс, которые используются для реальных окон верхнего уровня. Я не знаю от руки точную технику, которую они используют здесь, но некоторые варианты могли бы включать: нарисовать это вручную, чтобы подражать взгляду Windows; использование «тематических» API-интерфейсов Windows, которые позволяют получить доступ к графическим элементам, используемым для фрагментов заголовков и рисовать их там, где вы хотите; или, возможно, менее вероятно, что окно будет настроено как «дочернее окно MDI» - это один из исключительных случаев, когда окна рисуют рамку вокруг вложенного окна.

Перетаскиваемые ручки

Простейший подход здесь для дизайнера - создать восемь маленьких квадратных всплывающих окон без строки заголовка, которые располагаются над всеми остальными элементами - которые инициируют соответствующий код изменения размера при нажатии. Когда пользователь щелкает от элемента управления к элементу управления, просто переместите окна маркера перетаскивания в текущий активный элемент управления. (Обратите внимание, что во всем вышеперечисленном Windows сама выясняет, на кого нажали, вам никогда не придется сравнивать координаты мыши с координатами прямоугольника элемента и самостоятельно их обрабатывать.)

Сохранение и воссоздание

Для системных элементов управления с обычными окнами, которые используются неуправляемым C / C ++, это относительно просто: существует хорошо известный текстовый формат файла - .rc -, который описывает элементы управления и местоположения. Пусть дизайнер выплюнет это (и, вероятно, также файл resource.h), и все готово: любой проект C / C ++ может взять эти файлы и скомпилировать их. Управляемый код (C #, VB.Net) имеет несколько больше сложная схема, но это все та же основная идея: напишите описание в стиле, которого ожидают управляемые инструменты, и они с радостью скомпилируют его и будут его использовать.

(Элементы управления ActiveX - как вы уже догадались - целая другая история. Мне не известен стандартный формат, поэтому редактор форм и среда выполнения, в которой используются данные, будут тесно связаны друг с другом - например, редактор форм из pre- .Net VB6 создает формы, которые может использовать только VB. - Я думаю. Это было некоторое время назад ...)

Что касается воссоздания формы: если у вас есть файл .rc, он компилируется в ресурс диалога, в Windows имеется встроенная поддержка для его воссоздания. Аналогично, библиотеки поддержки управляемого кода знают, как воссоздать форму из ее определенного формата. Оба в основном анализируют описание, и для каждого элемента создают элементы соответствующих классов и задают соответствующий стиль, текст и другие свойства, как указано. Он не делает ничего, что вы не можете сделать сам, это всего лишь вспомогательный служебный код.

Обработка фокуса

Для набора HWND в любом контейнере, будь то в «тестовом» режиме или фактически работающем в реальном приложении, и независимо от того, позволяете ли вы Windows или Winforms обрабатывать создание формы или вы сами создали каждый HWND, вы можете добавить поддержка табуляции путем вызова IsDialogMessage в цикле сообщений: подробности см. в разделе замечаний на странице MSDN. (Хотя WinForms может это сделать, я думаю на самом деле выполняет свою собственную обработку фокуса, так что он может иметь порядок табуляции независимо от визуального стекового Z-порядка.)

Другие вещи для изучения ...

Подружитесь с приложением Spy ++ (входит в состав SDK, устанавливается вместе с Visual Studio). Если вы собираетесь что-то делать с HWND, управляемыми или неуправляемыми, очень полезно знать, как использовать этот инструмент: вы можете указать его на любой элемент пользовательского интерфейса в Windows и посмотреть, как он построен из дерева различные типы HWND. Направьте его на дизайнера VB и посмотрите, что на самом деле происходит для вас. (Щелкните значок «бинокль» на панели инструментов, затем перетащите перекрестие в интересующее вас окно.)

Также взгляните на файлы ресурсов, которые выкладывает дизайнер. Все, что можно настроить, переместить или отредактировать в конструкторе форм, соответствует некоторому элементу в одном из этих файлов ресурсов. Сделайте их копию, настройте некоторые параметры, а затем сравните файлы двух наборов и посмотрите, что изменилось. Попробуйте изменить некоторые вещи в файлах вручную (я думаю, что они почти все текстовые), перезагрузите компьютер и посмотрите, подхватил ли дизайнер ваши изменения.

Другие замечания ...

Многое из вышеперечисленного относится только к Windows, в частности к тому, что, поскольку мы используем собственные строительные блоки Window - HWND, - мы можем заставить саму Windows выполнить некоторую тяжелую работу за нас: это дает нам возможность повторно использовать сами элементы управления во время разработки, поэтому нам не нужно рисовать макеты; перехватывать ввод данных в других элементах управления, чтобы мы могли превратить щелчок в движение или в любое другое действие, которое мы хотим, или выяснить, по какому элементу управления выполняется щелчок, без необходимости самостоятельно определять местоположение. Если бы это был дизайнер для какой-то другой инфраструктуры пользовательского интерфейса - скажем, Flash - которая не использует HWND для внутреннего использования, скорее всего вместо этого она использовала бы собственных внутренних средств платформы для выполнения аналогичной работы.

Кроме того, гораздо проще, если вы ограничите количество элементов управления в палитре небольшим конечным набором, по крайней мере, сначала. Если вы хотите разрешить перетаскивание любого элемента управления, например. сторонний или тот, который вы использовали в другом проекте; Обычно вам сначала нужно каким-то образом зарегистрировать этот элемент управления, чтобы разработчик знал, что он доступен в первую очередь. Также вам может понадобиться какой-то способ узнать, какой значок он использует на панели инструментов, как его имя, какие свойства он поддерживает - и так далее.

Веселитесь, исследуя!

6 голосов
/ 04 апреля 2011

Вы реализуете конструктор форм почти как обычный графический интерфейс.У вас есть вещи, которые вы можете перетащить (ваши виджеты), у вас есть вещи, которые вы можете щелкнуть (ваши кнопки), и у вас есть вещи, которые вы можете выбрать (ваши размещенные виджеты), и это действительно так.


Q: Теперь, как вы отображаете окно в графическом интерфейсе?
A: Вы рисуете это, просто так.

В: А как вы храните вещи в этом окне?
A: Вы проверяете границы родительского объекта.Вы могли бы почти сказать, что дизайнер форм похож на маленькую игру, и у вас есть граф сцены, содержащий все ваши виджеты, связанные отношениями родитель-ребенок.

Q: Тогда, как вы выбираете материал в графическом интерфейсе?
A: Проверьте текущую позицию мыши при щелчке мыши по границам всех (близких) виджетов (сцены)График помогает только здесь, как quadtree).

В: Как вы выравниваете виджеты на сетке?
A: Для выравнивания сетки, давайте приведем простой пример: скажем, ваше реальное разрешение 100px на х- ось, но вы хотите, чтобы ваша сетка имела разрешение 10px на х.Теперь скажите, что вы перемещаете свой виджет на 28px в реальном разрешении.Чтобы получить разрешение сетки, вы просто делите на 10, получаете 2.8, округляете его и, наконец, перемещаете плитки виджета 3 в x.Округление является ключевым здесь.только если движение сетки >= ?.5, вы переходите к следующей плитке.В противном случае вы просто останетесь в старом.


Надеюсь, что это может дать вам общий совет о том, как начать дизайнера форм.Повеселись.:)
(PS: не знаю ни о каких конкретных функциях / классах WinAPI / MFC, которые могли бы вам помочь, извините.)

2 голосов
/ 04 апреля 2011

Просто чтобы добавить точку или два к тому, что @Xeo уже сказал:

Прежде всего, нет, вы не всегда рисуете все содержимое самостоятельно.На нормальном этапе проектирования вы просто рисуете что-то похожее на элемент управления, но (по крайней мере, IIRC) оно также позволяет вам «запускать» форму в тестовом режиме (конечно, это делает дизайнер диалогов VC ++, и хотя VB был более примитивнымЯ думаю, что у него тоже есть такая возможность).В тестовом режиме вы могли «запустить» форму до того, как вы (обязательно) прикрепили к ней какой-либо код - даже если нажатие кнопки (например) ничего не делает в окружающей программе, сам элемент управления работает как обычно- кнопка щелкает нормально, элемент управления редактирования позволяет вам редактировать и т. д.

Это делается путем создания экземпляра элемента управления с указанием правильного положения, размера и свойств.Элементы управления ActiveX делают немало для поддержки этого, как и предыдущие «пользовательские элементы управления Windows», хотя и с гораздо меньшей сложностью.С точки зрения элемента управления он работает примерно так же, как обычно, получая входные данные, отправляя уведомление своему родителю и т. Д. Единственное, что изменилось, это то, что родительский элемент больше всего игнорирует большинство получаемых им уведомлений, но элемент управления недействительно не знаю.

Есть два основных способа сделать это.Один из них - создать коллекцию элементов управления самостоятельно, вместе с их позициями, размерами и т. Д. И использовать CreateWindow (или CreateWindowEx и т. Д.) Для создания окна правильного класса.Хотя он относительно прост в обращении, у него есть недостаток, заключающийся в том, что он оставляет вам всю обработку вкладок.

Другая возможность состоит в создании структуры DLGTEMPLATE для хранения данных о диалоговом окне и некоторого DLGITEMTEMPLATES для отдельных элементов управления и, наконец, используйте CreateDialogIndirect, чтобы создать диалоговое окно с этими спецификациями, содержащее эти элементы управления.Это утомительно, но работает, и когда вы закончите, оно автоматически обрабатывает вкладки между элементами управления (и работает так же, как любой другой диалог, так как это тот же код Windows, создающий его в любом случае).

Во-вторых, так какВы пометили этот C ++, возможно, вы захотите взглянуть на код в CodeProject, который фактически реализует редактор диалогов.Хотя он не так сложен, как некоторые из коммерческих, он представляет собой разумно полный редактор форм / диалогов, в комплекте с большей частью того, о чем вы спрашивали.

...