Форум программистов, компьютерный форум, киберфорум
Наши страницы

C++ и WinAPI

Войти
Регистрация
Восстановить пароль
 
FrostAlex
4 / 4 / 0
Регистрация: 26.09.2013
Сообщений: 33
#1

Собственное визуальное оформление окна. Как правильно реализовать? - C++ WinAPI

01.09.2015, 16:28. Просмотров 1199. Ответов 9
Метки нет (Все метки)

При реализации многооконного интерфейса программы, столкнулся с несколькими проблемами:

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

Цель - воплотить интерфейс окна, подобный интерфейсу той же Visual Studio 2012. Много где пишут, что для этого следует иметь дело с сообщениями WM_NC... Но одной отрисовкой тут не обойтись - следует, как мне кажется, сначала удалить существующие элементы управления (или изменить) в НО, и создать свои собственные дополнительные контролы.

Есть и другой способ - задать стиль окна WS_POPUP - но он не подходит, ибо тогда теряются специфические, для Windows7 и старше, возможности стандартного окна (перетаскивание окон до упора вверх, и разворот на весь экран, в стороны, window-shaking и подобное). Возможно ли их каким-либо образом сымитировать программно?

Вторая проблема - это реализация СВО, только уже для дочерних окон (но не контролов! там я так понимаю достаточно обрабатывать WM_DRAWITEM). Снова-таки, интересует общий алгоритм создания дочерних окон управления со своим собственным дизайном: какие сообщения следует обрабатывать, какие стили задавать и прочее.

В общем, если кто-то когда-то успешно создавал СВО для приложений - пожалуйста, поделитесь опытом, материалами, источниками, которые могут послужить стартовой точкой в этом деле! Возможно в мсдн есть отдельная статья по этому поводу, а я ее просто проглядел - тогда не серчайте, дайте ссылку и вопрос будет решен)

P.S. Если кто-то знает как устроена реализация интерфейса именно в visual studio - поделитесь, пожалуйста, уж очень интересует как макйкрософтовцы такое сделали, и редактируют из года в год (видимо, без особых трудностей).
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
01.09.2015, 16:28
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Собственное визуальное оформление окна. Как правильно реализовать? (C++ WinAPI):

Как создать собственное сообщение и послать его из одной функции окна в другую - C++ WinAPI
Извиняюсь, если уже задавали этот вопрос, но похожей темы я не нашел. Как создать собственное сообщение и послать его из одной функции окна...

Как правильно реализовать двухбуферную отрисовку большого количества полигонов? - C++ WinAPI
Всем привет! Пытался найти способы устранения мерцания при отрисовки большого кол-ва примитивов, дошёл до двойной буферизации. Много тем и...

Как правильно реализовать задачу (всплывающие окна)? - JavaScript
Суть такая - есть php код, который выводит кучу ссылок вида echo'<a...

Как можно подключить визуальное оформление ХР, чтобы кнопки были объемными а не плоскими - Visual Basic
Как можно подключить визуальное оформление ХР, чтобы кнопки были объемными а не плоскими.

Собственное оформление меню Печать - C# WPF
Доброго времени суток форумчане. хочу сделать меню Печать с возможностью вывода на печать. внешнее оформление сделал, каким образом...

визуальное оформление - MS Excel
Доброго времени суток! В общем нужно сделать перевод из одной системы счисления в другую, для меня это не проблема. Нужно показать, как...

9
Operok
166 / 164 / 46
Регистрация: 15.02.2015
Сообщений: 480
Завершенные тесты: 2
01.09.2015, 19:10 #2
На сколько я себе представляю, нужно пробовать обрабатывать сообщение WM_NCHITTEST, которое должно возвращать значение - местоположение курсора. Т.е. через это сообщение вы сами можете уведомить систему что мышь сейчас находится на заголовке окана, например (HTCAPTION), тогда когда вам будет угодно.
По поводу собственных элементов управления, то для начала нужно определиться с функционалом этого "окна", обрабатывать много чего нужно будет (зависит от функционала), нажатия/отжатия кнопок (мыши и клавы), отрисовка (это пожалуй главное) и т.д.

