I have string that needs to be drawn inside rectangle.
The problem lies in the fact that sometimes string can be too big to fit inside.
How can I adjust font size so the string can fit inside?
I have read the docs for GDI and found nothing. I still keep searching on the Internet, hoping to find something or to get an idea of my own...
GDI+ is an option too...
The following code is posted in response to comment from user Jonathan Potter:
#include <windows.h>
#include <windowsx.h>
#include <CommCtrl.h>
#include <stdio.h> // swprintf_s()
#include <math.h>
#include <gdiplus.h>
#include <string>
using namespace Gdiplus;
// enable Visual Styles
#pragma comment( linker, "/manifestdependency:\"type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
language='*'\"")
// link with Common Controls library
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "GdiPlus.lib")
//global variables
HINSTANCE hInst;
// main window procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_PAINT:
{
PAINTSTRUCT ps = { 0 };
RECT rcClient = { 0 };
HDC hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rcClient);
int pageWidth = rcClient.right - rcClient.left,
pageHeight = rcClient.bottom - rcClient.top;
HFONT font = NULL, oldFont = NULL;
// target rectangle, text should fit inside
Rectangle(hdc, 0, 0, pageWidth / 4, pageHeight / 10);
SIZE sz;
GetTextExtentPoint32(hdc, L"This is very long string that might not fit into specified rectangle",
lstrlen(L"This is very long string that might not fit into specified rectangle"), &sz);
if (sz.cx > (pageWidth / 4))
{
// get current font
LOGFONT lf;
GetObject(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf);
// scale it
lf.lfHeight = MulDiv(lf.lfHeight, (pageWidth / 4), sz.cx);
font = CreateFontIndirect(&lf);
oldFont = SelectFont(hdc, font);
}
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(255, 0, 0));
// draw text in test rectangle
RECT rcText = { 0 };
rcText.left = 0;
rcText.top = 0;
rcText.right = pageWidth / 4;
rcText.bottom = pageHeight / 10;
DrawTextEx(hdc,
L"This is very long string that might not fit into specified rectangle",
wcslen(L"This is very long string that might not fit into specified rectangle"),
&rcText, DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_NOCLIP, NULL);
if (font != NULL)
{
SelectFont(hdc, oldFont);
DeleteFont(font);
}
EndPaint(hwnd, &ps);
}
return 0L;
case WM_SIZE:
{
InvalidateRect(hwnd, NULL, TRUE);
}
return 0L;
case WM_CLOSE:
::DestroyWindow(hwnd);
return 0L;
case WM_DESTROY:
{
::PostQuitMessage(0);
}
return 0L;
default:
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
// WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow)
{
// store hInstance in global variable for later use
hInst = hInstance;
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
// register main window class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
wc.lpszMenuName = NULL;
wc.lpszClassName = L"Main_Window";
wc.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, L"Window Registration Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// initialize common controls
INITCOMMONCONTROLSEX iccex;
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_LISTVIEW_CLASSES | ICC_STANDARD_CLASSES;
InitCommonControlsEx(&iccex);
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
// create main window
hwnd = CreateWindowEx(0, L"Main_Window", L"Autofit text inside rectangle",
WS_OVERLAPPEDWINDOW, 50, 50, 200, 200, NULL, NULL, hInstance, 0);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
GdiplusShutdown(gdiplusToken);
return Msg.wParam;
}
You're looking for DrawText:
int DrawText(_In_ HDC hDC,
_Inout_ LPCTSTR lpchText,
_In_ int nCount,
_Inout_ LPRECT lpRect,
_In_ UINT uFormat
);
You specify the rectangle, and it ensures the text does not get drawn outside of that rectangle. It also has a DT_CALCRECT flag if you need to calculate the rectangle based on the text and the current selected font. Or you can use the DT_END_ELLIPSIS, DT_PATH_ELLIPSIS or DT_WORD_ELLIPSIS flag to truncate the drawing of the text with ellipsis added so the user can see when the text is longer than the rectangle.
In theory, something like this should work, but I haven't tested it. Add appropriate error checking etc.
SIZE sz;
GetTextExtentPoint32(hDC, pszMyString, lstrlen(pszMyString), &sz);
if (sz.cx > iMyMaximumWidth)
{
// get current font
LOGFONT lf;
GetObject(GetCurrentObject(hDC, OBJ_FONT), sizeof(lf), &lf);
// scale it
lf.lfHeight = MulDiv(lf.lfHeight, iMyMaximumWidth, sz.cx);
HFONT hNewFont = CreateFontIndirect(&lf);
.. use hNewFont to render string, remember to delete it when done
}
Related
I am trying to create a DDB Grayscale Bitmap using 24BitRGB format by C++. But I always get a bitmap with black block at bottom. It will be very appreciated if someone can help me on this issue. Many thanks.
The bitmap is : 1024x408 widthxheight
Below is my code:
enter code here
#include "stdafx.h"
#include "Win32Project19.h"
#include <windows.h>
#include <wingdi.h>
#include <algorithm>
#define MAX_LOADSTRING 100
// 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此放置代码。
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_WIN32PROJECT19, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32PROJECT19));
MSG msg;
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECT19));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WIN32PROJECT19);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目的: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HBITMAP hBitmap;
HDC hdcMem, hdc;
static BYTE* lpBits = new BYTE[1024*3*408];
static BYTE* p;
static int cxClient, cyClient;
int i = 0;
int c = 0;
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_CREATE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
for (int y = 0; y < 408; y++)
{
for (int x = 0; x < 1024; x++)
{
lpBits[c + 0] = i;
lpBits[c + 1] = i;
lpBits[c + 2] = i;
c += 3;
}
i++;
if (i > 255)
i = 0;
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
hdc = BeginPaint(hWnd, &ps);
RECT rect;
// TODO: Please add code here....
hBitmap = CreateCompatibleBitmap(hdc, 1024, 408);
SetBitmapBits(hBitmap, 1024 * 3 * 408, lpBits);
GetClientRect(hWnd, &rect);
hdcMem = CreateCompatibleDC(hdc);
SelectObject(hdcMem, hBitmap);
StretchBlt(hdc, rect.left, rect.top, rect.right, rect.bottom, hdcMem, 0, 0, 1024,
408, SRCCOPY);
DeleteDC(hdcMem);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
DeleteObject(hBitmap);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
I always get below results (always a black block in the bottom):
You are assuming that bitmap uses 3 bytes per pixel, but it appears that you need 4 (one for alpha channel).
I got your code to work by simply replacing all 3 with 4:
static BYTE* lpBits = new BYTE[WIDTH * 4 * HEIGHT];
...
c += 4;
...
SetBitmapBits(hBitmap, WIDTH * 4 * HEIGHT, lpBits);
Also, you create your hBitmap for each WM_PAINT message you get, but delete only once, in WM_DESTROY. That causes a resource leak, that may lead to other issues. Since hBitmap is static, you can create it like this:
if (!hBitmap) {
hBitmap = CreateCompatibleBitmap(hdc, WIDTH, HEIGHT);
SetBitmapBits(hBitmap, WIDTH * 4 * HEIGHT, lpBits);
}
I am writing an app in C++ with the Win32 API. I first noticed that the rendered text with DrawTextLayout() was very fuzzy, so I added the following line:
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
At first, this seemed to do the trick: the text looked much crisper, and more of what I would expect from a Windows app.
However, when I resized the window (by dragging the bottom corner), I noticed that the text I drew was distorted.
How could I keep the crispness of using DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, but also keep the text from becoming stretched?
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <dwrite.h>
#include <d2d1.h>
#define IDS_APP_TITLE 103
#define IDR_MAINFRAME 128
#define IDD_PRACTICE_DIALOG 102
#define IDD_ABOUTBOX 103
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDI_PRACTICE 107
#define IDI_SMALL 108
#define IDC_PRACTICE 109
#define IDC_MYICON 2
#ifndef IDC_STATIC
#define IDC_STATIC -1
#endif
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
ID2D1Factory* m_pD2DFactory;
ID2D1HwndRenderTarget* m_pRenderTarget;
ID2D1SolidColorBrush* m_pBlackBrush;
IDWriteFactory* writeFactory;
IDWriteTextFormat* writeTextFormat;
IDWriteTextLayout* writeTextLayout;
RECT rc;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PRACTICE));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_PRACTICE);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr);
//create device independent resources
{
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
// Create a Direct2D factory.
D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
&m_pD2DFactory
);
DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown**>(&writeFactory)
);
}
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_PRACTICE, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance(hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_PRACTICE));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
//create device dependent resources
{
HRESULT hr = S_OK;
if (!m_pRenderTarget) {
GetClientRect(hWnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
// Create a Direct2D render target
hr = m_pD2DFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(hWnd, size), &m_pRenderTarget);
if (SUCCEEDED(hr))
{
// Create a black brush
hr = m_pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &m_pBlackBrush);
}
}
}
//render text
{
writeFactory->CreateTextFormat(
L"Times New Roman",
NULL,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
14.0f,
L"EN-US",
&writeTextFormat
);
writeFactory->CreateTextLayout(
L"String", // The string to be laid out and formatted.
6, // The length of the string.
writeTextFormat, // The text format to apply to the string (contains font information, etc).
200, // The length of the layout box.
500, // The width of the layout box.
&writeTextLayout // The IDWriteTextLayout interface pointer.
);
}
m_pRenderTarget->BeginDraw();
m_pRenderTarget->SetTransform(D2D1::IdentityMatrix());
m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
m_pRenderTarget->DrawTextLayout(D2D1::Point2F(0, 0), writeTextLayout, m_pBlackBrush);
m_pRenderTarget->EndDraw();
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
I apologize that this is a substantial amount of code; however, to be able to be completely run, I had to include this much.
Although setting dpi-awareness is a critical step to avoid bluriness on high-res monitors (when dpi is not 100%), your issue with the text getting stretched on a window resize is a separate issue.
This is simply a DirectX (DirectWrite) surface issue. I only have limited experience with DirectWrite, but the fix for this is very obvious. Like D3D, the DirectWrite surface stretches across the size of the window, but it's not directly aware of the window size change until you tell it.
Potentially two very easy fixes:
In your WndProc function, pass the new size to the m_pRenderTarget thing when the size changes. Catch WM_SIZE:
case WM_SIZE:
{
if (m_pRenderTarget)
{
GetClientRect(hWnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
m_pRenderTarget->Resize(size);
}
break;
}
Alternatively, since your WM_PAINT code would re-create the thing anyway if it's null:
case WM_SIZE:
{
if (m_pRenderTarget)
{
m_pRenderTarget->Release();
m_pRenderTarget = nullptr;
}
break;
}
One other minor fix. Invoke SetProcessDpiAwarenessContext before you call CreateWindow. Very early in WinMain before you start doing anything else. Or use the application manifest file to set it.
I want to explore C++ GUI coding.
I downloaded the example below and wanted to modify the output highlighted with: "<---------------". I know its a very stupid question but my goal is to learn the concept of GUI with C++ by doing.
What and why I have to do in order to add the second line "Test. 3, 2, 1.". My approach didn't work and only results the first line.
// HelloWindowsDesktop.cpp
// compile with: /D_UNICODE /DUNICODE /DWIN32 /D_WINDOWS /c
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
// Global variables
// The main window class name.
static TCHAR szWindowClass[] = _T("DesktopApp");
// The string that appears in the application's title bar.
static TCHAR szTitle[] = _T("Test Title");
HINSTANCE hInst;
// Forward declarations of functions included in this code module:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int CALLBACK WinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow
)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);
if (!RegisterClassEx(&wcex))
{
MessageBox(NULL,
_T("Call to RegisterClassEx failed!"),
_T("Test Program 00"),
NULL);
return 1;
}
// Store instance handle in our global variable
hInst = hInstance;
// The parameters to CreateWindow explained:
// szWindowClass: the name of the application
// szTitle: the text that appears in the title bar
// WS_OVERLAPPEDWINDOW: the type of window to create
// CW_USEDEFAULT, CW_USEDEFAULT: initial position (x, y)
// 500, 100: initial size (width, length)
// NULL: the parent of this window
// NULL: this application does not have a menu bar
// hInstance: the first parameter from WinMain
// NULL: not used in this application
HWND hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
500, 100,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd)
{
MessageBox(NULL,
_T("Call to CreateWindow failed!"),
_T("Test Program 01"),
NULL);
return 1;
}
// The parameters to ShowWindow explained:
// hWnd: the value returned from CreateWindow
// nCmdShow: the fourth parameter from WinMain
ShowWindow(hWnd,
nCmdShow);
UpdateWindow(hWnd);
// Main message loop:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
TCHAR greeting[] = _T("Test. 1, 2, 3.");
PAINTSTRUCT ps1;
HDC hdc1;
TCHAR greeting1[] = _T("Test. 3, 2, 1.");
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// Here your application is laid out.
// For this introduction, we just print out "Hello, Windows desktop!" <-----------------------------------------------
// in the top left corner.
TextOut(hdc,
5, 5,
greeting, _tcslen(greeting));
EndPaint(hWnd, &ps);
// CAN ONLY PAINT ONE???
hdc1 = BeginPaint(hWnd, &ps1);
TextOut(hdc1,
6, 6,
greeting1, _tcslen(greeting1));
// End application-specific layout section.
EndPaint(hWnd, &ps1);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
I have two icons (.ico files). A big 32x32 one and another small 16x16.
I'm trying to set the hIcon of my WNDCLASSEX to be the big one, and hIconSm to the smaller one. Yet I cannot for the life of me figure out how to do that! I first tried LoadIcon:
wndclass.hIcon = LoadIcon(instance, MAKEINTRESOURCE(IDI_SKELETON));
wndclass.hIconSm = LoadIcon(instance, MAKEINTRESOURCE(IDI_SKELETON_SM));
It always loads the same icon for both the top window bar and the taskbar. Same thing with LoadImage.
Here's all the codes:
Resource.h
#define IDI_SKELETON 1000
#define IDI_SKELETON_SM 1001
Skeleton.rc
#include "Resource.h"
IDI_SKELETON ICON "Skeleton.ico"
IDI_SKELETON_SM ICON "Skeleton_sm.ico"
WinMain.cpp
#include <windows.h>
#include "Resource.h"
LRESULT CALLBACK
HandleEvent(HWND window,
UINT message,
WPARAM wparam,
LPARAM lparam)
{
switch(message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC dc;
RECT rect;
dc = BeginPaint(window, &ps);
GetClientRect(window, &rect);
DrawText(dc, TEXT("This is a test window"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(window, &ps);
}
break;
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
break;
}
return DefWindowProc(window, message, wparam, lparam);
}
int CALLBACK
WinMain(HINSTANCE instance,
HINSTANCE previous,
LPSTR cmd,
int cmdshow)
{
WNDCLASSEX wndclass;
TCHAR classname[] = TEXT("24HoursClass");
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_VREDRAW | CS_HREDRAW;
wndclass.lpfnWndProc = HandleEvent;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = instance;
wndclass.hIcon = (HICON)LoadImage(instance, MAKEINTRESOURCE(IDI_SKELETON), IMAGE_ICON,
GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), 0);
wndclass.hIconSm = (HICON)LoadImage(instance, MAKEINTRESOURCE(IDI_SKELETON_SM), IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszMenuName = 0;
wndclass.lpszClassName = classname;
RegisterClassEx(&wndclass);
HWND window = CreateWindowA(classname, "24 Hours",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
0, 0, instance, 0);
MSG msg;
while(GetMessage(&msg, window, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
What am I missing?
Any help is appreciated!
I created a simple form to test in winapi: I uploaded here: http://pastebin.com/7dNjE1Tb
I would like to put a simple png file to my hwnd, for example this picture: http://www.ledavi-network.com/includes/images/bg_shadow_png.png
I know I should use this: http://msdn.microsoft.com/en-us/library/windows/desktop/dd145141(v=vs.85).aspx but I am very new in winapi and I dont find any example how to use this TransparentBlt function.
Someone could help me to create a very simple example?
EDIT:
But what I would like to do?
I would like to create a button which has special background with shadow. So If anyone know an easier way to do this, I will forget to use the TransparentBlt. So any else solution?
Here, this will do the job. You'll need to ensure you have gdiplus and msimg32 libraries linked for the AlphaBlend (TransparentBlt assumes that a single rgb color in the image will be treated as transparent. I.e it's just 1-bit transparency when using TransparentBlt - fully opaque or fully transparent.)
You'll (presumably) wish to use AlphaBlend for this job.
Note - I haven't bothered to add an WM_ERASEBKGND to the WndProc function. As a consequence, each time the image is re-drawn, it's drawn straight over the top of what's already there (try resizing the window). Just do a FillRect in erasebkg, and you'll be fine.
EDIT: Code updated to use displayImage with a NULL hbitmap to do the background too.
Here 'go:
#define WIN32_LEAN_AND_MEAN
#define WINVER 0x0600 // needed for alphablend function..
#include <windows.h>
#include <gdiplus.h>
const char g_szClassName[] = "myWindowClass";
// BMP, GIF, JPEG, PNG, TIFF, Exif, WMF, and EMF
// requires GDIPlus
HBITMAP mLoadImg(WCHAR *szFilename)
{
HBITMAP result=NULL;
Gdiplus::Bitmap* bitmap = new Gdiplus::Bitmap(szFilename,false);
bitmap->GetHBITMAP(NULL, &result);
delete bitmap;
return result;
}
//void CStaticImg::displayImage(HBITMAP mBmp, HWND mHwnd)
void displayImage(HBITMAP mBmp, HWND mHwnd)
{
RECT myRect;
BITMAP bm;
HDC screenDC, memDC;
HBITMAP oldBmp;
BLENDFUNCTION bf;
GetObject(mBmp, sizeof(bm), &bm);
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 0xff;
bf.AlphaFormat = AC_SRC_ALPHA;
screenDC = GetDC(mHwnd);
GetClientRect(mHwnd, &myRect);
if (mBmp == NULL)
FillRect(screenDC, &myRect, WHITE_BRUSH);
else
{
memDC = CreateCompatibleDC(screenDC);
oldBmp = (HBITMAP)SelectObject(memDC, mBmp);
AlphaBlend (screenDC, 0, 0, myRect.right,myRect.bottom, memDC, 0, 0, bm.bmWidth,bm.bmHeight, bf);
SelectObject(memDC, oldBmp);
DeleteDC(memDC);
ReleaseDC(mHwnd, screenDC);
}
}
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HBITMAP myImage;
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_ERASEBKGND:
displayImage(NULL, hwnd);
break;
case WM_CREATE:
myImage = mLoadImg(L"bg_shadow_png.png");
break;
case WM_PAINT:
//displayImage(HBITMAP mBmp, HWND mHwnd);
displayImage(myImage, hwnd);
ValidateRect(hwnd, NULL);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
static Gdiplus::GdiplusStartupInput gdiplusStartupInput;
static ULONG_PTR gdiplusToken;
// so we can load all the image formats that windows supports natively - (I'm using a transparent PNG on the main dialog)
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"The title of my window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Step 3: The Message Loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
Gdiplus::GdiplusShutdown(gdiplusToken);
return Msg.wParam;
}