Eigene Controls basteln



  • Hi, was muss ich tun um mit der Winapi eigene Controls zu basteln, wie z.b. Ein eigener Button oder eine Laufschrift?
    THX!



  • eigerner Button:

    // Die Windows-API einbinden
    #include <windows.h>
    
    // Eine Struktur für alle relevanten Daten
    typedef struct __s_button_info
    {
    	wchar_t szText[1024];  // Text
    	bool bMouseOver;         // Ist die Maus drüber?
    	bool bFocus;                // Hast das Control den Fokus?
    	bool bPressed;              // Wird der Button gerade gedrückt?
    	UINT uiID;                    // Die ID des Controls
    } SButtonInfo, *PButtonInfo;
    
    // Die Farben, die der Button verwendet
    const COLORREF cDarkGray = RGB(0x69, 0x69, 0x69);
    const COLORREF cLightGray = RGB(0xD3, 0xD3, 0xD3);
    const COLORREF cGray = RGB(0xA9, 0xA9, 0xA9);
    const COLORREF cVeryLightGray = RGB(0xEE, 0xEE, 0xEE);
    const COLORREF cBlue = RGB(0x00, 0x00, 0xFF);
    
    // Zum registrieren der Fensterklasse
    void InitWindowClass(HINSTANCE hInstance)
    {
    	WNDCLASSEX wce;
    	wce.cbClsExtra = 0;
    	wce.cbSize = sizeof(WNDCLASSEX);
    	wce.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
    	wce.hCursor = LoadCursor(NULL, IDC_ARROW);
    	wce.hIcon = wce.hIconSm = NULL;
    	wce.hInstance = hInstance;
    	wce.lpszMenuName = NULL;
    	wce.style = CS_HREDRAW|CS_VREDRAW;
    	wce.cbWndExtra = sizeof(PButtonInfo);
    	wce.lpfnWndProc = ButtonProc;
    	wce.lpszClassName = L"WINTEST_BUTTON";
    	RegisterClassEx(&wce);
    }
    
    // Hilfsfunktionen für ein flackerfreies Control
    BOOL CALLBACK ChildEnumProc(HWND hWnd, LPARAM lParam)
    {
    	InvalidateRect(hWnd, NULL, TRUE);
    	return TRUE;
    }
    
    HDC BeginScene(HWND hWnd, HBITMAP* pBitmap)
    {
    	RECT r = { 0 };
    	GetClientRect(hWnd, &r);
    	r.right -= r.left;
    	r.bottom -= r.top;
    	HDC hWindowDC = GetDC(hWnd);
    	HDC hDoubleBufferDC = CreateCompatibleDC(hWindowDC);
    	*pBitmap = CreateCompatibleBitmap(hWindowDC, r.right, r.bottom);
    	SelectObject(hDoubleBufferDC, (*pBitmap));
    	ReleaseDC(hWnd, hWindowDC);
    	wchar_t szClassName[1024];
    	GetClassName(hWnd, szClassName, 1024);
    	WNDCLASSEX wce = { 0 };
    	GetClassInfoEx(GetModuleHandle(NULL), szClassName, &wce);
    	FillRect(hDoubleBufferDC, &r, wce.hbrBackground);
    	return hDoubleBufferDC;
    }
    
    void EndScene(HWND hWnd, HDC hDoubleBufferDC, HBITMAP hBitmap)
    {
    	RECT r = { 0 };
    	GetUpdateRect(hWnd, &r, FALSE);
    	if ((r.bottom == 0) && (r.left == 0) && (r.right == 0) && (r.top == 0))
    		GetClientRect(hWnd, &r);
    	PAINTSTRUCT ps = { 0 };
    	HDC hWindowDC = BeginPaint(hWnd, &ps);
    	StretchBlt(hWindowDC, r.left, r.top, r.right, r.bottom, hDoubleBufferDC, r.left, r.top, r.right, r.bottom, SRCCOPY);
    	EndPaint(hWnd, &ps);
    	DeleteObject(hBitmap);
    	DeleteDC(hDoubleBufferDC);
    	ValidateRect(hWnd, &r);
    	EnumChildWindows(hWnd, ChildEnumProc, 0);
    }
    
    // Eine Linie Zeichnen
    void DrawLine(int x, int y, int cx, int cy, HDC hDC)
    {
    	MoveToEx(hDC, x, y, NULL);
    	LineTo(hDC, cx, cy);
    }
    
    // Überprüfen, ob ein Punkt innerhalb eines Rechtecks ist
    bool IsInRect(LPPOINT pPoint, LPRECT pRect)
    {
    	return ((pPoint->x <= pRect->right) && (pPoint->x >= pRect->left) && (pPoint->y <= pRect->bottom) && (pPoint->y >= pRect->top));
    }
    
    // und zu guter letzt: die Fensterprozedur des Buttons:
    LRESULT CALLBACK ButtonProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    	switch (msg)
    	{
    	case WM_CREATE:
    		{
    			LPCREATESTRUCT pCS = reinterpret_cast<LPCREATESTRUCT>(lParam);
    			PButtonInfo pInfo = new SButtonInfo;
    			wsprintf(pInfo->szText, L"%s", pCS->lpszName);
    			POINT CursorPos;
    			GetCursorPos(&CursorPos);
    			POINT pUpperLeft = { 0, 0 };
    			ClientToScreen(hWnd, &pUpperLeft);
    			POINT pLowerRight = { pCS->cx, pCS->cy };
    			ClientToScreen(hWnd, &pLowerRight);
    			RECT WindowRect = { pUpperLeft.x, pUpperLeft.y, pLowerRight.x, pLowerRight.y };
    			pInfo->bMouseOver = IsInRect(&CursorPos, &WindowRect);
    			pInfo->bFocus = false;
    			pInfo->bPressed = false;
    #pragma warning(disable: 4311)
    			pInfo->uiID = reinterpret_cast<UINT>(pCS->hMenu);
    			SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG>(pInfo));
    #pragma warning(default: 4311)
    			if (pCS->style & WS_DEFAULT)
    				SetFocus(hWnd);
    			return 0;
    		} break;
    	case WM_PAINT:
    		{
    #pragma warning(disable: 4312)
    			PButtonInfo pInfo = reinterpret_cast<PButtonInfo>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    #pragma warning(default: 4312)
    			RECT r = { 0 };
    			GetClientRect(hWnd, &r);
    			r.right -= r.left;
    			r.bottom -= r.top;
    			HBITMAP hBmp;
    			HDC hDC = BeginScene(hWnd, &hBmp);
    			HPEN hUpperLeftBorder, hLowerRightBorder;
    			HBRUSH hBackground;
    			if (!(pInfo->bPressed))
    			{
    				hUpperLeftBorder = CreatePen(PS_SOLID, 3, cLightGray);
    				hLowerRightBorder = CreatePen(PS_SOLID, 3, cDarkGray);
    			}
    			if (pInfo->bPressed)
    			{
    				hUpperLeftBorder = CreatePen(PS_SOLID, 3, cDarkGray);
    				hLowerRightBorder = CreatePen(PS_SOLID, 3, cLightGray);
    			}
    			hBackground = ((pInfo->bMouseOver) ? (CreateSolidBrush(cVeryLightGray)) : (CreateSolidBrush(cGray)));
    			FillRect(hDC, &r, hBackground);
    			COLORREF cOldColor = SetBkColor(hDC, ((pInfo->bMouseOver) ? (cVeryLightGray) : (cGray)));
    			COLORREF cOldFore = SetTextColor(hDC, ((pInfo->bFocus) ? (cBlue) : (0)));
    			DrawText(hDC, pInfo->szText, -1, &r, DT_VCENTER|DT_SINGLELINE|DT_CENTER);
    			SetTextColor(hDC, cOldFore);
    			SetBkColor(hDC, cOldColor);
    			HPEN hOldPen = reinterpret_cast<HPEN>(SelectObject(hDC, hLowerRightBorder));
    			DrawLine(r.right - 2, r.top + 1, r.right - 2, r.bottom - 2, hDC);
    			DrawLine(r.right - 2, r.bottom - 2, r.left + 1, r.bottom - 2, hDC);
    			DeleteObject(SelectObject(hDC, hUpperLeftBorder));
    			DrawLine(r.left + 1, r.bottom - 2, r.left + 1, r.top + 1, hDC);
    			DrawLine(r.left + 1, r.top + 1, r.right - 2, r.top + 1, hDC);
    			DeleteObject(SelectObject(hDC, hOldPen));
    			EndScene(hWnd, hDC, hBmp);
    			return 0;
    		} break;
    	case WM_ERASEBKGND:
    		{
    			return 1;
    		} break;
    	case WM_SETFOCUS:
    		{
    #pragma warning(disable: 4312)
    			PButtonInfo pInfo = reinterpret_cast<PButtonInfo>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    #pragma warning(default: 4312)
    			pInfo->bFocus = true;
    			RedrawWindow(hWnd, NULL, NULL, RDW_ERASE|RDW_FRAME|RDW_INTERNALPAINT|RDW_INVALIDATE);
    			return 0;
    		} break;
    	case WM_KILLFOCUS:
    		{
    #pragma warning(disable: 4312)
    			PButtonInfo pInfo = reinterpret_cast<PButtonInfo>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    #pragma warning(default: 4312)
    			pInfo->bFocus = false;
    			RedrawWindow(hWnd, NULL, NULL, RDW_ERASE|RDW_FRAME|RDW_INTERNALPAINT|RDW_INVALIDATE);
    			return 0;
    		} break;
    	case WM_DESTROY:
    		{
    #pragma warning(disable: 4312)
    			PButtonInfo pInfo = reinterpret_cast<PButtonInfo>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    #pragma warning(default: 4312)
    			delete pInfo;
    			return 0;
    		} break;
    	case WM_MOUSEMOVE:
    		{
    #pragma warning(disable: 4312)
    			PButtonInfo pInfo = reinterpret_cast<PButtonInfo>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    #pragma warning(default: 4312)
    			POINT CursorPos = { LOWORD(lParam), HIWORD(lParam) };
    			RECT WindowRect;
    			GetWindowRect(hWnd, &WindowRect);
    			WindowRect.bottom -= WindowRect.top;
    			WindowRect.right -= WindowRect.left;
    			WindowRect.left = WindowRect.top = 0;
    			if (IsInRect(&CursorPos, &WindowRect))
    			{
    				SetCapture(hWnd);
    				pInfo->bMouseOver = true;
    			}
    			else
    			{
    				pInfo->bMouseOver = false;
    				if (pInfo->bPressed == false)
    				{
    					ReleaseCapture();
    				}
    			}
    			RedrawWindow(hWnd, NULL, NULL, RDW_ERASE|RDW_FRAME|RDW_INTERNALPAINT|RDW_INVALIDATE);
    			return 0;
    		} break;
    	case WM_SETTEXT:
    		{
    #pragma warning(disable: 4312)
    			PButtonInfo pInfo = reinterpret_cast<PButtonInfo>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    #pragma warning(default: 4312)
    			wsprintf(pInfo->szText, L"%s", lParam);
    			RedrawWindow(hWnd, NULL, NULL, RDW_ERASE|RDW_FRAME|RDW_INTERNALPAINT|RDW_INVALIDATE);
    		} break;
    	case WM_LBUTTONDOWN:
    		{
    #pragma warning(disable: 4312)
    			PButtonInfo pInfo = reinterpret_cast<PButtonInfo>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    #pragma warning(default: 4312)
    			pInfo->bPressed = true;
    			SetFocus(hWnd);
    			RedrawWindow(hWnd, NULL, NULL, RDW_ERASE|RDW_FRAME|RDW_INTERNALPAINT|RDW_INVALIDATE);
    			return 0;
    		} break;
    	case WM_LBUTTONUP:
    		{
    #pragma warning(disable: 4312)
    			PButtonInfo pInfo = reinterpret_cast<PButtonInfo>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    #pragma warning(default: 4312)
    			pInfo->bPressed = false;
    			POINT CursorPos = { LOWORD(lParam), HIWORD(lParam) };
    			RECT WindowRect;
    			GetWindowRect(hWnd, &WindowRect);
    			WindowRect.bottom -= WindowRect.top;
    			WindowRect.right -= WindowRect.left;
    			WindowRect.left = WindowRect.top = 0;
    			if (IsInRect(&CursorPos, &WindowRect))
    			{
    				pInfo->bMouseOver = false;
    				ReleaseCapture();
    				SendMessage(GetParent(hWnd), WM_COMMAND, MAKEWPARAM(pInfo->uiID, BN_CLICKED), reinterpret_cast<LPARAM>(hWnd));
    			}
    			else
    			{
    				ReleaseCapture();
    			}
    			RedrawWindow(hWnd, NULL, NULL, RDW_ERASE|RDW_FRAME|RDW_INTERNALPAINT|RDW_INVALIDATE);
    			return 0;
    		} break;
    	case WM_KEYDOWN:
    		{
    			switch (wParam)
    			{
    			case VK_SPACE:
    			case VK_RETURN:
    				{
    #pragma warning(disable: 4312)
    					PButtonInfo pInfo = reinterpret_cast<PButtonInfo>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    #pragma warning(default: 4312)
    					SendMessage(GetParent(hWnd), WM_COMMAND, MAKEWPARAM(pInfo->uiID, BN_CLICKED), reinterpret_cast<LPARAM>(hWnd));
    				} break;
    			case VK_TAB:
    				{
    					HWND hStart = hWnd;
    					HWND hSearch = FindWindowEx(GetParent(hWnd), hStart, NULL, NULL);
    					while (hSearch != hWnd)
    					{
    						if (GetWindowLong(hSearch, GWL_STYLE) & WS_TABSTOP)
    						{
    							SetFocus(hSearch);
    							return 0;
    						}
    						hStart = hSearch;
    						if (!IsWindow(hSearch))
    							hStart = NULL;
    						hSearch = FindWindowEx(GetParent(hWnd), hStart, NULL, NULL);
    					}
    				} break;
    			}
    			return 0;
    		} break;
    	}
    	return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    


  • Super, danke!
    Hab aber gar nit gewusst, dass das so viel Arbeits is.



  • kommt drauf an, ob du alles komplett schreibst...

    wenn du nur das Aussehen von Standard-Controls ändern willst, hilft oft auch OwnerDrawn oder SubClassing.
    Willst du komplett was eigenes, dann wird das schon ein ganz schönes Stück Code.
    dabei ist ein Button noch so im Mittelfeld.
    Am einfachsten sind wohl einfache Statics, oder Groupboxs oder so, wo nur was angezeigt wird.
    Am schwierigsten sind dann wahrscheinlich eigene Edit-Controls.



  • googlemail schrieb:

    Super, danke!
    Hab aber gar nit gewusst, dass das so viel Arbeits is.

    WinAPI ist halt noch viel low-level Arbeit. Daher macht es auch Spass, wenn man die Zeit dafür findet. 😃


Log in to reply