Добавлено через 1 час 37 минут
https://github.com/wine-mirror/wine/tree/master/dlls/user32 тут много чего можно узнать о стандартных элементах управления, хоть это и не исходники winAPI, это пожалуй наиболее приближенная его реализация. Только хочу добавить, если уж использовать C++ для создания собственных контролов, то сразу оборачивать их в классы, а для рисования лучше сразу использовать GDI+ (gdiplus.h, gdiplus.lib).
1
FrostAlex
4 / 4 / 0
Регистрация: 26.09.2013
Сообщений: 33
01.09.2015, 20:12  [ТС] #3
Информация на счет WM_NCHITTEST'a действительно помогла. Спасибо.
На счет функционала окна и классовой обертки - это все понятно. Я хочу создать общий базовый класс для всех дочерних окон в моем приложении, который будет при инициализации создавать два окна: первое - дочернее окно, а второе - окно рабочей области, которое является дочерним по отношению первого окна. Таким образом все наследники класса будут получать окно - рабочую область - в которой они вольны делать все что угодно. А первое окно будет отвечать за внешний вид окна и именно в его оконную процедуру будут сперва попадать все сообщения, и именно его неклиентскую область я хочу обрабатывать, чтобы придать всем дочерним окнам похожий вид.

Я бы хотел еще поинтересоваться, не возникнет ли у меня проблем с нативной отрисовкой неклиентской области? Дело в том, что при первых моих тестах с сообщением WM_NCPAINT я замечал эффект мерцания, что свидетельствует о том, что неклиентская область перерисовывается сперва самой Windows. Как можно отключить перерисовку НО?

Добавлено через 23 минуты
Аа, еще одно, возможно Вы подскажете мне как следует грамотно поступать в следующей ситуации:
Допустим у меня есть класс какого-то собственного окна, например окна параметров какого-то ресурса: картинки, звукового файла или еще что-то. Я не думаю что стоит для каждого окна регистрировать свой класс, но тогда у меня возникает вопрос на счет оконной процедуры. Естественно что первую оконную процедуру я устанавливаю при первой регистрации класса окна, которая происходит при создании первого объекта класса, ну или же по вызову функции RegWndClass. И вероятнее всего это будет DefWindowProc.
У каждого окна параметров есть свой набор контролов, которым он должен как-то управлять и для этого ему нужна своя личная оконная процедура. В таком случае я думаю, что следует вот эту оконную процедуру создавать где-то возле описания класса (с++) конкретного окна параметров и "жестко" подменять оконную процедуру у вновь созданного окна внутри его класса.
Примерно так:
C++
1
2
3
4
5
6
7
8
9
10
11
LRESULT WndProcForExactCustomWindow(...)
 
class ExactCustomWindowClass: public CustomWindowBasic
{
public:
void Initialize(...)
{
hWnd = CreateWindow("CommonStandardWindowClass", ...);
SetWindowLong(hWnd, GWL_WNDPROC, WndProcForExactCustomWindow);
}
}
Стоит ли действовать таким путем, или же регистрация классов окон не особо утруждает Windows?
А так же, как правильнее организовывать саму оконную процедуру: стоит ли использовать статические переменные для сохранения значений? А если одну и ту же относительно простую процедуру надо будет использовать двум контролам одновременно? Тогда значения для одного окна будут мешать работе другого... Значит надо использовать GetWindowLong с ключом GWL_USERDATA и к каждому окну привязывать объект класса которому он принадлежит?
0
Operok
166 / 164 / 46
Регистрация: 15.02.2015
Сообщений: 480
Завершенные тесты: 2
02.09.2015, 10:05 #4
Делай как все... в базовом классе опиши виртуальный метод, который будет обрабатывать основные сообщения окна, вызывая другие методы класса (тоже лучше виртуальные). Сигнатуру можно задать какую-нибудь такую: LRESULT WndProc(UINT msg, WPARAM wParam, LPARAM lParam); или передавать по ссылке свою структуру:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Message
{
    UINT msg;
    WPARAM wParam;
    LPARAM lParam;
    LRESULT lResult = NULL;
};
 
