Форум программистов
 
Контакты: о проблемах с регистрацией, почтой и по другим вопросам пишите сюда - alarforum@yandex.ru, проверяйте папку спам! Обязательно пройдите активизацию e-mail.

Вернуться   Форум программистов > Низкоуровневое программирование > Win Api
Регистрация

Восстановить пароль
Повторная активизация e-mail

Ответ
 
Опции темы
Старый 04.04.2018, 05:14   #1
ArhiCoder
Пользователь
 
Регистрация: 03.04.2018
Сообщений: 11
Репутация: 10
Восклицание Subclass Paint Button Control

Здравствуйте.
Ищу помощь в теме о замене процедуры отрисовки для Window Controls (в данный момент начну с Button Control, т.к. подготовил пример).
Суть - полностью заменить отрисовку стандартных контролов, любым способом(в дальнейшем хочу использовать данный код для вывода графики на поверхности типа DirectX или OpenGL).
Нашёл несколько вариантов:
1) Добавление флага OwnerDraw, и в оконной процедуре обработать WM_DRAWITEM, отрисовав с помощью ф. BitBlt() своё изображение, при том что там же можно поменять поверхность вывода. Однако такой способ мне не очень симпатичен.
2) Subclassing WM_PAINT процедуры для конкретного контрола (т.к. планирую заменить отрисовку не только для кнопок, но и для других контролов, то данный метод может служить примером).
И так вот код для PUSHBUTTON кнопки, однако он работает не очень правильно. После нажатия на кнопку (LEFTBUTTONDOWN) кнопка всё ещё рисует стандартным способом то, что должна по дефолту (хотя я разсчитывал, что данное сообщение пришлёт повторный WM_PAINT и отловив состояние, я бы перерисовал по своему).
Код:

