2016-05-10 3 views
-2

я начал недавно учить C++ и WinAPI, я хочу, чтобы иметь возможность создавать свои собственные программыПочему C++ отправляет WM_DRAWITEM в родительский процесс?

Я подклассы кнопки (в отдельный файл, потому что я люблю вещи в чистоте и организованы - это то, что это плохая идея), потому что я хочу иметь несколько таких же параметров. Я также хочу его нарисовать, и вопрос, который появляется у меня на голове, - это не лучше, если бы все было в файле класса? Значение всех параметров, включая пользовательскую ничью. Досадно идти в основной файл, чтобы изменить внешний вид и задать все остальные параметры в файле класса. Я использую кодовые блоки.

EDIT (объяснение):

#include <Windows.h> 
#include <Winuser.h> 
#include "CustomButton.h" 

/*global vars*/ 
WNDPROC CustomButton::CustomButtonLongPtr; 

/*functions*/ 
LRESULT CALLBACK CustomButtonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 

CustomButton * CustomButton::CreateCustomButton(HINSTANCE hInstance, HWND hwnd, int pos_x, int pos_y, int width, int height) 
{ 
    CustomButton * p_CustomButton = new CustomButton; 
    HWND customButton = CreateWindowEx(0, "BUTTON", "OK", WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, pos_x, pos_y, width, height, hwnd, (HMENU)200, (HINSTANCE)GetWindowLong(hwnd, GWLP_HINSTANCE), p_CustomButton); 
    if(customButton == NULL) 
    { 
     delete p_CustomButton; 
     MessageBox(NULL, "Problem creating the Search box.", "Error", 0); 
     return 0; 
    } 
    CustomButton::CustomButtonLongPtr = (WNDPROC)SetWindowLongPtr(customButton, GWLP_WNDPROC, (LONG_PTR)&CustomButton::CustomButtonProc); 
    return p_CustomButton; 
} 

Я хочу использовать WM_DRAWITEM для этой кнопки в CustomButtonProc и мне интересно, почему разработчики считают, что это будет перехитрили, чтобы использовать его только в родительских WinProc.

+1

Что вы подразумеваете под 'не было бы лучше иметь все это в файле класса? Значение всех параметров, включая пользовательский draw.' Покажите нам пример кода – stackptr

+2

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

+0

В качестве альтернативы вы можете переопределить 'WM_PAINT' при нажатии кнопки подкласса. –

ответ

2

Это немного сложно объяснить.

Вы, вероятно, исходит от фона, на котором вы подключите функцию в розетку для обработки событий, что-то вроде

extern void onClicked(void); 
button->OnClicked = onClicked; 

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

void (*OnClicked)(void); 
void (*OnDoubleClicked)(void); 
void (*OnDisabled)(void); 
void (*OnHighlight)(void); 
void (*OnKillFocus)(void); 
void (*OnPaint)(void); 
void (*OnSetFocus)(void); 
void (*OnUnhighlight)(void); 
void (*OnUnpushed)(void); 
HBRUSH (*OnCtlColorButton)(void); 

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

Поскольку Windows нуждается в способе связи между системой и окном и между окнами, Microsoft решила создать интерфейс передачи сообщений, в котором каждое сообщение имело 16-разрядный код и два размера указателя (изначально один 32-разрядный и один 16-разрядный) и возвращаемое значение размера указателя. И не так много сообщений, которые нужны Windows для себя, предоставляя как оконным классам, так и приложению много недвижимости для использования сообщений для общения. Так почему бы не использовать сообщение для оповещения о событии?

Использование сообщения позволяет избежать потери памяти, в то же время позволяя кнопке передавать данные и возвращать данные из целевого окна. Таким образом, логика следует, что все кнопки будет нужно сделать, это

/* this is not the correct syntax but let's use it for expository purposes */ 
#define BN_CLICKED someNumberHere 
case WM_LBUTTONUP: 
    SendMessage(GetParent(hwnd), BN_CLICKED, hwnd); 
    break; 

и родитель будет обращаться с этим:

case BN_CLICKED: 
    if (whichButton == button1) 
     doButton1Stuff(); 
    break; 

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