//методы класса
virtual void OnWmPaint(Message &msg);
//...
virtual void WndProc(Message &msg)
{
   switch (msg.msg) {
   case WM_PAINT:
       OnWmPaint(msg);
       return msg.lResult;
   ...
   default:
       return ::DefWindowProc(this->Hwnd, msg.msg, msg.wParam, msg.lParam);
   }
}
...
Перегружая виртуальные методы, ты будешь создавать свою обработку сообщений для класса-контрола. Останется одна проблема, как из CALLBACK функции вызвать метод нужного класса? Нужна функция которая по хендлу окна будет возвращать указатель на нужный объект (контрол). Вариантов много, основные это: ассоциативный массив <HWND, CustomWindowBasic*> (подобное используется в MFC), GWL_USERDATA окна (GetWindowLong/SetWindowLong), свойства окна (GetProp/SetProp - медленнее чем с GWL_USERDATA) и, на сколько мне известно, самый быстрый способ - переходник, с которым я решил не возиться (используется в WTL, позволяет передавать в оконную процедуру вместо хэндла окна указатель на объект-контрол).
В базовом классе нужны статическая __stdcall функция, которую нужно будет указывать при регистрации класса. Задача этой функции - получить указатель на контрол и вызвать его метод обработки сообщений. Если получится всё поведение контрола описать виртуальными методами, то достаточно будет регистрации одного класса. На сколько я знаю, в дотНете даже при создании стандартных контролов, типа кнопки или комбобокса, делается перерегистрация класса с новым именем и оконной процедурой.

По поводу отрисовки неклиентской области. Разве собственная обработка сообщения WM_NCHITTEST не позволяет выдавать системе область клиентской области за неклиентскую (я именно так растолковал описание этого сообщения)? И таким образом создавать TOPMOST окно и самому рисовать заголовок и системное меню.
1
FrostAlex
4 / 4 / 0
Регистрация: 26.09.2013
Сообщений: 33
02.09.2015, 12:06  [ТС] #5
Благодарю!
Да, как Вы и говорили, обработка WM_NCHITTEST устраняет проблему с перерисовкой. Просто если обрабатывать только сообщение WM_NCPAINT (что я понимаю уже, весьма глупо) то перерисовка происходит, но при обработке WM_NCHITTEST по умолчанию, Windows получает данные о том, что я нахожусь на стандартных областях (кнопках, пунктах меню), и таким образом отрисовывает поверх моей НО еще и свои элементы, при этом даже не отправляет сам WM_NCPAINT, что по-моему является особенно странным... Ведь именно это сообщение отвечает как раз на перерисовку неклиентской области, и именно оно должно "оставлять за собой последнее слово" о том, как будет выглядеть НО...