// Windows 32 Application->EmptyProject
// Source Files->New Item->.cpp (ANSI file)
// Properties->General(All configurations)->Use MultyByte CharSet
//a.Pragma
#if (_MSC_VER >= 1400) // Check MSC version
#pragma warning(push)
#pragma warning(disable: 4996) // sprintf() unsafe
#pragma warning(disable: 4312) // conversion() int to ..
#endif
//a.Include
#include <windows.h>
#include "windowsx.h"
#include <atlbase.h>
//g.Defines
#define ID_LOG 904
#define ID_CTL 400
#define uWM_ACREATE	1200
//g.Variables:
int gMenu[] = {100,101,102};
HINSTANCE hInst;
HWND hMlr=NULL;
int gLnm=0;
const int gCls=2;
const int gWnd=4;
HWND mhWnd[gWnd]={NULL};
char gWNm[gWnd][20]={"Win32Main","Logger","Settings","Help"};
//g.Controls
const int gCtl=10;
HWND hCtl[gCtl]={NULL};
WNDPROC gDef[gCtl]={NULL};
//f.Prototypes - Main
int RegWindow(int);
int CreWindow(int);
int UnregWnd();
void LogMessage(char *);
//
LRESULT CALLBACK ProcMain(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ProcChild(HWND, UINT, WPARAM, LPARAM);
//
LRESULT CALLBACK ButtonProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK CustomProc(HWND, UINT, WPARAM, LPARAM);
//
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	MSG msg;
	int mErr[5]={0};
	hInst=hInstance;
	{
		char fMb[100]="";
		for(int i=0;i<gCls;i++)
		{
			mErr[0]=RegWindow(i);
			if(mErr[0]!=1){
				sprintf(fMb,"RegisterClass(cls%ld) Failed. Er#%ld",i,mErr[0]);
				MessageBox(NULL, fMb, "Error!", MB_OK);
				return 0;
			}
		}
		mErr[0]=CreWindow(0);
		if(mErr[0]!=1){
			sprintf(fMb,"CreWindow(%ld) Failed. Er#%ld",0,mErr[0]);
			MessageBox(NULL, fMb, "Error!", MB_OK);
			UnregWnd();
			return 0;
		}
	}
	if(!mhWnd[0]){
		MessageBox(NULL, "MainHandle Failed.", "Error!", MB_OK);
		return 0;
	}
	//start
	while(GetMessage(&msg,NULL,0,0))
    {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
    }
	//exit
	{
		char fMb[100]="";
		mErr[0]=UnregWnd();
		if(mErr[0]!=1){
			sprintf(fMb,"UnregisterClass() Failed. Er#%ld",mErr[0]);
			MessageBox(NULL, fMb, "Error!", MB_OK);
		}
	}
	return (int) msg.wParam;
}
int UnregWnd()
{
	char fMb[20]="";
	int fErr=0;
	for(int i=gCls-1;i > -1;i--)
	{
		sprintf(fMb,"cls%ld",i);
		fErr=UnregisterClass(fMb,hInst);
		if(fErr==0)
			return -i;
	}
	return 1;
}
int RegWindow(int vd)
{
	if(vd < 0 || vd >= gCls)
		return -1;
	char fMb[20]="";
	WNDCLASS Wcc;
	Wcc.style=CS_HREDRAW | CS_VREDRAW;
	if(vd == 0)
		Wcc.lpfnWndProc=ProcMain;
	else
		Wcc.lpfnWndProc=ProcChild;
	sprintf(fMb,"cls%ld",vd);
	Wcc.lpszClassName=fMb;
	Wcc.cbClsExtra=0;
	Wcc.cbWndExtra=0;
	Wcc.hInstance=hInst;
	Wcc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
	Wcc.hCursor=LoadCursor(NULL,IDC_ARROW);
	Wcc.hbrBackground=(HBRUSH)GetStockObject(COLOR_WINDOW+1);
	Wcc.lpszMenuName=NULL;
	if(!RegisterClass(&Wcc))
		return -2;
	return 1;
}
int CreWindow(int vd)
{
	RECT frc;
	HWND hDesktop;
	if(vd < 0 || vd > gWnd)
		return -1;
	if(mhWnd[vd])
		return -2;
	if(vd == 0)
		mhWnd[vd]=CreateWindow("cls0",gWNm[vd],WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInst,NULL);
	else
	{
		if(!mhWnd[0])
			return -3;
		mhWnd[vd]=CreateWindow("cls1",gWNm[vd],WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,mhWnd[0],NULL,hInst,NULL);
	}
	if(!mhWnd[vd])
		return -4;
	ShowWindow(mhWnd[vd],SW_SHOW);
	hDesktop = GetDesktopWindow();
	GetWindowRect(hDesktop, &frc);
	if(vd == 0)
		MoveWindow(mhWnd[vd], 0, 0, frc.right/2, frc.bottom/2, true);
	else
		MoveWindow(mhWnd[vd], frc.right/2, 0, frc.right/2, frc.bottom-50, true);
	//MoveWindow(mhWnd[vd], frc.right/2, frc.bottom/2, frc.right/2, frc.bottom/2, true);
	SendMessage(mhWnd[vd], uWM_ACREATE, (WPARAM)0, (LPARAM)0);
	return 1;
}
LRESULT CALLBACK ProcMain(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;
	int fErr[10]={0};
	char fBuf[250]="";
	//
	switch (message)
	{
	case WM_CREATE:
		{
			HMENU hMenu=NULL, hFileMenu=NULL, hHelpMenu=NULL;
			hFileMenu = CreatePopupMenu();
			hHelpMenu = CreatePopupMenu();
			if(hFileMenu == NULL || hHelpMenu == NULL)
				MessageBox(hWnd, "CreatePopupMenu() Failed.", "Error!", MB_OK);
			else
			{
				AppendMenu (hFileMenu, MF_ENABLED | MF_STRING, gMenu[0], "&Logger"); 
				AppendMenu (hFileMenu, MF_GRAYED | MF_STRING, gMenu[1], "E&xit");
				AppendMenu (hHelpMenu, MF_ENABLED | MF_STRING, gMenu[2], "&About");
				hMenu = CreateMenu();
				if(hMenu == NULL)
					MessageBox(hWnd, "CreateMenu() Failed.", "Error!", MB_OK);
				else
				{
					AppendMenu(hMenu, MF_ENABLED | MF_POPUP, (UINT_PTR) hFileMenu, "&File");
					AppendMenu(hMenu, MF_ENABLED | MF_POPUP, (UINT_PTR) hHelpMenu, "&Help");
					fErr[0]=SetMenu (hWnd, hMenu);
					if(fErr[0] == NULL)
						MessageBox(hWnd, "SetMenu() Failed.", "Error!", MB_OK);
				}
			}
		}
		break;
	case uWM_ACREATE:
		{
			fErr[0]=CreWindow(1);
			if(fErr[0]!=1){
				sprintf(fBuf,"CreWindow(%ld) Failed. Er#%ld",1,fErr[0]);
				MessageBox(hWnd, fBuf, "Error!", MB_OK);
			}
			else
				LogMessage("Logger Activated");
			//
			WNDCLASS fWcl = {0};
			fWcl.lpszClassName = "MyControl";
			fWcl.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
			fWcl.style         = CS_HREDRAW;
			fWcl.lpfnWndProc   = CustomProc;
			fWcl.hCursor       = LoadCursor(0, IDC_ARROW);
			RegisterClass(&fWcl);
			hCtl[0] = CreateWindowEx(WS_EX_STATICEDGE , "MyControl", "Custom", WS_CHILD | WS_VISIBLE, 5, 5, 100, 30, hWnd, (HMENU)ID_CTL, hInst, NULL);
			hCtl[1] = CreateWindow("button", "OwnBtn", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_NOTIFY | BS_OWNERDRAW, 5, 50, 100, 23, hWnd, (HMENU)ID_CTL+1, hInst, NULL);
			hCtl[2] = CreateWindow("button", "SubBtn", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 115, 50, 100, 23, hWnd, (HMENU)ID_CTL+2, hInst, NULL);
			gDef[2] = (WNDPROC)SetWindowLong(hCtl[2], GWL_WNDPROC, (long)ButtonProc);
		}
		break;
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		if(wmId==gMenu[0])
		{
			if(mhWnd[1] != NULL){
				DestroyWindow(mhWnd[1]);
				mhWnd[1]=NULL;
			}else{
				fErr[0]=CreWindow(1);
				if(fErr[0]!=1){
					sprintf(fBuf,"CreWindow(1) Failed. Er#%ld",fErr[0]);
					MessageBox(hWnd, fBuf, "Error!", MB_OK);
				}
			}
		}
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		for(int i=1;i<gWnd;i++)
		{
			if(mhWnd[i]!=NULL){
				DestroyWindow(mhWnd[i]);
				mhWnd[i]=NULL;
			}
		}
		if(hCtl[0])
		{
			DestroyWindow(hCtl[0]);
			UnregisterClass("MyControl", hInst);
		}
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}


Последний раз редактировалось ArhiCoder; 04.04.2018 в 05:28.
ArhiCoder вне форума   Ответить с цитированием
Старый 04.04.2018, 05:16   #2
ArhiCoder
Пользователь
 
Регистрация: 03.04.2018
Сообщений: 11
Репутация: 10
По умолчанию

Код:

LRESULT CALLBACK ProcChild(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	WNDCLASS wr;
	wr.hInstance = hInst;
	UNREFERENCED_PARAMETER(lParam);
	RECT frc;
	int fErr[10]={0};
	char fBuf[250]="";
	int fAi=-1;
	switch (message)
	{
	case WM_CREATE:
		break;
	case uWM_ACREATE:
		GetWindowTextA(hDlg,fBuf,_countof(fBuf));
		if(strcmp(gWNm[1],fBuf)==0)
		{
			GetClientRect(hDlg, &frc);
			hMlr = CreateWindow( "listbox", NULL, WS_CHILD | LBS_NOSEL | WS_VSCROLL | SS_LEFT | WS_BORDER, 0, 0, frc.right, frc.bottom, hDlg, (HMENU) (int)ID_LOG, hInst, NULL );
			if(hMlr == NULL)
				MessageBox(NULL, "Logger ListBox == NULL", "Error!", MB_OK);
			ShowWindow(hMlr,SW_NORMAL);
		}
		break;
	case WM_SIZE:
		GetWindowTextA(hDlg,fBuf,_countof(fBuf));
		if(strcmp(gWNm[1],fBuf)==0){
			if(hMlr)
				MoveWindow(hMlr, 0, 0, (DWORD)LOWORD(lParam), (DWORD)HIWORD(lParam), true);
		}
		break;
	case WM_DESTROY:
		GetWindowTextA(hDlg,fBuf,_countof(fBuf));
		for(int i=0;i<gWnd;i++){
			if(strcmp(fBuf,gWNm[i])==0){fAi=i;break;}
		}
		if(fAi < 1){
			MessageBox(hDlg, "HWND not found.", "Error!", MB_OK);
			return 0;
		}
		if(fAi > 0)
			mhWnd[fAi]=NULL;
		if(fAi == 1)
			hMlr=NULL;
		SwitchToThisWindow(mhWnd[0],1);
		break;
	default:
		return DefWindowProc(hDlg, message, wParam, lParam);
	}
	return 0;
}
void LogMessage(char *fStr)
{
	if(hMlr != NULL)
	{
		if(gLnm > 10000)
		{
			gLnm=1;
			SendMessage(hMlr, CB_RESETCONTENT, 0, 0);
			SendMessage(hMlr, LB_ADDSTRING, 0, (LPARAM)"-Reset Content");
		}
		SendMessage(hMlr, LB_ADDSTRING, 0, (LPARAM)fStr);
		SendMessage(hMlr, WM_VSCROLL, SB_BOTTOM, NULL);
		gLnm++;
	}
}
//Custom Procedures
LRESULT CALLBACK CustomProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	HBRUSH hBrush1, hBrush2, hDefBrush;
	HPEN hPen1, hDefPen;
	HFONT hFont1, hDefFont;
	PAINTSTRUCT ps;
	RECT rect;
	HDC hdc;
	char fBuf[100]="";
	switch(msg)
	{
	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);
		GetClientRect(hwnd, &rect);
		hBrush1 = CreateSolidBrush(RGB(250, 250, 200));
		hBrush2 = CreateSolidBrush(RGB(250, 210, 210));
		hPen1 = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
		hDefPen = (HPEN)SelectObject(hdc, hPen1);
		hFont1 = CreateFont(17, 0, 0, 0, FW_MEDIUM, 0, 0, 0, 0, 0, 0, 0, 0, "Tahoma");
		hDefFont = (HFONT)SelectObject(hdc, hFont1);
		SelectObject(hdc, hBrush1);
		Rectangle(hdc, 0, 0, rect.right, rect.bottom);
		hDefBrush = (HBRUSH)SelectObject(hdc, hBrush2);
		Rectangle(hdc, 2, 2, rect.right-2, rect.bottom-2);
		SelectObject(hdc, hDefPen);
		SetBkMode(hdc, TRANSPARENT);
		SetTextColor(hdc, RGB(0, 0, 255));
		GetWindowText(hwnd, fBuf, _countof(fBuf));
		DrawText(hdc, fBuf, strlen(fBuf), &rect, DT_CENTER | DT_VCENTER);
		SelectObject(hdc, hDefBrush);
		DeleteObject(hBrush1);
		DeleteObject(hBrush2);
		DeleteObject(hPen1);
		SelectObject(hdc, hDefFont);
		DeleteObject(hFont1);
		EndPaint(hwnd, &ps);
		break;
	}
	return DefWindowProc(hwnd, msg, wParam, lParam);
}
LRESULT CALLBACK ButtonProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	RECT rect;
	HBRUSH hBrush1, hDefBrush;
	HPEN hPen1, hDefPen;
	int fErr[5]={0};
	char fBuf[200]="";
	//Testing Messages
	int fSkip[]={132,512,32};
	for(int i=0;i<sizeof(fSkip);i++){
		if(msg == fSkip[i]){fErr[0]=1;break;}
	}
	if(fErr[0]==0)
	{
		sprintf(fBuf,"[CP]msg=%x",msg);
		LogMessage(fBuf);
	}
	//
	switch (msg)
	{
	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);
		GetClientRect(hwnd, &rect);
		fErr[0]=SendMessage(hwnd, BM_GETSTATE, 0, 0);
		if(fErr[0] == BST_PUSHED)
			hBrush1 = CreateSolidBrush(RGB(255, 255, 184));
		else
			hBrush1 = CreateSolidBrush(RGB(255, 255, 160));
		hDefBrush = (HBRUSH)SelectObject(hdc, hBrush1);
		Rectangle(hdc, 0, 0, rect.right, rect.bottom);
		SelectObject(hdc, hDefBrush);
		DeleteObject(hBrush1);
		EndPaint(hwnd, &ps);
		return 1;
	case WM_CAPTURECHANGED:
		return 1;
	}
	return CallWindowProc(gDef[2], hwnd, msg, wParam, lParam);
}