Теперь зачем отправлять сообщение родителям? Если мы рассматриваем окна как конечные точки связи, то это очевидно: вы хотите, чтобы кнопка сообщила родителям, что она была нажата, потому что вы сообщаете, что нажата кнопка!

Но, что более важно, вы не указали процедуру окна кнопки. Microsoft сделала, и они предоставляют одну и ту же программу для каждой программы. Если бы вы могли обработать сообщение в процедуре кнопки, куда бы вы его положили? В конце концов, вы не можете изменить процедуру кнопки.

(В настоящее время мы, что называется «подклассы», который позволяет переопределить оконную процедуру единого окна, чтобы сделать собственную обработку. Это не используется для обработки событий, потому что это больше работы, чем просто отправка до родителя.)

Все это распространяется на пользовательскую ничью; просто замените «custom draw» на «clicked», и это все равно имеет смысл. Надеюсь, это объяснение было ясным даже с этой ментальной заменой.


Если вы хотите, вы можете написать собственное средство обработки событий в пути указателя функций. Сохраните карту оконных дескрипторов функций событий и вызовите глобальную функцию отправки во всех ваших оконных процедурах для обработки сообщений о событиях WM_COMMAND, WM_NOTIFY и (для трековых баров) WM_HSCROLL и WM_VSCROLL. Как вы это делаете, зависит от вас, но подумайте, действительно ли вы хотите сделать это таким образом; иногда это необходимо, но иногда это не так. Если вы это сделаете, не забудьте предоставить способ передачи произвольных данных функции события, которая была решена при подключении к событию, поэтому обработчик события может сделать что-то разумное, не полагаясь на глобальное состояние.

+0

Я вижу, поэтому окно proc было создано до того, как подклассы появились. Я рад, что есть способы переопределить это, потому что это делает жизнь более сложной, распространяя различные части кода повсюду. Спасибо за подробное объяснение! :) – Xzsh4s575sf75

0

Благодаря комментариям RemyLebeau и IInpectable я также смог найти решение моего разочарования, которое я собираюсь объяснить здесь для кого-то еще, кто почесывает головы по этой самой проблеме. Это решение не требует VCL или любого компонента Visual Studio и т. Д.

Сначала определите собственное пользовательское сообщение, в том, что вы можете достичь его в WndProc:

#define MY_DRAWITEM (WM_APP+1) 
UINT uDrawButtonMsg = RegisterWindowMessage(_T("MY_DRAWITEM")); 

Затем выяснить, какой номер присвоен:

std::cout << uDrawButtonMsg; //for example 49648 

И отправить это сообщение от WndProc ваших подклассов управления из любого сообщения, которое требуется, например, WM_DRAWITEM:

case WM_DRAWITEM: 
{ 
    ::SendMessage(p_CustomButton->customButton, uDrawButtonMsg, wParam, lParam); 
    break; 
} 

А потом в подклассе только поймать сообщение числом 5 цифр вы искали давно:

if(49648 == uMsg) 
{ 
    //do your DRAWITEM stuff here 
} 

Спасибо всем, кто внес свой вклад в эту статью помогая exaplanation, советы, а также исторический фон!

+0

[RegisterWindowMessage] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms644947.aspx) предназначен для обмена данными между процессами (идентификатор сообщения является уникальным во всем * * система **). Это не то, что вам нужно. Вы можете статически назначать идентификаторы сообщений в диапазоне 'WM_APP + x'. Btw. вы подписываетесь на старый способ [подклассифицировать элементы управления] (https://msdn.microsoft.com/en-us/library/windows/desktop/bb773183.aspx), который имеет значительные недостатки. – IInspectable

+0

@Инспективный, так что вы говорите, что я должен пойти с SetWindowSubclass? И использование этого также решит мои проблемы с сообщением DRAWITEM? – Xzsh4s575sf75

+0

Элементы управления подклассами ничего не изменят. Сообщение 'WM_DRAWITEM' по-прежнему переходит к родительскому. Я предполагал, что вам не нужно регистрировать ** системные широкие ** уникальные идентификаторы сообщений, когда отправитель и получатель живут в одном приложении. – IInspectable