P.S. Было бы гораздо удобнее, если WM_NCPAINT принимало в своем lParam'е еще и значение возвращенное WM_NCHITTEST'ом...
1
Operok
166 / 164 / 46
Регистрация: 15.02.2015
Сообщений: 480
Завершенные тесты: 2
02.09.2015, 15:06 #6
Да, если ловить сообщение WM_NCPAINT, нужно будет всю НО самому перерисовывать, как-то так .
Получается, что не всегда проверка местоположения указателя мыши делается через NCHITTEST, например, если определить свою область HTCLOSE и включить стиль WS_SYSMENU, то при нажатии на эту область будет прорисовываться стандартная (win95) кнопка закрыть ровно в правом верхнем углу (и это при заблокированном WM_NCPAINT), более того, нажатие на кнопку будет засчитано если ты нажал ЛКМ в области HTCLOSE, а отпустил над кнопкой.
Выходит что нужно отключать все стили связанные со стандартым окном (WS_CAPTION, WS_SYSMENU, WS_BORDER, WS_SIZEBOX, и т.д.), в обработчике WM_NCHITTEST полезно будет обработать области HTCAPTION, HTTOP, HTBOTOM и прочие, для возможности перетаскивания окна и изменения размера (всё-таки придётся включить стили WS_MINIMIZEBOX и WS_MAXIMIZEBOX, но не обрабатывать области HTMAXBUTTON и HTMINBUTTON). WM_NCHITTEST + HTCAPTION позволяет перетаскивать окно, разворачивать по двойному клику, но не позволяет разворачивать окно перетаскивая окно к верхнему краю экрана, не полностью функционируют комбинации win+(стрелки). Возможно, потому что эти функции были добавлены в ГУИ с последними версиями ОС в виде каких-нибудь костылей, типа тем оформления, они ещё 20 лет назад зареклись не менять старый API для обратной совместимости.

Добавлено через 19 минут
Понял! за манипуляции с размером окна отвечает WM_SIZEBOX (он же WS_THICKFRAME), важен именно что бы был включен этот стиль. Но вот как сделать эту рамку невидимой?
1
FrostAlex
4 / 4 / 0
Регистрация: 26.09.2013
Сообщений: 33
02.09.2015, 17:06  [ТС] #7
Частичное решение заключается в том, чтобы окно (главное) попросту создавать со стилем WS_POPUP, а далее просто нужно корректно обработать WM_NCHITTEST (WM_NCPAINT уже понятное дело отпадает). Окно спокойно перемещается и меняет свои размеры, да вот только беда в том, что не поддерживаются последние функции GUI (Ох уж эти инвалиды-майкрософтовцы, на костылях...). Но мы сейчас по идее преследуем другую цель)

Я поэкспериментировал и заметил, что если с WS_SIZEBOX задать еще WS_MAXIMIZEBOX и обязательно WS_POPUP , то мы добьемся поддержки вот этих вот самых функций перемещений окон, а так же shaking'а. Но вот тонкая рамка остается... Но она вообще никак не мешает, ее можно попросту закрашивать в какой-то свой цвет и не обращать на нее внимания.
Суммируя: наиболее (пока что, надеюсь) приближенный вариант решения проблемы является задания стиля окна комбинацией таких флагов: WS_POPUP | WS_SIZEBOX | WS_MAXIMIZEBOX.
И да... В msdn написано, что WS_MAXIMIZEBOX не может быть задан без WS_SYSMENU, однако свой эффект он дает, и именно из-за него окно поддерживает "визуальные функции" винды.

Добавлено через 37 минут
А нет... Не подходит метод, предложенный выше мною. В том случае, у винды вообще сносит "крышу" и она начинает отображать окно на весь экран, не учитывая маленькую рамку справа и снизу... Это однозначно багованый способ.
P.S. При использовании WS_POPUP придется однозначно забыть про стандартное меню. И вероятнее всего придется писать свое.

Добавлено через 36 минут
В общем, нашел я похожее обсуждение:
Интерфейс приложений в стиле Office 2013, Visual Studio 2012/2013, Adobe CC
Вся суть оказывается заключается в создании главного окна прозрачным и накидыванием на него уже непрозрачных дочерних окон (достаточно глупое решение проблемы, и как я успел заметить, достаточно корявое).