Буду очень признателен так же за информацию или примеры по другим типам контролов (editbox, combobox, listbox).
*П.С. Если нарушил правила форума о длине кода, или чему либо ещё - прошу прощенья, ибо это моё первое появление на этом форуме, и ещё не вник во все правила.

Последний раз редактировалось ArhiCoder; 04.04.2018 в 05:23.
ArhiCoder вне форума   Ответить с цитированием
Старый 04.04.2018, 13:58   #3
waleri
Профессионал
 
Регистрация: 13.07.2012
Адрес: Нижний Новгород
Сообщений: 5,539
Репутация: 1728
По умолчанию

BM_SETSTATE рисует кнопку сам.

Самый простой выход - вставить обработчкик BM_SETSTATE, который будет вызывать InvalidateRect(hWnd, NULL, FALSE) и дальше вызывать gDef[2].

Самый правильный выход - реализовать весь функционал кнопки самому (те. пересылать все в DefWindowProc).
waleri вне форума   Ответить с цитированием
Старый 04.04.2018, 15:05   #4
ArhiCoder
Пользователь
 
Регистрация: 03.04.2018
Сообщений: 11
Репутация: 10
По умолчанию

Цитата:
Сообщение от waleri Посмотреть сообщение
BM_SETSTATE рисует кнопку сам.
Вот что меня и удивило, что данная команда не вызывает WM_PAINT.
Т.е. по логике ("в идеальном мире"), оно должно же быть. Весьма странно что майкрософт внедрили процедуру рисования таким образом..
Я так понимаю что и для других типов контролов будут такие же логически не очень правильные моменты. А есть ли где либо полное описание того, как в стандарте сделано всё это самими майкрософтами? Т.е. каскад команд и сам код того как это реализовано по дефолту?

