I have this odd behavior where my code was unable to call a event handler when i compile and run using JNI but it worked if i compile it as .exe in VS 2013.
The ones not working are listed under case ID_KRUSKAL_IMPERFECT, case ID_KRUSKAL_PERFECT, case ID_PRIM_IMPERFECT, case ID_PRIM_PERFECT. This cases are menu's (see image below for illustration ). By right it is suppose to pop up another dialog box
However, when i clicked case ID_FILE_EXIT the window closed (so it worked for this button)
Source code as attached :
#include <windows.h>
#include <iostream>
#include <string>
#include <fstream>
//#include "resource.h"
#include "maze.h"
#include <sstream>
#include <vector>
#include "HelloWorld.h"
#include "win_main.h"
#include "KruskalAlgo.h"
#include "PrimAlgo.h"
//JNI
#include <jni.h>
#include <stdio.h>
#include "Window.h"
//win32 GUI ID
#define IDR_MENU1 102
#define IDI_ICON1 103
#define IDD_DIALOG1 105
#define IDC_EDIT1 1005
#define IDC_WIDTH 1005
#define IDC_EDIT2 1006
#define IDC_EDIT3 1007
#define IDC_WALLBUTTON 1008
#define IDC_PATHBUTTON 1009
#define IDC_EDIT4 1010
#define IDC_HEIGHT 1010
#define ID_STUFF_GO 40001
#define ID_FILE_EXIT 40002
#define ID_FILE_GENERATEMAZE 40003
#define ID_FILE_UPLOADMAZE 40004
#define ID_GENERATEMAZE_KRUSKAL 40005
#define ID_GENERATEMAZE_PRIM 40006
#define ID_GENERATEMAZE_KRUSKAL40007 40007
#define ID_GENERATEMAZE_PRIM40008 40008
#define ID_GENERATEMANUALLY_KRUSKAL 40009
#define ID_GENERATEMANUALLY_PRIM 40010
#define ID_KRUSKAL_PERFECT 40011
#define ID_KRUSKAL_IMPERFECT 40012
#define ID_PRIM_PERFECT 40013
#define ID_PRIM_IMPERFECT 40014
#define ID_GENERATEMAZEAUTO 40015
//class name
LPCSTR g_szClassName = "mazeGenerator";
//2d vector to store 1s and 0s
typedef std::vector<std::vector<int>> IntMatrix;
IntMatrix vector2D;
//function header
void drawMaze(HWND hwnd);
void fileUpload(HWND hwnd);
void chooseColour(HWND hwnd);
//global height
int inRowCount;
//global width
int inColCount;
int disp;
PrimAlgo p;
PrimAlgo pi;
KruskalAlgo k;
KruskalAlgo ki;
INT_PTR CALLBACK ConfigDialog(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
//HWND hWndComboBox;
switch (Message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_WALLBUTTON:
chooseColour(hwnd);
break;
case IDC_PATHBUTTON:
chooseColour(hwnd);
break;
case IDOK:
inColCount = GetDlgItemInt(hwnd, IDC_WIDTH, NULL, FALSE);
inRowCount = GetDlgItemInt(hwnd, IDC_HEIGHT, NULL, FALSE);
EndDialog(hwnd, IDD_DIALOG1);
break;
case IDCANCEL:
EndDialog(hwnd, IDD_DIALOG1);
break;
default:
return FALSE;
}
default:
return FALSE;
}
return TRUE;
}
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND temp;
HINSTANCE htemp;
switch (msg)
{
case WM_CREATE:
{
//initialization
HMENU hMenu, hSubMenu1, hSubMenu2, hSubMenu3, hSubMenu4, hSubMenu5;
HICON hIcon, hIconSm;
//menu
hMenu = CreateMenu();
hSubMenu1 = CreatePopupMenu();
hSubMenu2 = CreatePopupMenu();
hSubMenu3 = CreatePopupMenu();
hSubMenu4 = CreatePopupMenu();
hSubMenu5 = CreatePopupMenu();
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu1, "&File");
AppendMenu(hSubMenu1, MF_STRING | MF_POPUP, (UINT)hSubMenu2, "&New Game");
AppendMenu(hSubMenu2, MF_STRING, ID_GENERATEMAZEAUTO, "&Generate Automatically");
AppendMenu(hSubMenu2, MF_STRING | MF_POPUP, (UINT)hSubMenu3, "&Generate Manually");
AppendMenu(hSubMenu3, MF_STRING | MF_POPUP, (UINT)hSubMenu4, "&Kruskal");
AppendMenu(hSubMenu4, MF_STRING, ID_KRUSKAL_PERFECT, "&Perfect");
AppendMenu(hSubMenu4, MF_STRING, ID_KRUSKAL_IMPERFECT, "&Imperfect");
AppendMenu(hSubMenu3, MF_STRING | MF_POPUP, (UINT)hSubMenu5, "&Prim's");
AppendMenu(hSubMenu5, MF_STRING, ID_KRUSKAL_PERFECT, "&Perfect");
AppendMenu(hSubMenu5, MF_STRING, ID_KRUSKAL_IMPERFECT, "&Imperfect");
AppendMenu(hSubMenu1, MF_STRING, ID_FILE_EXIT, "&Exit");
/*hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO, "&Go");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Stuff");*/
SetMenu(hwnd, hMenu);
hIcon = (HICON)LoadImage(NULL, "logo.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
if (hIcon)
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
else
MessageBox(hwnd, "Could not load large icon!", "Error", MB_OK | MB_ICONERROR);
hIconSm = (HICON)LoadImage(NULL, "logo.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
if (hIconSm)
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
else
MessageBox(hwnd, "Could not load small icon!", "Error", MB_OK | MB_ICONERROR);
}
break;
case WM_PAINT:
{
drawMaze(hwnd);
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_FILE_EXIT:
PostMessage(hwnd, WM_CLOSE, 0, 0);
break;
case ID_GENERATEMAZEAUTO:
fileUpload(hwnd);
InvalidateRect(hwnd, 0, TRUE);
SetWindowPos(hwnd, 0, 0, 0, (inColCount * 10) + 17, (inRowCount * 10) + 60, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
break;
case ID_PRIM_PERFECT:
disp = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), hwnd, ConfigDialog);
p.setDimension(inRowCount, inColCount);
p.setWallPath(0, 1);
p.generateMaze();
vector2D = p.get2DOutput();
InvalidateRect(hwnd, 0, TRUE);
SetWindowPos(hwnd, 0, 0, 0, (inColCount * 10) + 17, (inRowCount * 10) + 60, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
break;
case ID_PRIM_IMPERFECT:
disp = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), hwnd, ConfigDialog);
pi.setDimension(inRowCount, inColCount);
pi.setWallPath(0, 1);
pi.generateMaze();
pi.generateImperfect();
vector2D = pi.get2DOutput();
InvalidateRect(hwnd, 0, TRUE);
SetWindowPos(hwnd, 0, 0, 0, (inColCount * 10) + 17, (inRowCount * 10) + 60, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
break;
case ID_KRUSKAL_PERFECT:
disp = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), hwnd, ConfigDialog);
k.setDimension(inRowCount, inColCount);
k.setWallPath(0, 1);
k.generateMaze();
vector2D = k.get2DOutput();
InvalidateRect(hwnd, 0, TRUE);
SetWindowPos(hwnd, 0, 0, 0, (inColCount * 10) + 17, (inRowCount * 10) + 60, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
break;
case ID_KRUSKAL_IMPERFECT:
disp = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), hwnd, ConfigDialog);
ki.setDimension(inRowCount, inColCount);
ki.setWallPath(0, 1);
ki.generateMaze();
ki.generateImperfect();
vector2D = ki.get2DOutput();
InvalidateRect(hwnd, 0, TRUE);
SetWindowPos(hwnd, 0, 0, 0, (inColCount * 10) + 17, (inRowCount * 10) + 60, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
break;
}
break;
case WM_CLOSE:
{
DestroyWindow(hwnd);
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;
//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.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
wc.lpszClassName = g_szClassName;
wc.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 16, 16, 0);
if (!RegisterClassEx(&wc))
{
MessageBoxA(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_WINDOWEDGE,
g_szClassName,
"Maze Generator 0.1",
WS_OVERLAPPEDWINDOW,
0, 0, 300, 60,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
{
MessageBoxA(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);
}
return Msg.wParam;
}
//draws maze based on vector2D
void chooseColour(HWND hwnd){
CHOOSECOLOR cc; // common dialog box structure
static COLORREF acrCustClr[16]; // array of custom colors
HBRUSH hbrush; // brush handle
static DWORD rgbCurrent; // initial color selection
ZeroMemory(&cc, sizeof(cc));
cc.lStructSize = sizeof(cc);
cc.hwndOwner = hwnd;
cc.lpCustColors = (LPDWORD)acrCustClr;
cc.rgbResult = rgbCurrent;
cc.Flags = CC_FULLOPEN | CC_RGBINIT;
if (ChooseColor(&cc) == TRUE)
{
hbrush = CreateSolidBrush(cc.rgbResult);
rgbCurrent = cc.rgbResult;
}
}
void drawMaze(HWND hwnd){
RECT rcClient;
GetClientRect(hwnd, &rcClient);
PAINTSTRUCT ps;
//starting x and y value for first maze cell
int startRow = 0;
int startCol = 0;
//displacement
int dRow = 10;
int dCol = 10;
//counter
int rowCounter = 0;
int colCounter = 0;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &rcClient, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
for (int rowId = 0; rowId < inRowCount; rowId++){
for (int colId = 0; colId < inColCount; colId++){
if (vector2D[rowId][colId] == 1){
RECT cell = { startCol, startRow, startCol + dCol, startRow + dRow };
FillRect(hdc, &cell, (HBRUSH)GetStockObject(WHITE_BRUSH));
}
if (vector2D[rowId][colId] == 0){
RECT cell = { startCol, startRow, startCol + dCol, startRow + dRow };
FillRect(hdc, &cell, (HBRUSH)GetStockObject(BLACK_BRUSH));
}
startCol += dCol;
colCounter++;
if (colCounter == inColCount)
{
startCol = 0;
colCounter = 0;
}
}
startRow += dRow;
rowCounter++;
if (rowCounter == inRowCount)
{
startRow = 0;
rowCounter = 0;
}
}
EndPaint(hwnd, &ps);
}
void fileUpload(HWND hwnd){
//file upload
OPENFILENAME ofn;
HANDLE hFile;
DWORD dwRead;
char filechar[20000] = "";
int countMaze = 0;
int countValue = 0;
int x = 0;
int y = 0;
int tempx = 0;
int tempy = 0;
char szFileName[MAX_PATH] = "";
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn); // SEE NOTE BELOW
ofn.hwndOwner = hwnd;
ofn.lpstrFilter = (LPCSTR)"Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
ofn.lpstrFile = (LPSTR)szFileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrDefExt = (LPCSTR)"txt";
if (GetOpenFileName(&ofn))
{
// Load the file name for maze drawing
// Load the file name for maze drawing
//MessageBox(hWnd, (LPCWSTR)szFileName, (LPCWSTR)L"Notice",MB_OK | MB_ICONINFORMATION);
hFile = CreateFile((LPCSTR)szFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
ReadFile(hFile, filechar, sizeof(filechar), &dwRead, NULL);
int status = 1;//to track invalid input detected in the readfile
while (dwRead != countMaze)
{
char temp;
//temp in ASCII value (convert to 0 and 1)
temp = *&filechar[countMaze];
if (temp != '\r' && temp != '\n' && temp != '0' && temp != '1')
{
status = 0;
break;
}
if (temp == '\r')//For carriage return case.
{
countMaze = countMaze + 1;
continue;
}
if (temp == '\n')//For newline case.
{
y = dwRead / (x + 2);
break;
}
x = x + 1;
countMaze = countMaze + 1;
}
//return the width and height needed to print maze.
if (status == 1){
inColCount = x;
inRowCount = y;
//resize maze based on x and y values.
vector2D.resize(y);
for (int i = 0; i < y; i++){
vector2D[i].resize(x);
}
while (dwRead > countValue)
{
char temp;
//temp in ASCII value
temp = *&filechar[countValue];
if (temp)
if (temp == '\r')//For carriage return case.
{
countValue++;
continue;
}
if (temp == '\n')//For newline case.
{
countValue++;
tempx++;
tempy = 0;
continue;
}
//minus 48 to convert from ascii value to integer
vector2D[tempx][tempy] = int(temp - 48);
tempy = tempy + 1;
countValue++;
}
}
else{
MessageBox(hwnd, (LPCSTR)"Invalid maze file found ! Re-upload please with text file containing 0 and 1 only please", (LPCSTR)"Notice",
MB_OK | MB_ICONINFORMATION);
}
if (status == 1){
//close hFile handler
CloseHandle(hFile);
}
}
}
// Implementation of native method sayHello() of HelloJNI class
JNIEXPORT void JNICALL Java_Window_load(JNIEnv *env, jobject thisObj) {
WinMain(0,0,0,1);
printf("Hello World!\n");
return;
}
It doesn't work because the hInstance passed to WinMain is NULL in Java_Window_load(). You need to use the HINSTANCE of the DLL or else resources like your menus won't work.
Do something like this:
static HINSTANCE sDllInstance;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
if (fdwReason == DLL_PROCESS_ATTACH) {
sDllInstance = hinstDLL;
}
return TRUE;
}
// Implementation of native method sayHello() of HelloJNI class
JNIEXPORT void JNICALL Java_Window_load(JNIEnv *env, jobject thisObj) {
WinMain(sDllInstance,0,0,1);
printf("Hello World!\n");
return;
}
Related
I am trying to write code where the user is able to choose an image from their computer. I was able to get a piece of code working where the window would update itself when the user choosed an image. So, I went ahead and added some buttons to the screen and now, the image wont even load onto the screen. I'm confused as to why. It's not the image size because I chose a small image to load in as well.
Here is my function for putting the image up:
void putImage(HDC hdc, HWND hWnd)
{
Graphics graphic(hdc);
Image* image = Image::FromFile(filePath);
Status status = graphic.DrawImage(image, 10, 20);
RECT updateRect = { 0 };
updateRect.left = 10;
updateRect.top = 10;
updateRect.right = updateRect.left + image->GetWidth();
updateRect.bottom = updateRect.top + image->GetHeight();
RedrawWindow(hWnd, &updateRect, NULL, RDW_INVALIDATE);
}
And here's my paint:
case WM_PAINT: {
PAINTSTRUCT ps;
HDC screen = BeginPaint(hWnd, &ps);
HPAINTBUFFER hbuff = BeginBufferedPaint(ps.hdc, &ps.rcPaint, BPBF_COMPATIBLEBITMAP, NULL, &screen);
if (hbuff)
{
RECT rc;
GetClientRect(hWnd, &rc);
FillRect(screen, &rc, GetSysColorBrush(COLOR_WINDOW));
putImage(screen, hWnd);
hr = EndBufferedPaint(hbuff, TRUE);
}
EndPaint(hWnd, &ps); } break;
I just added the buttons with CreateWindowW with WS_VISIBLE and WS_CHILD.
If you set the absolute path of the file, your code will work. It is worth mentioning that you can comment RedrawWindow, because this function will repeatedly refresh the window and cause flicker.
This is a reproducible example, you can try:
#include <Windows.h>
#include <ObjIdl.h>
#include <gdiplus.h>
#include <uxtheme.h>
#include <shobjidl_core.h>
#pragma comment (lib,"Gdiplus.lib")
#pragma comment(lib,"Uxtheme")
#define MAX_LOADSTRING 100
#define IDB_BUTTON1 101
using namespace Gdiplus;
using namespace std;
void putImage(HDC, HWND);
void Opendialog();
HWND hwnd;
PWSTR pszFilePath;
BOOL flag = 0;
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message)
{
case WM_CREATE:
{
HWND hwndButton = CreateWindow(
L"BUTTON", // Predefined class; Unicode assumed
L"OK", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
500, // x position
10, // y position
100, // Button width
100, // Button height
hwnd, // Parent window
(HMENU)IDB_BUTTON1, // No menu.
(HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE),
NULL); // Pointer not needed.
}
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDB_BUTTON1:
Opendialog();
flag = 1;
InvalidateRect(hwnd, NULL, FALSE);
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
HRESULT hr = S_OK;
PAINTSTRUCT ps;
HDC screen = BeginPaint(hwnd, &ps);
// TODO: Add any drawing code that uses hdc here...
if (flag == 1)
{
HPAINTBUFFER hbuff = BeginBufferedPaint(ps.hdc, &ps.rcPaint, BPBF_COMPATIBLEBITMAP, NULL, &screen);
if (hbuff)
{
RECT rc;
GetClientRect(hwnd, &rc);
FillRect(screen, &rc, GetSysColorBrush(COLOR_WINDOW));
putImage(screen, hwnd);
hr = EndBufferedPaint(hbuff, TRUE);
}
flag = 0;
}
EndPaint(hwnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
};
HINSTANCE hinst;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevinstance, PSTR szCmdLine, int iCmdShow) {
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
//Initialize GDI+
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
hinst = GetModuleHandle(NULL);
// create a window class:
WNDCLASS wc = {};
wc.lpfnWndProc = WndProc;
wc.hInstance = hinst;
wc.lpszClassName = L"win32";
// register class with operating system:
RegisterClass(&wc);
// create and show window:
hwnd = CreateWindow(L"win32", L"My program", WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL, 0, 0, 1000, 800, NULL, NULL, hinst, NULL);
if (hwnd == NULL) {
return 0;
}
ShowWindow(hwnd, SW_SHOW);
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
void putImage(HDC hdc, HWND hWnd)
{
Graphics graphic(hdc);
Image* image = Image::FromFile(pszFilePath);
Status status = graphic.DrawImage(image, 10, 20);
RECT updateRect = { 0 };
updateRect.left = 10;
updateRect.top = 10;
updateRect.right = updateRect.left + image->GetWidth();
updateRect.bottom = updateRect.top + image->GetHeight();
// RedrawWindow(hWnd, &updateRect, NULL, RDW_INVALIDATE);
}
void Opendialog()
{
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED |
COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
IFileOpenDialog* pFileOpen;
// Create the FileOpenDialog object.
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));
if (SUCCEEDED(hr))
{
// Show the Open dialog box.
hr = pFileOpen->Show(NULL);
// Get the file name from the dialog box.
if (SUCCEEDED(hr))
{
IShellItem* pItem;
hr = pFileOpen->GetResult(&pItem);
if (SUCCEEDED(hr))
{
hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
// Display the file name to the user.
if (SUCCEEDED(hr))
{
// MessageBox(NULL, pszFilePath, L"File Path", MB_OK);
CoTaskMemFree(pszFilePath);
}
pItem->Release();
}
}
pFileOpen->Release();
}
CoUninitialize();
}
}
Debug:
Updated:
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <tchar.h>
#include <string>
#include<string.h>
#include <iostream>
#include <uxtheme.h>
#include <shobjidl.h>
#include <objidl.h>
#include <gdiplus.h>
#include <gdiplusheaders.h>
#pragma comment (lib,"Gdiplus.lib")
#pragma comment (lib, "uxtheme.lib")
#pragma comment(lib, "user32.lib")
#pragma warning(disable:4996)
using namespace Gdiplus;
#define FILE_OPEN 1
#define FILE_MENU_EXIT 3
#define MENU_HELP 4
static TCHAR szWindowClass[] = _T("DesktopApp");
static TCHAR szTitle[] = _T("Multi-Purpose Media Editor");
HINSTANCE hInst;
HMENU hMenu;
PWSTR filePath;
int flag = 0;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void AddMenus(HWND);
void AddControls(HWND);
void OpenFileWindow(HWND);
void putImage(HDC, HWND);
std::wstring s2ws(const std::string&);
//Main Function for Windows Desktop Application
int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine, _In_ int nCmdShow)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = 0;
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("Windows Desktop Guided Tour"),
NULL);
return 1;
}
hInst = hInstance;
DEVMODE settings;
EnumDisplaySettings(NULL, 0, &settings);
HWND hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_SYSMENU,
CW_USEDEFAULT, CW_USEDEFAULT,
GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
NULL,
NULL,
hInstance,
NULL
);
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
if (!hWnd)
{
MessageBox(NULL,
_T("Call to CreateWindow failed!"),
_T("Windows Desktop Guided Tour"),
NULL);
return 1;
}
ShowWindow(hWnd, nCmdShow);
// Main message loop:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HRESULT hr;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_CREATE:
AddMenus(hWnd);
AddControls(hWnd);
hr = BufferedPaintInit();
break;
case WM_COMMAND:
switch (wParam)
{
case FILE_MENU_EXIT:
// File Menu Exit
DestroyWindow(hWnd);
break;
case FILE_OPEN:
OpenFileWindow(hWnd);
flag = 1;
InvalidateRect(hWnd, NULL, FALSE);
break;
}
break;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC screen = BeginPaint(hWnd, &ps);
/*Gdiplus::Graphics gf(screen);
Gdiplus::Bitmap jpgicon(L"jpg-icon.png");
Gdiplus::Bitmap pdficon(L"pdf-icon.png");
gf.DrawImage(&jpgicon, 10, 10);
gf.DrawImage(&pdficon, 900, 10);*/
if (flag)
{
HPAINTBUFFER hbuff = BeginBufferedPaint(ps.hdc, &ps.rcPaint, BPBF_COMPATIBLEBITMAP, NULL, &screen);
if (hbuff)
{
RECT rc;
GetClientRect(hWnd, &rc);
FillRect(screen, &rc, GetSysColorBrush(COLOR_WINDOW));
//DrawText(screen, L"Multi-Purpose Media Editor", -1, &rc, DT_BOTTOM | DT_VCENTER | DT_SINGLELINE);
putImage(screen, hWnd);
hr = EndBufferedPaint(hbuff, TRUE);
}
flag = 0;
}
EndPaint(hWnd, &ps); } break;
case WM_DESTROY:
BufferedPaintUnInit();
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
void AddMenus(HWND hWnd)
{
hMenu = CreateMenu();
HMENU hFileMenu = CreateMenu();
HMENU hSubMenu = CreateMenu();
// File Open Submenu
/*AppendMenu(hSubMenu, MF_STRING, FILE_OPEN_JPG, s2ws("JPG").c_str());
AppendMenu(hSubMenu, MF_STRING, FILE_OPEN_PDF, s2ws("PDF").c_str());*/
// File Popup Menu
// (UINT_PTR)hSubMenu <- for submenu
AppendMenu(hFileMenu, MF_POPUP, FILE_OPEN, s2ws("Open").c_str());
AppendMenu(hFileMenu, MF_SEPARATOR, NULL, NULL);
AppendMenu(hFileMenu, MF_STRING, FILE_MENU_EXIT, s2ws("Exit").c_str());
// Main Menu
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hFileMenu, s2ws("File").c_str());
AppendMenu(hMenu, MF_STRING, MENU_HELP, s2ws("Help").c_str());
SetMenu(hWnd, hMenu);
}
void AddControls(HWND hWnd)
{
// Static and Edit Controls
CreateWindowW(L"static", L"Multi-Purpose Media Editor", WS_VISIBLE | WS_CHILD | WS_BORDER | SS_CENTER, GetSystemMetrics(SM_CXSCREEN) / 2, 10, 180, 30, hWnd, NULL, NULL, NULL);
// Button on Window
//CreateWindowW(L"button", L"JPG Editor", WS_VISIBLE | WS_CHILD, 190, 150, 100, 30, hWnd, NULL, NULL, NULL);
//CreateWindowW(L"button", L"PDF Editor", WS_VISIBLE | WS_CHILD, 770, 150, 100, 30, hWnd, NULL, NULL, NULL);
CreateWindowW(L"button", L"Edit Image", WS_VISIBLE | WS_CHILD, GetSystemMetrics(SM_CXSCREEN) / 4, 3 * GetSystemMetrics(SM_CYSCREEN) / 4, 100, 30, hWnd, (HMENU)FILE_OPEN, NULL, NULL);
CreateWindowW(L"button", L"Edit PDF", WS_VISIBLE | WS_CHILD, 3 * GetSystemMetrics(SM_CXSCREEN) / 4, 3 * GetSystemMetrics(SM_CYSCREEN) / 4, 100, 30, hWnd, NULL, NULL, NULL);
}
void OpenFileWindow(HWND hWnd)
{
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
IFileOpenDialog* pFileOpen;
// Create the FileOpenDialog object.
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));
if (SUCCEEDED(hr))
{
// Show the Open dialog box.
hr = pFileOpen->Show(NULL);
// Get the file name from the dialog box.
if (SUCCEEDED(hr))
{
IShellItem* pItem;
hr = pFileOpen->GetResult(&pItem);
if (SUCCEEDED(hr))
{
PWSTR pszFilePath;
hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
// Display the file name to the user.
if (SUCCEEDED(hr))
{
char szBuffer[255];
WideCharToMultiByte(CP_ACP, 0, pszFilePath, -1, szBuffer, sizeof(szBuffer), NULL, NULL);
// JPG/JPEG/PNG
if (1)
{
filePath = pszFilePath;
}
// PDF
//else if ()
//{
//}
// Error MSG
else
{
MessageBox(NULL, L"Not a supported media!", L"Error", MB_OK);
}
//LoadImageW(hInst, pszFilePath, 0, 0, 0, LR_DEFAULTSIZE);
//MessageBox(NULL, pszFilePath, L"File Path", MB_OK);
CoTaskMemFree(pszFilePath);
}
pItem->Release();
}
}
pFileOpen->Release();
}
CoUninitialize();
}
}
void putImage(HDC hdc, HWND hWnd)
{
Graphics graphic(hdc);
Image* image = Image::FromFile(filePath);
Status status = graphic.DrawImage(image, 10, 20);
RECT updateRect = { 0 };
updateRect.left = 10;
updateRect.top = 10;
updateRect.right = updateRect.left + image->GetWidth();
updateRect.bottom = updateRect.top + image->GetHeight();
//RedrawWindow(hWnd, &updateRect, NULL, RDW_INVALIDATE);
//RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
}
std::wstring s2ws(const std::string& s)
{
int len;
int slength = (int)s.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}
So I have this project where I have to make like a logical scheme. The only thing that I need to kinda learn to do is the line that can connect to boxe. Think at it like a tree in c++, I have the bubbles and all i need to do is connect them with a line ( I'm thinking like double click-ing on the first bubble then hold the click till i get to the other bubble). This is the code I have till now ( more like a test code )
#include <stdio.h>
#include <winuser.h>
#include <iostream>
#define File_Menu_NEW 1
#define File_Menu_OPEN 2
#define File_Menu_EXIT 3
#define Change_Title 4
#define Open_file 5
#define Save_file 6
LRESULT CALLBACK WindowProcedure(HWND,UINT,WPARAM,LPARAM);
void AddMenus(HWND);
void AddControls(HWND);
HMENU hMenu;
HWND hEdit;
HWND hDestroy;
int x=100,y=100,h=200,l=20,ok;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR args, int ncmdshow)
{
WNDCLASSW wc = {0};
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hCursor = LoadCursor(NULL,IDC_ARROW);
wc.hInstance = hInst;
wc.lpszClassName = L"myWindowClass";
wc.lpfnWndProc = WindowProcedure;
HCURSOR SetCursor(HCURSOR hCursor);
DRAGDROP_S_USEDEFAULTCURSORS;
if(!RegisterClassW(&wc))
return -1;
CreateWindowW(L"myWindowClass",L"Transformator Logic",WS_OVERLAPPEDWINDOW | WS_VISIBLE,100,100,500,500,
NULL,NULL,NULL,NULL);
MSG msg={0};
while(GetMessage(&msg,NULL,NULL,NULL) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
void display_file(char* path)
{
FILE *file;
file = fopen(path,"rb");
fseek(file,0,SEEK_END);
int _size = ftell(file);
rewind(file);
char *data = new char[_size+1];
fread(data,_size,1,file);
data[_size] = '\0';
SetWindowText(hEdit,data);
fclose(file);
}
void open_file(HWND hWnd)
{
OPENFILENAME ofn;
char file_name[100];
ZeroMemory(&ofn,sizeof(OPENFILENAME));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hWnd;
ofn.lpstrFile = file_name;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = 100;
ofn.lpstrFilter = "All files\0*.*\0Source Files\0*.CPP\0Text Files\0*.TXT\0";
ofn.nFilterIndex = 1;
GetOpenFileName(&ofn);
display_file(ofn.lpstrFile);
}
void write_file(char *path)
{
FILE *file;
file = fopen(path,"w");
int _size = GetWindowTextLength(hEdit);
char *data = new char[_size+1];
GetWindowText(hEdit,data,_size+1);
fwrite(data,_size+1,1,file);
fclose(file);
}
void save_file(HWND hWnd)
{
OPENFILENAME ofn;
char file_name[100];
ZeroMemory(&ofn,sizeof(OPENFILENAME));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hWnd;
ofn.lpstrFile = file_name;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = 100;
ofn.lpstrFilter = "All files\0*.*\0Source Files\0*.CPP\0Text Files\0*.TXT\0";
ofn.nFilterIndex = 1;
GetSaveFileName(&ofn);
write_file(ofn.lpstrFile);
}
LRESULT CALLBACK WindowProcedure(HWND hWnd,UINT msg,WPARAM wp,LPARAM lp)
{
switch(msg)
{
case WM_LBUTTONDBLCLK:
{
int xm = LOWORD(lp);
int ym = HIWORD(lp);
}
case WM_LBUTTONDOWN:
{
int iPosX = LOWORD(lp);
int iPosY = HIWORD(lp);
if(x<=iPosX&&x+l>=iPosX&&y<=iPosY&&y+h>=iPosY)
{
DestroyWindow(hDestroy);
ok=1;
}
break;
}
case WM_LBUTTONUP:
{
int iPosx = LOWORD(lp);
int iPosy = HIWORD(lp);
x=iPosx;
y=iPosy;
if(ok==1)
{
hDestroy=CreateWindowW(L"Static",L"Drag and drop test",WS_VISIBLE | WS_CHILD | WS_BORDER | SS_CENTER,x,y,h,l,hWnd,
NULL,NULL,NULL);
ok=0;
}
break;
}
case WM_COMMAND:
switch(wp)
{
case Open_file:
open_file(hWnd);
break;
case Save_file:
save_file(hWnd);
break;
case File_Menu_EXIT:
DestroyWindow(hWnd);
break;
case File_Menu_NEW:
MessageBeep(MB_ICONINFORMATION);
break;
case Change_Title:
wchar_t text[100];
GetWindowTextW(hEdit,text,100);
SetWindowTextW(hWnd,text);
break;
}
break;
case WM_CREATE:
AddMenus(hWnd);
AddControls(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hWnd,msg,wp,lp);
}
}
void AddMenus(HWND hWnd)
{
hMenu = CreateMenu();
HMENU hFileMenu = CreateMenu();
HMENU hSchemePrestabilite = CreateMenu();
HMENU hSubMenu = CreateMenu();
AppendMenu(hMenu,MF_POPUP,(UINT_PTR)hFileMenu,"File");
AppendMenu(hFileMenu,MF_STRING,File_Menu_NEW,"New");
AppendMenu(hFileMenu,MF_POPUP,(UINT_PTR)hSubMenu,"Open SubMenu");
AppendMenu(hSubMenu,MF_STRING,NULL,"SubMenu Item");
//AppendMenu(hSubMenu,MF_STRING,Change_Title,"Change Title");
AppendMenu(hFileMenu,MF_SEPARATOR,NULL,NULL);
AppendMenu(hFileMenu,MF_STRING,File_Menu_EXIT,"Exit");
AppendMenu(hMenu,MF_STRING,Open_file,"Open File");
AppendMenu(hMenu,MF_STRING,Save_file,"Save File");
SetMenu(hWnd,hMenu);
}
void AddControls(HWND hWnd)
{
hDestroy =CreateWindowW(L"Static",L"Drag and drop test",WS_VISIBLE | WS_CHILD | WS_BORDER | SS_CENTER,x,y,h,l,hWnd,
NULL,NULL,NULL);
CreateWindowW(L"Static",L"Line",WS_VISIBLE | WS_CHILD | WS_BORDER | SS_CENTER,100,200,200,20,hWnd,
NULL,NULL,NULL);
//hEdit = CreateWindowW(L"Edit",NULL,WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,100,100,300,300,hWnd,
//(HMENU)Open_file,NULL,NULL);
// CreateWindowW(L"Button",L"Change Title",WS_VISIBLE | WS_CHILD,100,254,200,20,hWnd,
// (HMENU)Change_Title,NULL,NULL);
}
You need to handle the WM_PAINT message inside your WindowProc and draw the line yourself:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
HPEN hpen = CreatePen(PS_SOLID, 2, RGB(0, 0, 0)); // specify line width and color
HANDLE oldpen = SelectObject(hdc, hpen);
MoveToEx(hdc, 10, 10, NULL); // specify "from" coordiates
LineTo(hdc, 400, 300); // specify "to" coordinates
SelectObject(hdc, oldpen);
DeleteObject(hpen);
EndPaint(hWnd, &ps);
break;
}
Alternatively, you can create a "line" component that can draw itself, and add it as a child to your window.
I created a ListView using CreateWindowEx and using the WC_LISTVIEW as class name.
I'm trying to create a smooth scrolling. Everything works perfectly except that list is not drawn correctly. See below the screenshot of the list:
The list view has the following style in CreateWindowEx:
WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_NOCOLUMNHEADER |
WS_TABSTOP | WS_BORDER | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_OWNERDRAWFIXED
I'm using a timer to scroll window. It does the following:
ScrollWindowEx(
listHandle,
0,
step * linesDelta,
NULL,
NULL,
0, 0, 0
);
UpdateWindow(listHandle);
Scroll works perfectly except painting.
I tried:
UpdateWindow() - screenshoot attached
RedrawWindow with all possible options - window is painted only once
InvalidateRect + UpdateWindow = same as 2
InvalidateRect + SendMessage(hwnd, WM_PAINT, 0, 0) - same as 2
The code that paints the item for the list is below:
LRESULT drawItem(HWND hwnd, DRAWITEMSTRUCT* drawStruct) {
Item *itemData = (Item *)drawStruct->itemData;
HDC hdc = drawStruct->hDC;
COLORREF backgroundColor;
COLORREF oldColor;
if (drawStruct->itemState & ODS_SELECTED || ListView_GetHotItem(hwnd) == drawStruct->itemID) {
backgroundColor = GetSysColor(COLOR_HIGHLIGHT);
oldColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
} else {
backgroundColor = RGB(255, 255, 255);
oldColor = SetTextColor(hdc, GetSysColor(COLOR_CAPTIONTEXT));
}
HBRUSH backgroundBrush = CreateSolidBrush(backgroundColor);
HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, backgroundBrush);
FillRect(hdc, &drawStruct->rcItem, backgroundBrush);
drawStruct->rcItem.left += 5;
drawStruct->rcItem.right -= 5;
drawStruct->rcItem.left += 30;
DrawText(hdc, itemData->path, -1, &drawStruct->rcItem,
DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS);
drawStruct->rcItem.left -= 30;
if (itemData->searchData && itemData->searchData->bitmap) {
HBITMAP bitmap = itemData->searchData->bitmap;
HDC hdcMem = CreateCompatibleDC(hdc);
HGDIOBJ oldBitmap = SelectObject(hdcMem, bitmap);
BITMAPINFO bi = { 0 };
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
// Get the bitmap info header.
if (0 != GetDIBits(
hdcMem, // hdc
bitmap, // hbmp
0, // uStartScan
0, // cScanLines
NULL, // lpvBits
&bi,
DIB_RGB_COLORS
)) {
BLENDFUNCTION blendFunc;
blendFunc.BlendOp = AC_SRC_OVER;
blendFunc.BlendFlags = 0;
blendFunc.SourceConstantAlpha = 255;
blendFunc.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(hdc,
drawStruct->rcItem.left + 2, //dest X
drawStruct->rcItem.top + 3, //dest Y
bi.bmiHeader.biWidth,
bi.bmiHeader.biHeight,
hdcMem, 0, 0,
bi.bmiHeader.biWidth,
bi.bmiHeader.biHeight, blendFunc);
}
SelectObject(hdcMem, oldBitmap);
DeleteDC(hdcMem);
}
SelectObject(hdc, hOldBrush);
DeleteObject(backgroundBrush);
SetTextColor(hdc, oldColor);
return 0;
}
Does anyone know a solution for this?
Please see below a complete example created from scratch that has exactly the same behavior:
#include "stdafx.h"
#include "TestList.h"
#include <strsafe.h>
#include <commctrl.h>
#define MAX_LOADSTRING 100
#define ID_LIST_BOX 200
#define TIMER_ID_SMOOTH_SCROLL 100
class ListData {
int scrollToDelta;
int currentScrollPos;
int numPixelsToChangeScrollPos;
int numPixelsChanged;
public:
HWND listWindow;
WNDPROC defaultListProcedure;
void startSmoothScrolling(HWND hwnd, int delta) {
if (delta < 0) {
scrollToDelta = 100;
} else {
scrollToDelta = -100;
}
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_POS;
if (GetScrollInfo(listWindow, SB_VERT, &si)) {
double count = SendMessage(listWindow, LVM_GETITEMCOUNT, 0, 0);
double totalHeight = count * 30;
currentScrollPos = (int)((totalHeight * (double)si.nPos) / (double)si.nMax);
numPixelsToChangeScrollPos = totalHeight / si.nMax;
numPixelsChanged = 0;
} else {
currentScrollPos = 0;
numPixelsChanged = 0;
numPixelsToChangeScrollPos = 30;
}
}
void smoothScroll(HWND listHandle) {
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_POS;
DWORD linesDelta;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesDelta, 0);
if (scrollToDelta < 0) {
if (GetScrollInfo(listHandle, SB_VERT, &si)) {
if (si.nPos == 0) {
KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL);
return;
}
}
scrollToDelta += 5;
int step = -5;
if (scrollToDelta > -80) {
step = -4;
} else if (scrollToDelta > -60) {
step = -3;
} else if (scrollToDelta > -40) {
step = -3;
} else if (scrollToDelta > -20) {
step = -2;
}
numPixelsChanged += abs(step);
if (numPixelsChanged >= numPixelsToChangeScrollPos) {
int posDelta = numPixelsChanged / numPixelsToChangeScrollPos;
numPixelsChanged -= posDelta * numPixelsToChangeScrollPos;
si.nPos = si.nPos + posDelta;
si.fMask = SIF_POS;
SetScrollInfo(listHandle, SB_VERT, &si, TRUE);
}
ScrollWindowEx(
listHandle,
0,
step * linesDelta,
NULL,
NULL,
0, 0,
SW_INVALIDATE);
if (scrollToDelta >= 0) {
KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL);
}
} else {
if (GetScrollInfo(listHandle, SB_VERT, &si)) {
int pos = GetScrollPos(listHandle, SB_VERT);
if (pos == si.nMax) {
KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL);
return;
}
}
scrollToDelta -= 5;
int step = 5;
if (scrollToDelta > -80) {
step = 4;
} else if (scrollToDelta > -60) {
step = 3;
} else if (scrollToDelta > -40) {
step = 3;
} else if (scrollToDelta > -20) {
step = 2;
}
numPixelsChanged += abs(step);
if (numPixelsChanged >= numPixelsToChangeScrollPos) {
int posDelta = numPixelsChanged / numPixelsToChangeScrollPos;
numPixelsChanged -= posDelta * numPixelsToChangeScrollPos;
si.nPos = si.nPos - posDelta;
si.fMask = SIF_POS;
SetScrollInfo(listHandle, SB_VERT, &si, TRUE);
}
ScrollWindowEx(
listHandle,
0,
step * linesDelta,
NULL,
NULL,
0, 0, 0
);
if (scrollToDelta <= 0) {
KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//RedrawWindow(listHandle, NULL, NULL,
// RDW_UPDATENOW | RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_NOERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
//InvalidateRect(listHandle, NULL, FALSE);
//SendMessage(listHandle, WM_PAINT, 0, 0);
UpdateWindow(listHandle);
//ListView_RedrawItems(listHandle, 0, 300);
////////////////////////////////////////////////////////////////////////////////////////////////////
}
};
struct Item {
WCHAR *name;
};
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
// Forward declarations of functions included in this code module:
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: Place code here.
// Initialize global strings
StringCchCopy(szTitle, MAX_LOADSTRING, L"Test");
StringCchCopy(szWindowClass, MAX_LOADSTRING, L"TestClassList");
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTLIST));
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;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
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_TESTLIST));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_TESTLIST);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
LRESULT drawItem(HWND hwnd, DRAWITEMSTRUCT* drawStruct) {
Item *itemData = (Item *)drawStruct->itemData;
HDC hdc = drawStruct->hDC;
COLORREF backgroundColor;
//pcd->clrTextBk;
COLORREF oldColor;
if (drawStruct->itemState & ODS_SELECTED || ListView_GetHotItem(hwnd) == drawStruct->itemID) {
backgroundColor = GetSysColor(COLOR_HIGHLIGHT);
oldColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
} else {
backgroundColor = RGB(255, 255, 255);
oldColor = SetTextColor(hdc, GetSysColor(COLOR_CAPTIONTEXT));
}
HBRUSH backgroundBrush = CreateSolidBrush(backgroundColor);
HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, backgroundBrush);
FillRect(hdc, &drawStruct->rcItem, backgroundBrush);
drawStruct->rcItem.left += 5;
drawStruct->rcItem.right -= 5;
drawStruct->rcItem.left += 30;
DrawText(hdc, itemData->name, -1, &drawStruct->rcItem,
DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS);
drawStruct->rcItem.left -= 30;
SelectObject(hdc, hOldBrush);
DeleteObject(backgroundBrush);
SetTextColor(hdc, oldColor);
return 0;
}
LRESULT CALLBACK ListViewWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
////////////////////////////////////////////////////////////////////////////////////////////////////
case WM_TIMER: {
if (wParam == TIMER_ID_SMOOTH_SCROLL) {
ListData *listData = (ListData*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
listData->smoothScroll(hwnd);
}
break;
}
case WM_MOUSEWHEEL: {
int delta = HIWORD(wParam);
ListData *listData = (ListData*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
listData->startSmoothScrolling(hwnd, delta);
SetTimer(hwnd, TIMER_ID_SMOOTH_SCROLL, 200, NULL);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
default:
ListData *listData = (ListData*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
return CallWindowProc(listData->defaultListProcedure, hwnd, uMsg, wParam, lParam);
}
return 0;
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
100, 100, 400, 400, nullptr, nullptr, hInstance, nullptr);
if (!hWnd) {
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
HWND listWindow = CreateWindowEx(
0,
WC_LISTVIEW,
L"",
WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_NOCOLUMNHEADER |
WS_TABSTOP | WS_BORDER | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_OWNERDRAWFIXED,
1, //x
1, //y
400 - 20, //width
400 - 20, //height
hWnd,
(HMENU)ID_LIST_BOX,
hInstance,
NULL);
ListData *listData = new ListData();
listData->listWindow = listWindow;
SetWindowLongPtr(listWindow, GWLP_USERDATA, (LPARAM)listData);
listData->defaultListProcedure = (WNDPROC)SetWindowLongPtr(listWindow, GWLP_WNDPROC, (LONG_PTR)ListViewWindowProc);
ListView_SetExtendedListViewStyle(listWindow, LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_AUTOSIZECOLUMNS);
SendMessage(listWindow, LVM_SETTEXTBKCOLOR, 0, 0xFFFFFF);
LVCOLUMN col;
col.mask = LVCF_TEXT | LVCF_WIDTH;
col.pszText = L"";
col.cx = 390;
SendMessage(listWindow, LVM_INSERTCOLUMN, 0, (LPARAM)&col);
LVITEM item;
item.mask = LVIF_PARAM | LVIF_TEXT;
item.iSubItem = 0;
for (int i = 0; i < 300; i++) {
item.iItem = i;
Item *itemData = (Item*)malloc(sizeof(Item));
WCHAR *name = (WCHAR*)malloc(sizeof(WCHAR) * 30);;
wsprintf(name, L"Item Name %d", i);
itemData->name = name;
item.pszText = name;
item.lParam = (LPARAM)itemData;
SendMessage(listWindow, LVM_INSERTITEM, 0, (LPARAM)&item);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// 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)
{
switch (message) {
case WM_DRAWITEM: {
////////////////////////////////////////////////////////////////////////////////////////////////////
if (wParam == ID_LIST_BOX) {
DRAWITEMSTRUCT *drawStruct = (DRAWITEMSTRUCT*)lParam;
drawItem(drawStruct->hwndItem, drawStruct);
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
break;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_MEASUREITEM: {
if (wParam == ID_LIST_BOX) {
MEASUREITEMSTRUCT *measureStruct = (MEASUREITEMSTRUCT*)lParam;
measureStruct->itemHeight = 30;
measureStruct->itemWidth = 390;
return TRUE;
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Here's an old trick: before invoking ScrollWindowEx() invalidate the entire listHandle area without erasing the background.
InvalidateRect(listHandle, NULL, FALSE);
ScrollWindowEx(...
Have a nice day!
From MSDN for ScrollWindowEx:
If the SW_INVALIDATE and SW_ERASE flags are not specified, ScrollWindowEx does not invalidate the area that is scrolled from. If either of these flags is set, ScrollWindowEx invalidates this area. The area is not updated until the application calls the UpdateWindow function, calls the RedrawWindow function (specifying the RDW_UPDATENOW or RDW_ERASENOW flag), or retrieves the WM_PAINT message from the application queue.
So let's look at what you tried:
UpdateWindow() - screenshoot attached
Nothing got invalidated, so the update region is empty, so UpdateWindow does nothing.
RedrawWindow with all possible options - window is painted only once
If called correctly, this should invalidate the client. You can cause the WM_ERASEBKGND to happen immediately, but the WM_PAINT message will come only when there's nothing else in the queue. I suspect this didn't work because the WM_TIMER takes priority over the WM_PAINT. (Both are special messages in that they are not actually posted but synthesized when you call GetMessage and nothing else is pending.)
InvalidateRect + UpdateWindow = same as 2
I would expect this to work, but it seems to make more sense to pass the flags to ScrollWindowEx to get the invalidation. I think what's happening is that the control is not designed to draw the items an non-integral positions. So you're getting invalidation, but the window is attempting to draw the items at different offsets than you expect. I don't see an straightforward way to solve this.
InvalidateRect + SendMessage(hwnd, WM_PAINT, 0, 0) - same as 2
That's not a valid WM_PAINT message. Don't do that.
The problem is that ListView is created using the LVS_REPORT flag. This means that list cannot scroll smoothly but only in lines. I.e. if line height is, for instance, 25, scrolling 20 pixels will make the list scroll 25 pixels.
One more issue is that ScrollWindowEx doesn't really scroll the list (or at least it wasn't used correctly). In order to scroll the list, the ListView_Scroll macro should be used (which again scrolls by lines not by pixels).
I do not see where the error is. The window display only the first character you type, and the other is not. Although the cursor moves when typing. And when minimize and maximize the window, the text becomes visible.
#include <windows.h>
#define IDI_MYICON 201
#define IDR_MYMENU 201
#define ID_FIO_FirstName 4002
#define ID_FIO_SecondName 4003
#define ID_FIO_Name 4001
#define ID_INFO_DeveloperStatus 9002
#define ID_INFO_LearningForm 9003
#define ID_INFO_Group 9004
#define BUFSIZE 6665535
#define SHIFTED 0x8000
const char g_szClassName[] = "MFW";
LONG APIENTRY WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
TEXTMETRIC tm;
static DWORD dwCharX;
static DWORD dwCharY;
static DWORD dwClientX;
static DWORD dwClientY;
static DWORD dwLineLen;
static int nCaretPosX = 0;
static int nCaretPosY = 0;
static int nCharWidth = 0;
static int cch = 0;
static int nCurChar = 0;
static PTCHAR pchInputBuf;
int i, j;
int cCR = 0;
int nCRIndex = 0;
int nVirtKey;
TCHAR szBuf[128];
TCHAR ch;
PAINTSTRUCT ps;
RECT rc;
SIZE sz;
COLORREF crPrevText;
COLORREF crPrevBk;
switch(Message)
{
case WM_CREATE:
{
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
ReleaseDC(hwnd, hdc);
dwCharX = tm.tmAveCharWidth;
dwCharY = tm.tmHeight;
pchInputBuf=(LPTSTR)GlobalAlloc(GPTR,BUFSIZE*sizeof(TCHAR));
HMENU hMenu, hSubMenu;
HICON hIcon, hIconSm;
hMenu = CreateMenu();
hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_FIO_FirstName, "FirstName");
AppendMenu(hSubMenu, MF_STRING, ID_FIO_SecondName, "SecondName");
AppendMenu(hSubMenu, MF_STRING, ID_FIO_Name, "Name");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&FIO");
hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_INFO_DeveloperStatus, "DeveloperStatus");
AppendMenu(hSubMenu, MF_STRING, ID_INFO_LearningForm, "LearningForm");
AppendMenu(hSubMenu, MF_STRING, ID_INFO_Group, "Group");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&INFO");
SetMenu(hwnd, hMenu);
hIcon =(HICON)LoadImage(NULL, "my_icon.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
if(hIcon)
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
else
MessageBox(hwnd, "Could not load large icon!","Error", MB_OK | MB_ICONERROR);
hIconSm =(HICON)LoadImage(NULL, "my_icon.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
if(hIconSm)
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
else
MessageBox(hwnd, "Could not load small icon!","Error", MB_OK | MB_ICONERROR);
break;
}
break;
case WM_PAINT:
if (cch==0)break;
hdc=BeginPaint(hwnd,&ps);
HideCaret(hwnd);
SetRect(&rc, 0, 0, dwLineLen, dwClientY);
DrawText(hdc, pchInputBuf,-1,&rc,DT_NOCLIP);
ShowCaret(hwnd);
EndPaint(hwnd, &ps);
break;
case WM_SIZE:
dwClientX=LOWORD(lParam);
dwClientY = HIWORD(lParam);
dwLineLen=dwClientX-dwCharX;
break;
case WM_SETFOCUS:
CreateCaret(hwnd, (HBITMAP) 0,3, dwCharY);
SetCaretPos(nCaretPosX,nCaretPosY*dwCharY);
ShowCaret(hwnd);
break;
case WM_KILLFOCUS:
HideCaret(hwnd);
DestroyCaret();
break;
case WM_CHAR:
if (cch > BUFSIZE-5)
{
pchInputBuf[cch] = 0x00;
SendMessage(hwnd,WM_PAINT, 0, 0);
}
switch (wParam)
{
case 0x08:
case 0x0A:
case 0x1B:
MessageBeep(0xFFFFFFFF);
return 0;
case 0x09:
for (i = 0; i < 4; i++)
SendMessage(hwnd, WM_CHAR, 0x20, 0);
return 0;
case 0x0D:
pchInputBuf[cch++] = 0x0D;
nCaretPosX = 0;
nCaretPosY += 1;
break;
default:
ch =(TCHAR)wParam;
HideCaret(hwnd);
hdc = GetDC(hwnd);
GetCharWidth32(hdc,wParam,wParam,&nCharWidth);
DrawText(hdc, pchInputBuf,-1,&rc, DT_NOCLIP);
ReleaseDC(hwnd, hdc);
pchInputBuf[cch++] = ch;
nCaretPosX += nCharWidth;
if ((DWORD) nCaretPosX > dwLineLen)
{
nCaretPosX = 0;
pchInputBuf[cch++] = 0x0D;
++nCaretPosY;
}
nCurChar = cch;
ShowCaret(hwnd);
break;
}
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
break;
case WM_KEYDOWN:
switch (wParam)
{
case VK_LEFT:
if (nCaretPosX > 0)
{
HideCaret(hwnd);
ch = pchInputBuf[--nCurChar];
hdc = GetDC(hwnd);
GetCharWidth32(hdc, ch, ch, &nCharWidth);
ReleaseDC(hwnd, hdc);
nCaretPosX = max(nCaretPosX - nCharWidth, 0);
ShowCaret(hwnd);
} break;
case VK_RIGHT:
if (nCurChar > cch) {
HideCaret(hwnd);
ch = pchInputBuf[nCurChar];
if (ch == 0x0D) {
nCaretPosX = 0;
nCaretPosY++;
}
else {
hdc = GetDC(hwnd);
nVirtKey = GetKeyState(VK_SHIFT);
if (nVirtKey & SHIFTED) {
crPrevText = SetTextColor(hdc,RGB(255, 255, 255));
crPrevBk = SetBkColor(hdc,RGB(0,0,0));
TextOut(hdc, nCaretPosX,nCaretPosY * dwCharY,&ch,1);
SetTextColor(hdc, crPrevText);
SetBkColor(hdc, crPrevBk);
}
GetCharWidth32(hdc, ch, ch, &nCharWidth);
ReleaseDC(hwnd, hdc);
nCaretPosX = nCaretPosX + nCharWidth;
}
nCurChar++;
ShowCaret(hwnd);
break;
}
break;
case VK_HOME:
nCaretPosX = nCaretPosY = 0;
nCurChar = 0;
break;
case VK_END:
for (i=0; i < cch; i++)
{
if (pchInputBuf[i] == 0x0D) {
cCR++;
nCRIndex = i + 1;
}
}
nCaretPosY = cCR;
for (i = nCRIndex, j = 0; i < cch; i++, j++)
szBuf[j] = pchInputBuf[i];
szBuf[j] = TEXT('\0');
hdc = GetDC(hwnd);
GetTextExtentPoint32(hdc,szBuf,lstrlen(szBuf),&sz);
nCaretPosX = sz.cx;
ReleaseDC(hwnd, hdc);
nCurChar = cch;
break;
default:
break;
}
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_FIO_FirstName:
MessageBox(hwnd, "F-3", "FirstName", 0);
break;
case ID_FIO_SecondName:
MessageBox(hwnd, "B-2", "SecondName", 0);
break;
case ID_FIO_Name:
MessageBox(hwnd, "A-1", "Name", 0);
break;
case ID_INFO_DeveloperStatus:
MessageBox(hwnd, "S", "DeveloperStatus", 0);
break;
case ID_INFO_LearningForm:
MessageBox(hwnd, "FORM", "LearningForm", 0);
break;
case ID_INFO_Group:
MessageBox(hwnd, "GR", "Group", 0);
break;
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
GlobalFree((HGLOBAL) pchInputBuf);
UnregisterHotKey(hwnd, 0xAAAA);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(GetModuleHandle(NULL),MAKEINTRESOURCE(IDI_MYICON));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MYMENU);
wc.lpszClassName = g_szClassName;
wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(IDI_MYICON),IMAGE_ICON,16,16, 0);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
hwnd = CreateWindowEx(
WS_EX_OVERLAPPEDWINDOW,
g_szClassName,
"QWERTY",
WS_OVERLAPPEDWINDOW,
200, 100, 800, 600,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
MSG Msg;
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
You use WM_PAINT incorrectly. You shouldn't send it manually. You need to call InvalidateRect(hwnd, NULL, true); instead of SendMessage(hwnd,WM_PAINT, 0, 0); and it will post a WM_PAINT message to your WndProc correctly.
When you minimize/maximize the window, Windows call internally something like InvalidateRect(hwnd, NULL, true);
Addition by manuell:
Also always call BeginPaint/EndPaint in WM_PAINT handler (you shouldn't break before calling BeginPaint/EndPaint).
i need to create edit box in c++ without using mfc.....
only win32
CreateWindow("EDIT", ...);. You can use CreateWindowEx if you prefer, but it's not necessary. To use it, you'll also normally want to have your window respond to WM_FOCUS by calling SetFocus to set the focus on the edit control. You'll typically also want to respond to WM_MOVE (or is it WM_SIZE - I can't remember) by resizing the edit control to fit the client area of the parent window.
Of course you can also create a dialog (DialogBox or DialogBoxEx) containing an edit control. This avoids having to manually set the focus and such.
So, here's a simple demo program. This creates a main window, and fills its client area with an edit control. It can open and save files, do cut/copy/paste of the data in the control, and select a font in which to display the data in the control. It has an accelerator table, so it knows about the usual keyboard shortcuts for most of those (e.g., ctrl-x = cut, ctrl-c = copy, ctrl-v = paste).
First, the main source code:
// notepad.cpp
#include <windows.h>
#include "notepad.h"
#include <string.h>
#include <string>
#include <fstream>
#include <sstream>
HINSTANCE hInst;
HWND hwnd;
static const HMENU edit_id = HMENU(100);
static HWND hwndEdit;
void Invalidate(HWND window) {
RECT rect;
GetClientRect(window, &rect);
InvalidateRect(window, &rect, TRUE);
}
class file {
std::string filename;
char buffer[FILENAME_MAX];
void write_file() {
size_t size = SendMessage(hwndEdit, WM_GETTEXTLENGTH, 0, 0);
std::string buffer(size+1, '\0');
SendMessage(hwndEdit, WM_GETTEXT, size + 1, (LPARAM)&buffer[0]);
std::ofstream out(filename);
out.write(&buffer[0], size);
}
long long get_size(std::string const& filename) {
WIN32_FIND_DATA data;
auto h = FindFirstFile(filename.c_str(), &data);
long long size = data.nFileSizeHigh;
size <<= 32;
size |= data.nFileSizeLow;
return size;
}
void read_file() {
std::ifstream in(filename);
std::string buffer;
long long size = get_size(filename);
if (size > 1024 * 1024) {
MessageBox(hwnd, "File too large", "", MB_OK);
return;
}
buffer.resize(size+1);
in.read(&buffer[0], size);
std::string::size_type pos = 0;
unsigned count = 0;
while ((pos = buffer.find('\n', pos)) != std::string::npos) {
buffer.replace(pos, 1, "\r\n");
pos += 2;
++count;
}
SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)buffer.c_str());
}
public:
file() : buffer("\0\0") {}
bool open() {
if (SendMessage(hwndEdit, EM_GETMODIFY, 0, 0)) {
if (MessageBox(hwnd, "Open without saving current text?", "Buffer Modified", MB_OKCANCEL) == IDCANCEL)
return false;
}
OPENFILENAMEA spec{};
spec.lStructSize = sizeof(spec);
spec.hwndOwner = hwnd;
spec.lpstrFile = buffer;
spec.nMaxFile = sizeof(buffer);
spec.Flags = OFN_ENABLESIZING | OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_SHAREAWARE | OFN_FILEMUSTEXIST;
if (GetOpenFileName(&spec)) {
filename = spec.lpstrFile;
read_file();
return true;
}
return false;
}
bool save() {
if (filename.empty())
return save_as();
write_file();
SendMessage(hwndEdit, EM_SETMODIFY, 0, 0);
return true;
}
bool save_as() {
OPENFILENAMEA spec{};
spec.lStructSize = sizeof(spec);
spec.hwndOwner = hwnd;
spec.lpstrFile = buffer;
spec.nMaxFile = sizeof(buffer);
spec.Flags = OFN_OVERWRITEPROMPT | OFN_ENABLESIZING | OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_SHAREAWARE;
if (GetSaveFileName(&spec)) {
filename = spec.lpstrFile;
write_file();
SendMessage(hwndEdit, EM_SETMODIFY, 0, 0);
return true;
}
return false;
}
} file;
class font {
HFONT current;
LOGFONT log_font;
CHOOSEFONT spec;
bool choose() {
spec.lStructSize = sizeof(spec);
spec.hwndOwner = hwnd;
spec.lpLogFont = &log_font;
spec.Flags = CF_INITTOLOGFONTSTRUCT | CF_FORCEFONTEXIST | CF_SCREENFONTS;
return ChooseFont(&spec);
}
public:
font()
: current(NULL)
, log_font{}
, spec{}
{}
bool select() {
if (!choose())
return false;
current = CreateFontIndirect(&log_font);
SendMessage(hwndEdit, WM_SETFONT, *reinterpret_cast<WPARAM *>(¤t), TRUE);
return true;
}
} font;
LRESULT CALLBACK MainWndProc(HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam)
{
PAINTSTRUCT ps;
HDC dc;
RECT rect;
int i;
switch (message) {
case WM_PAINT:
dc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_CREATE:
hwndEdit = CreateWindowEx(
0,
"EDIT",
NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN,
0, 0, 0, 0,
hwnd,
edit_id,
hInst,
NULL);
if (hwndEdit == nullptr)
return -1;
return 0;
case WM_SIZE: {
int width = LOWORD(lparam);
int height = HIWORD(lparam);
MoveWindow(hwndEdit, 0, 0, width, height, TRUE);
break;
}
case WM_SETFOCUS:
SetFocus(hwndEdit);
break;
case WM_COMMAND :
switch(LOWORD(wparam)) {
case ID_EXIT:
if (wparam == ID_EXIT)
PostMessage(hwnd, WM_DESTROY, 0, 0);
break;
case ID_FILE_OPEN:
if (file.open())
return 0;
break;
case ID_FILE_SAVE:
if (file.save())
return 0;
break;
case ID_FILE_SAVEAS:
if (file.save_as())
return 0;
break;
case ID_EDIT_UNDO:
if (SendMessage(hwndEdit, EM_CANUNDO, 0, 0))
SendMessage(hwndEdit, WM_UNDO, 0, 0);
return 0;
case ID_EDIT_SELECT_ALL:
SendMessage(hwndEdit, EM_SETSEL, 0, -1);
return 0;
case ID_EDIT_CUT:
SendMessage(hwndEdit, WM_CUT, 0, 0);
break;
case ID_EDIT_COPY:
SendMessage(hwndEdit, WM_COPY, 0, 0);
break;
case ID_EDIT_PASTE:
SendMessage(hwndEdit, WM_PASTE, 0, 0);
break;
case ID_VIEW_FONT:
return font.select();
default: {
return DefWindowProc(hwnd, message, wparam, lparam);
}
}
}
return DefWindowProc(hwnd, message, wparam, lparam);
}
BOOL Init(HINSTANCE hInstance, int nCmdShow)
{
WNDCLASSEX wc;
hInst = hInstance;
wc.cbSize = sizeof(WNDCLASSEX);
wc.hIconSm = (HICON)LoadImage(hInstance,
MAKEINTRESOURCE(IDI_APPICON),
IMAGE_ICON,
16, 16,
0);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPICON));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = "MAINMENU";
wc.lpszClassName = title;
if (!RegisterClassEx(&wc))
return FALSE;
hwnd = CreateWindow(title,
title,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, 0,
CW_USEDEFAULT, 0,
NULL,
NULL,
hInstance,
NULL);
if (!hwnd) {
return FALSE;
}
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
return TRUE;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
HACCEL AccelTable;
if (!Init(hInstance, nCmdShow))
return FALSE;
AccelTable = LoadAccelerators(hInstance, "SHORTCUTS");
while (GetMessage(&msg, NULL, 0, 0))
if (!TranslateAccelerator(hwnd, AccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
The, the header:
// notepad.h
#define ID_EXIT 100
#define ID_FILE_OPEN 101
#define ID_FILE_SAVE 102
#define ID_FILE_SAVEAS 103
#define ID_EDIT_CUT 201
#define ID_EDIT_COPY 202
#define ID_EDIT_PASTE 203
#define ID_EDIT_UNDO 204
#define ID_EDIT_FIND 211
#define ID_EDIT_FIND_NEXT 212
#define ID_EDIT_REPLACE 213
#define ID_EDIT_SELECT_ALL 214
#define ID_VIEW_WRAP 301
#define ID_VIEW_FONT 302
#define IDI_APPICON 400
static char title[] = "Minimum Window";
Note: the header includes defines for a few commands (find/find-next/replace) that aren't implemented in the program.
Then you need a resource file, on this general order:
// notepad.rc
#include "notepad.h"
MAINMENU MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "Open\tCtrl+O", ID_FILE_OPEN
MENUITEM "Save\tCtrl+S", ID_FILE_SAVE
MENUITEM "Save As", ID_FILE_SAVEAS
MENUITEM "E&xit", ID_EXIT
END
POPUP "&Edit"
BEGIN
MENUITEM "Undo\tCtrl+Z", ID_EDIT_UNDO
MENUITEM "Cut\tCtrl+X", ID_EDIT_CUT
MENUITEM "Copy\tCtrl+C", ID_EDIT_COPY
MENUITEM "Paste\tCtrl+V", ID_EDIT_PASTE
MENUITEM SEPARATOR
MENUITEM "Find...\tCtrl+F", ID_EDIT_FIND
MENUITEM "Find Next", ID_EDIT_FIND_NEXT
MENUITEM "Replace...\tCtrl+H", ID_EDIT_REPLACE
MENUITEM "Select All\tCtrl+A", ID_EDIT_SELECT_ALL
END
POPUP "Format"
BEGIN
MENUITEM "&Font", ID_VIEW_FONT
END
END
SHORTCUTS ACCELERATORS
BEGIN
"^O", ID_FILE_OPEN, ASCII
"^S", ID_FILE_SAVE, ASCII
"^A", ID_EDIT_SELECT_ALL, ASCII
"^Z", ID_EDIT_UNDO, ASCII
"^X", ID_EDIT_CUT, ASCII
"^C", ID_EDIT_COPY, ASCII
"^V", ID_EDIT_PASTE, ASCII
END
Although not strictly necessary, a Makefile to build it comes in handy:
notepad.exe: notepad.obj notepad.res
link notepad.obj user32.lib gdi32.lib comdlg32.lib notepad.res
notepad.res: notepad.rc
rc -r notepad.rc
notepad.obj: notepad.cpp
cl -c notepad.cpp
clean:
del *.res
del *.obj
So, if you save those into a directory, open a command prompt for Microsoft's compiler, and type nmake in that directory, it should build a notepad.exe, which will be a mildly stripped down version of the normal Windows notepad. It's missing find/replace, printing, and a couple of other things, but at least has enough to give a decent starting point for how to create and use an edit control.
Oh--one other note. This is mostly quickly hacked together from bits and pieces of old code, with a little bit of new duct tape (so to speak) to hold them together. It is not, by any means, exemplary of the best possible coding practices throughout (to put it nicely).
HWND CreateTextBox(CONST INT iX, CONST INT iY, CONST UINT uWidth, CONST UINT uHeight, HWND hWnd, CONST UINT uId, HINSTANCE hInstance)
{
HWND hWndRet = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("Edit"), NULL, WS_CHILD, iX, iY, (signed)uWidth, (signed)uHeight, hWnd, (HMENU)uId, hInstance, NULL);
SetBkColor(GetDC(hWndRet), RGB(255, 255, 255));
return hWndRet;
}
Just a small function I use for creating default, blank text boxes.