Попробовал воплотить из второго поста (из этого обсуждения) совет, только на winapi.
Задал стиль WS_EX_LAYERED и изменил прозрачность, да вот только не получается дочерние окна нормально отрисовывать. Они имеют такую же прозрачность как и их отцовское окно. Попробовал также использовать WS_CLIPCHILDREN для того, чтобы вырезать области дочерних окон из области отрисовки главного окна (я ведь правильно понимаю предназначение этого флага?).
Как можно избежать перенесения прозрачности на дочерние окна?
1
Operok
166 / 164 / 46
Регистрация: 15.02.2015
Сообщений: 480
Завершенные тесты: 2
04.09.2015, 14:28 #8
Решений на чистом GDI (или GDI+) нет, так как в винде есть правило, что окно рисует только само себя, т.е. оно не может рисовать в динамике то, что находится за ним, так как не знает, когда там происходят изменения. WS_EX_LAYER появился с выходом win2000 и позволил делать (полу)прозрачные окна (тень курсора, полупрозрачная иконка файла при драг-дропе и т.д.), но подобная отрисовка уже возлагается на видеокарту. Например, я перекинул программку с Layered окном на удалённую машину через тимВьювер и запустил её, ничего, само собой не вышло, окно просто не отобразилось на экране. Кстати, начиная с вин8 можно делать "слоёные" дочерние окна, но отключать прозрачность у дочерних окон с Layered предком по прежнему нельзя.
Насчёт "нечистого" GDI, т.е. с использованием OpenGL или DirectX(2d) можно нарисовать что угодно, вот например те же слоёные окна с помощью openGL, мы как бы рисуем с помощью openGL, но в битмап, который потом выводим на экран с помощью GDI. Правда это медленно по сравнению с чистым использованием openGL, но создавать десктопные приложения на чистом DirectX или OpenGL, думаю не лучший вариант, так?
1
UI-Maker
38 / 38 / 9
Регистрация: 05.09.2015
Сообщений: 262
05.09.2015, 16:35 #9
FrostAlex :: С обычными и со слоёными окнами надо работать по-разному. Давайте вы нарисуете и подробно поясните что именно хотите получить, а после я попробую подсказать?
Интерфейса "Visual Studio 2012" не знаю. Кстати на чём вы пишете? MFC+WinAPI? Чистый WinAPI?

Operok :: Я вас немного поправлю. В Win2000/XP/Vista оно как раз не "возлагается на видеокарту". Лишь с Win7 они перенесли это на неё.
0
FrostAlex
4 / 4 / 0
Регистрация: 26.09.2013
Сообщений: 33
06.09.2015, 18:13  [ТС] #10
Operok: Спасибо. Буду дальше что-то искать, пробовать. Скорее всего доберусь до, как Вы и упомянули, до DirectX.
UI-Maker: Пишу на чистом WinAPI. То, чего я хочу добиться, это абсолютно полной самостоятельной отрисовки рамки окна (иконка приложения, заголовок, управляющие кнопки закрытия, свертывания, рамка окна и подобное + свои дополнительные контролы), так, чтобы Windows вообще не вмешивался в эту отрисовку. При этом я хочу сохранить абсолютно полностью весь функционал стандартных окон так, чтобы любой новый специфический для новой версии Windows функционал подхватывался самостоятельно (например shaking или же позиционирование окна в зависимости от того, к какой границе экрана его прижать при переносе).
На счет картинок - выше я кидал ссылку на другое обсуждение, там приведен пример визуального оформления окна. Вот что-то в этом роде я желаю воплотить, но используя чистый WinAPI (если конечно же это возможно, и если не начали для таких целей широко использовать DirectX и прочие графические апи).
0
06.09.2015, 18:13
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
06.09.2015, 18:13
Привет! Вот еще темы с ответами:

Визуальное оформление приложений - Xamarin
Доброго времени суток! Задам, наверное, один из самых распространённых вопросов: какие интерфейсы можно делать при помощи Xamarin? ...

TreeView визуальное оформление - C# WPF
собственно не как не получается заставить его изменить свой внешний вид. так как элементы у меня добавляются программно (как написано в...

Визуальное оформление PHP - PHP Сети
Здравствуйте , есть php код , как сделать выравнивание его по середине , изменить цвет фона и текста Вот сам код , скриншот ниже : ...

Оформление: визуальное разграничение блоков - HTML, CSS
Кто как разграничивает блоки? Бордеры, тени, отступы? Может быть кто то поделится эффектными способами разграничения информации? ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
10
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.