Цитата:
Сообщение от waleri Посмотреть сообщение
Самый правильный выход - реализовать весь функционал кнопки самому (те. пересылать все в DefWindowProc).
Не могли бы вы пояснить, что вы имеете ввиду? Т.е. в чём принципиальное отличие того, что я заменю лишь часть кода (отрисовки) от того, что я как понимаю, вы предлагаете полностью заменить все процедуры. (В таком случае не проще ли реализовать с нуля свой CustomControl, и сделать логически правильную цепочку команд)
ArhiCoder вне форума   Ответить с цитированием
Старый 04.04.2018, 15:12   #5
waleri
Профессионал
 
Регистрация: 13.07.2012
Адрес: Нижний Новгород
Сообщений: 5,539
Репутация: 1728
По умолчанию

Цитата:
Сообщение от ArhiCoder Посмотреть сообщение
каскад команд и сам код того как это реализовано по дефолту
Нет, такого нигде нет и быть не должно - это implementation detail. Сегодня сделано так а завтра переделаем иначе. Для кнопок сказано, что BM_SETSTAТЕ меняет состояние а если хотите рисовать сами - BS_OWNERDRAW. Все остальное от лукавого.

Цитата:
Сообщение от ArhiCoder Посмотреть сообщение
не проще ли реализовать с нуля свой CustomControl
Об этом и речь, просто он должен иметь класс "BUTTON" и вести себя как ем... button... Если делаете для своих программ, тогда конечно можно называть как угодно и реализовать любой функционал любым способом.
waleri вне форума   Ответить с цитированием
Старый 04.04.2018, 15:28   #6
ArhiCoder
Пользователь
 
Регистрация: 03.04.2018
Сообщений: 11
Репутация: 10
По умолчанию

Цитата:
Сообщение от waleri Посмотреть сообщение
быть не должно
Весьма странно, учитывая что весь мир всегда ссылается на эталон (даже в области программирования есть стандарты, например HTTP, хоть он и меняется со временем, но он есть и он опубликован), а тут его нет, и он даже не представлен.
Ладно, я понял.. такова политика частного сектора, принудить ни кто не может, поэтому делаем что хотим. =)

Цитата:
Сообщение от waleri Посмотреть сообщение
он должен иметь класс "BUTTON"
Ну тогда это уже не CustomControl, т.к. прийдётся делать через сабклассирование, т.е. то как начал это делать я.
ArhiCoder вне форума   Ответить с цитированием
Ответ

Опции темы

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход

Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
как создать инсталлер этого самого Paint'a. Какие файлы,библиотеки использует Paint? Чтобы я мог закинуть их и создать установщик M1ZL-S Windows 7 31.07.2017 20:23
Если Button 1 и Button 2 нажаты - Button 3 стала активной, как? FleXik Общие вопросы Delphi 25 11.03.2015 14:52
Как привязать компоненту TComboBox к Button, чтобы при нажатии Button, данные из ComboBox вводились в TstringGrid Marta_ Помощь студентам 2 15.10.2011 01:02
Paint Артэс Win Api 4 30.11.2008 03:09
paint ben95 Общие вопросы Delphi 1 30.03.2008 09:27


19:47.


Powered by vBulletin® Version 3.8.8 Beta 2
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.

RusProfile.ru


Справочник российских юридических лиц и организаций.
Проекты отопления, пеллетные котлы, бойлеры, радиаторы
интернет магазин respective.ru