Why do the plot lines disappear in my Win32 application? - c++

I'm trying to write a light Win32 program (OS is Windows XP SP 1) which should draw a 2D plot from a data set stored in a .csv file. I'm using Code::Blocks IDE with a MS Visual C++ Express 2008 compiler. Therefore, I'm also using SDK as a source of Windows headers and libraries.
Here's the code:
main.cpp
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include "daneWykres.h"
static TCHAR szWindowClass[] = _T("win32app");
static TCHAR szTitle[] = _T("Win32 Guided Tour Application");
POINT logicalUnitSize;
RECT ClientRect;
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
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("Win32 Guided Tour"),
0);
return 1;
}
hInst = hInstance;
logicalUnitSize.x = 560;
logicalUnitSize.y = 560;
HWND hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
100, 100,
logicalUnitSize.x, logicalUnitSize.y,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd)
{
MessageBox(NULL,
_T("Call to CreateWindow failed!"),
_T("Win32 Guided Tour"),
0);
return 1;
}
GetClientRect(hWnd,&ClientRect);
ShowWindow(hWnd,
nCmdShow);
UpdateWindow(hWnd);
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)
{
PAINTSTRUCT ps;
HDC hdc;
daneWykres* rysik = new daneWykres;
rysik->sczytaj(); // method putting data from CSV into a static array dane[][]
int iloscPunktow = rysik->getRozmiar(); // a getter for the array's size (number of points)
double minX = rysik->najmniejsza(0); // the lowest X coordinate
double minY = rysik->najmniejsza(1); // the lowest Y coordinate
double maxX = rysik->najwieksza(0); // the highest X coordinate
double maxY = rysik->najwieksza(1); // the highest Y coordinate
int lgXokna = 30; // top left corner X coordinate of the border rectangle
int lgYokna = 30; // top left corner Y coordinate of the border rectangle
int pdXokna = ClientRect.right - lgXokna; // bottom right corner X coordinate of the border rectangle
int pdYokna = ClientRect.bottom - lgYokna; // bottom right corner Y coordinate of the border rectangle
POINT punkt;
punkt.x = 0;
punkt.y = 0;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
HPEN hPenOld;
HPEN hSolidPen;
HPEN hBluePen;
COLORREF qLineColor;
qLineColor = RGB(0,0,0);
hSolidPen = CreatePen(PS_SOLID, 1, qLineColor);
qLineColor = RGB(0,0,255);
hBluePen = CreatePen(PS_SOLID, 2, qLineColor);
hPenOld = (HPEN)SelectObject(hdc, hSolidPen);
// the border rectangle
Rectangle(ps.hdc, lgXokna, lgYokna, pdXokna, pdYokna);
hPenOld = (HPEN)SelectObject(hdc, hBluePen);
// draws text every time a daneWykres constructor is called
if (rysik->getFlaga() == true) DrawText(ps.hdc, TEXT("Rysik istnieje"), -1, &ClientRect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
// 'the drawing loop'
for (int i = 0; i < iloscPunktow; i++)
{
punkt.x = lgXokna + ((pdXokna - lgXokna)*(rysik->getSingleData(i,0) - minX)/(maxX - minX));
punkt.y = pdYokna - ((pdYokna - lgYokna)*(rysik->getSingleData(i,1) - minY)/(maxY - minY));
if (i > 0) LineTo(ps.hdc,punkt.x,punkt.y);
MoveToEx(ps.hdc,punkt.x,punkt.y,NULL);
}
SelectObject(hdc,hPenOld);
DeleteObject(hSolidPen);
DeleteObject(hBluePen);
delete rysik;
EndPaint(hWnd, &ps);
break;
case WM_SIZE:
GetClientRect(hWnd,&ClientRect); // update the client area dimensions (and the plot shape with it)
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
daneWykres.h
#ifndef DANEWYKRES_H_INCLUDED
#define DANEWYKRES_H_INCLUDED
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;
class daneWykres {
static const int rozmiar = 3490; // number of data points
private:
bool flaga; // set to 'true' when constructor is called
ifstream file;
string value;
double wartosc;
double data[rozmiar][2];
public:
daneWykres();
~daneWykres();
double strToDouble(string);
void sczytaj(); // reading method adjusted to the contents of 'file.csv'
double najmniejsza(int indeksKolumny); // returns minimum value in a dataset
double najwieksza(int indeksKolumny); // returns maximum value in a dataset
int getRozmiar();
// returns an X or Y coordinate of a data point by row and column index, respectively
double getSingleData(int indeksWiersza, int indeksKolumny);
bool getFlaga();
};
#endif // DANEWYKRES_H_INCLUDED
daneWykres.cpp
#include "daneWykres.h"
daneWykres::daneWykres()
{
file.open("file.csv");
flaga = true;
}
daneWykres::~daneWykres()
{
file.close();
flaga = false;
}
double daneWykres::strToDouble(string zapis)
{
const char* c = zapis.c_str();
return atof(c);
}
void daneWykres::sczytaj()
{
int i = 0;
while (i < rozmiar)
{
getline(file,value,',');
wartosc = strToDouble(value);
data[i][0] = wartosc;
getline(file,value,',');
wartosc = strToDouble(value);
data[i][1] = wartosc;
getline(file,value,'\n');
i++;
}
}
double daneWykres::najmniejsza(int indeksKolumny)
{
double najmniejsza = data[0][indeksKolumny];
for (int i = 0; i < rozmiar; i++) najmniejsza = ((data[i][indeksKolumny] < najmniejsza) ? data[i][indeksKolumny] : najmniejsza);
return najmniejsza;
}
double daneWykres::najwieksza(int indeksKolumny)
{
double najwieksza = data[0][indeksKolumny];
for (int i = 0; i < rozmiar; i++) najwieksza = ((data[i][indeksKolumny] > najwieksza) ? data[i][indeksKolumny] : najwieksza);
return najwieksza;
}
int daneWykres::getRozmiar()
{
return rozmiar;
}
double daneWykres::getSingleData(int indeksWiersza, int indeksKolumny)
{
return data[indeksWiersza][indeksKolumny];
}
bool daneWykres::getFlaga()
{
return flaga;
}
Also, here's the link to a zip containing 'file.csv', just in case:
https://drive.google.com/file/d/1QHore2OJpQqaAzgxJEiVJIs0BFiSShFo/view?usp=sharing
Whenever I'm running the build via Code::Blocks (don't mind the title) on a machine with Win XP SP1, I see this:
https://imgur.com/SjOMu0G
And after resizing or minimizing a few times I see this:
https://imgur.com/mwzOf3w
I initially thought it was a matter of the object from daneWykres class being prematurely destroyed but the 'Rysik istnieje' string displays properly.
I've got hold of "Programming Windows" by C. Petzold recently but I'm still struggling to understand the basic concepts underlaying WinAPI and it shows.
So, the main question is: why are the black line rectangle and 'Rysik istnieje' string being drawn, yet the blue plot lines cease to be drawn at some point?
EDIT:
I've moved the delete rysik back to the end of the WM_PAINT message. I've separated the declarations and assignments and moved it all into the WM_PAINT message (along with allocation to and deallocation from rysik). This has seemingly helped limit the memory/resource leak.
I've moved the code for opening and reading the file into global functions. The resulting application seems to run smoother but I don't see any significant changes in the Task Manager compared to the earlier edit.
Here's what I have thus far:
main.cpp
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include <fstream>
#include <string>
#include <vector>
#include "daneWykres.h"
static TCHAR szWindowClass[] = _T("win32app");
static TCHAR szTitle[] = _T("My Application");
double data[3490][2];
POINT logicalUnitSize;
RECT ClientRect;
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
double strToDouble(string zapis)
{
const char* c = zapis.c_str();
return atof(c);
}
void sczytaj(ifstream& file, double (&data)[3490][2])
{
double wartosc;
string line;
unsigned int i = 0;
unsigned int pos, pos2;
while (getline(file,line))
{
pos = 0;
pos2 = line.find(',',pos);
wartosc = strToDouble(line.substr(pos,(pos2-pos)));
data[i][0] = wartosc;
pos = pos2 + 1;
pos2 = line.find(',',pos);
wartosc = strToDouble(line.substr(pos,(pos2-pos)));
data[i][1] = wartosc;
i++;
}
}
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
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("Win32 Guided Tour"),
0);
return 1;
}
hInst = hInstance;
ifstream file;
file.open("file.csv");
sczytaj(file,data);
file.close();
logicalUnitSize.x = 560;
logicalUnitSize.y = 560;
HWND hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
100, 100,
logicalUnitSize.x, logicalUnitSize.y,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd)
{
MessageBox(NULL,
_T("Call to CreateWindow failed!"),
_T("Win32 Guided Tour"),
0);
return 1;
}
GetClientRect(hWnd,&ClientRect);
ShowWindow(hWnd,
nCmdShow);
UpdateWindow(hWnd);
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)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
daneWykres* rysik;
rysik = new daneWykres;
int iloscPunktow;
iloscPunktow = rysik->getRozmiar();
double minX, minY, maxX, maxY;
minX = rysik->najmniejsza(data, 0);
minY = rysik->najmniejsza(data, 1);
maxX = rysik->najwieksza(data, 0);
maxY = rysik->najwieksza(data, 1);
int lgXokna, lgYokna, pdXokna, pdYokna;
lgXokna = 30;
lgYokna = 30;
pdXokna = ClientRect.right - lgXokna;
pdYokna = ClientRect.bottom - lgYokna;
POINT punkt;
punkt.x = 0;
punkt.y = 0;
HPEN hPen;
HPEN hSolidPen;
HPEN hBluePen;
hSolidPen = CreatePen(PS_SOLID, 1, RGB(0,0,0));
hBluePen = CreatePen(PS_SOLID, 2, RGB(0,0,255));
hPen = (HPEN)SelectObject(hdc, hSolidPen);
Rectangle(ps.hdc, lgXokna, lgYokna, pdXokna, pdYokna);
SelectObject(hdc,hPen);
DeleteObject(hSolidPen);
hPen = (HPEN)SelectObject(hdc, hBluePen);
if (rysik) DrawText(ps.hdc, TEXT("Rysik istnieje"), -1, &ClientRect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
for (int i = 0; i < iloscPunktow; i++)
{
punkt.x = lgXokna + ((pdXokna - lgXokna)*(data[i][0] - minX)/(maxX - minX));
punkt.y = pdYokna - ((pdYokna - lgYokna)*(data[i][1] - minY)/(maxY - minY));
if (i > 0) LineTo(ps.hdc,punkt.x,punkt.y);
MoveToEx(ps.hdc,punkt.x,punkt.y,NULL);
}
SelectObject(hdc,hPen);
DeleteObject(hBluePen);
delete rysik;
EndPaint(hWnd, &ps);
break;
case WM_SIZE:
GetClientRect(hWnd,&ClientRect);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
daneWykres.h
#ifndef DANEWYKRES_H_INCLUDED
#define DANEWYKRES_H_INCLUDED
#include <fstream>
#include <string>
#include <cstdlib>
#include <vector>
using namespace std;
class daneWykres {
static const int rozmiar = 3490;
private:
public:
daneWykres();
~daneWykres();
double najmniejsza( double (&data)[3490][2], int indeksKolumny);
double najwieksza( double (&data)[3490][2], int indeksKolumny);
int getRozmiar();
};
#endif // DANEWYKRES_H_INCLUDED
daneWykres.cpp
#include "daneWykres.h"
daneWykres::daneWykres()
{
}
daneWykres::~daneWykres()
{
}
double daneWykres::najmniejsza( double (&data)[3490][2], int indeksKolumny)
{
double najmniejsza = data[0][indeksKolumny];
for (int i = 0; i < rozmiar; i++) najmniejsza = ((data[i][indeksKolumny] < najmniejsza) ? data[i][indeksKolumny] : najmniejsza);
return najmniejsza;
}
double daneWykres::najwieksza( double (&data)[3490][2], int indeksKolumny)
{
double najwieksza = data[0][indeksKolumny];
for (int i = 0; i < rozmiar; i++) najwieksza = ((data[i][indeksKolumny] > najwieksza) ? data[i][indeksKolumny] : najwieksza);
return najwieksza;
}
int daneWykres::getRozmiar()
{
return rozmiar;
}

Related

Why are the angles calculated in c++ not accurate?

I wanted to write a turtle in c++, but the angles at the turn are not accurate.
I tried to create a Koch curve, but the result was bad
Hereis a larger picture of this, it looks like the angles are wrong
This is my program:
#include <windows.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include "turtle.h"
#define MAX_LOADSTRING 100
// Global Variables:
#define HIBA_00 TEXT("Error:Program initialisation process.")
HINSTANCE hInstGlob;
int OwniCmdShow;
char szClassName[] = "WindowsApp";
HWND Form1;
LRESULT CALLBACK WndProc0(HWND, UINT, WPARAM, LPARAM);
RECT rc;
HDC hdcMem;
HBITMAP hbmMem, hbmOld;
HBRUSH hbrBkGnd;
HDC hdc;
PAINTSTRUCT ps;
void DB_prepare_puffer(void);
void DB_start_drawing(void);
void DB_end_drawing(void);
void draw(void);
void screen_clear(void);
void print(int n);
int depth = 4;
float length = 1000;
turtle t;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("StdWinClassName");
MSG msg;
WNDCLASS wndclass0;
OwniCmdShow = iCmdShow;
hInstGlob = hInstance;
wndclass0.style = CS_HREDRAW | CS_VREDRAW;
wndclass0.lpfnWndProc = WndProc0;
wndclass0.cbClsExtra = 0;
wndclass0.cbWndExtra = 0;
wndclass0.hInstance = hInstance;
wndclass0.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass0.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass0.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass0.lpszMenuName = NULL;
wndclass0.lpszClassName = TEXT("WIN0");
if (!RegisterClass(&wndclass0))
{
MessageBox(NULL, HIBA_00, TEXT("Program Start"), MB_ICONERROR); return 0;
}
Form1 = CreateWindow(TEXT("WIN0"),
TEXT("Form1"),
(WS_OVERLAPPED | WS_SYSMENU | WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX),
0,
0,
1920,
1050,
NULL,
NULL,
hInstance,
NULL);
DB_prepare_puffer();
ShowWindow(Form1, OwniCmdShow);
UpdateWindow(Form1);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc0(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc_lokal;
PAINTSTRUCT ps_lokal;
switch (message) {
case WM_CREATE:
break;
case WM_MOUSEMOVE:
break;
case WM_ERASEBKGND:
return (LRESULT)1;
break;
case WM_PAINT:
hdc_lokal = BeginPaint(hwnd, &ps_lokal);
EndPaint(hwnd, &ps_lokal);
t.init();
draw();
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, message, wParam, lParam);
break;
}
return NULL;
}
void DB_prepare_puffer(void)
{
GetClientRect(Form1, &rc);
hdc = GetDC(Form1);
hbmMem = CreateCompatibleBitmap(hdc, rc.right - rc.left, rc.bottom - rc.top);
}
void DB_start_drawing(void)
{
GetClientRect(Form1, &rc);
hdc = GetDC(Form1);
BeginPaint(Form1, &ps);
hdcMem = CreateCompatibleDC(hdc);
hbmOld = (HBITMAP)SelectObject(hdcMem, hbmMem);
hbrBkGnd = CreateSolidBrush(RGB(255, 255, 255));
FillRect(hdcMem, &rc, hbrBkGnd);
DeleteObject(hbrBkGnd);
}
void DB_end_drawing(void)
{
BitBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
EndPaint(Form1, &ps);
}
void draw(void)
{
DB_start_drawing();
screen_clear();
int i = 0;
depth = 5;
length = 1200;
while (i < depth) {
length = length / 3;
i++;
}
t.right(270);
print(depth);
DB_end_drawing();
}
//************************
//clear screen
//************************
void screen_clear(void)
{
HBRUSH hBrush;
RECT rect;
HDC hdc;
hdc = hdcMem;
hBrush = CreateSolidBrush(RGB(255, 255, 255));
SelectObject(hdc, hBrush);
SetRect(&rect, 0, 0, 1500, 900);
FillRect(hdc, &rect, hBrush);
DeleteObject(hBrush);
}
void print(int m) {
int n = m;
if (n == 0) {
t.forward(length, hdcMem);
}
if (n != 0){
n -= 1;
print(n);
t.left(60);
print(n);
t.right(120);
print(n);
t.left(60);
print(n);
}
}
this is the turtle.h file:
#pragma once
#define _USE_MATH_DEFINES
#include "resource.h"
#include <windows.h>
#include <cmath>
class turtle {
public:
void init();
void right(double _angle);
void left(double _angle);
void forward(int distance, HDC hdc);
double angle = 0;
POINT pos;
protected:
private:
};
And this is the turtle.cpp:
#pragma once
#include "turtle.h"
void turtle::init()
{
pos.x = 40;
pos.y = 800;
angle = 0;
}
void turtle::left(double _angle) {
angle -= _angle;
}
void turtle::right(double _angle) {
angle += _angle;
}
void turtle::forward(int distance, HDC hdc) {
MoveToEx(hdc, pos.x, pos.y, NULL);
double cos_angle = cos(angle * M_PI / (double)180);
double sin_angle = sin(angle * M_PI / (double)180);
POINT endpoint;
endpoint.x = (0 - (double)distance * sin_angle) + pos.x;
endpoint.y = (0 + (double)distance * cos_angle) + pos.y;
LineTo(hdc, endpoint.x, endpoint.y);
pos = endpoint;
}
In Python, however, this code worked (essentially the same as the c++ code):
from turtle import *
color('blue', 'white')
speed(100000000)
penup()
back(600)
pendown()
begin_fill()
depth = 0
length = 300
i = 0
def print(n):
if n == 0:
forward(length )
if n != 0:
n -= 1
print(n)
left(60)
print(n)
right(120)
print(n)
left(60)
print(n)
i = 0
depth = 5
length = 300
while i < depth:
length = length / 3
i += 1
print(depth)
(Result)
I hope you can help me.
Thanks
It will work with this code because the previously calculated position is stored more accurately.
turtle.cpp:
#pragma once
#include "turtle.h"
void turtle::init()
{
pos.x = 40;
pos.y = 800;
angle = 0;
}
void turtle::left(double _angle) {
angle -= _angle;
}
void turtle::right(double _angle) {
angle += _angle;
}
void turtle::forward(double distance, HDC hdc) {
MoveToEx(hdc, pos.x, pos.y, NULL);
double cos_angle = cos(angle * M_PI / (double)180);
double sin_angle = sin(angle * M_PI / (double)180);
DPOINT endpoint;
endpoint.x = (0 - distance * sin_angle) + pos.x;
endpoint.y = (0 + distance * cos_angle) + pos.y;
LineTo(hdc, endpoint.x, endpoint.y);
pos = endpoint;
}
And insert this code to turtle.h:
`
typedef struct DPOINT {
double x;
double y;
};
`
And change the 'pos' variable type to 'DPOINT'

Why does my StretchDiBits (windows.h) projected the bitmap on screen so slow and strippy?

CreateWindow.h
#pragma once
#include <Windows.h>
#include <iostream>
#include <stdint.h>
#include <cmath>
#include <vector>
#include <sstream>
using namespace std;
struct returncursorposdemcical
{
float x, y;
};
struct CustomImage
{
vector<vector<unsigned>> CImage;
int long height, width;
};
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
class window
{
public:
window();
window(const window&) = delete;
window& operator = (const window&) = delete;
~window();
bool windowpro();
void stretchbit();
void backgroundcolor(int R, int G, int B);
private:
};
CreateWindow.cpp
#include "../Header/CreateWindow.h"
WNDCLASS WindowClass = {};
HWND CreateMyWindow;
HDC mydc;
int BitmapWidth;
int BitmapHeight;
RECT ClientRect;
int ClientWidth;
int ClientHeight;
long int buffer_sizes;
void* buffer_memory;
BITMAPINFO buffer_bitmap;
HINSTANCE myhinstance;
window::window()
{
WindowClass.lpszClassName = "Game_Engine";
WindowClass.lpfnWndProc = WindowProc;
WindowClass.hInstance = myhinstance;
WindowClass.hCursor = LoadCursor(0, IDC_CROSS);
RegisterClass(&WindowClass);
CreateMyWindow = CreateWindowEx(0, "Game_Engine", "Program",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
0, 0, GetModuleHandle(nullptr), 0);
mydc = GetDC(CreateMyWindow);
ShowWindow(CreateMyWindow, SW_SHOWMAXIMIZED);
}
window::~window()
{
std::cout << "destroy";
ReleaseDC(CreateMyWindow, mydc);
UnregisterClass("Game_Engine", myhinstance);
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
case WM_MOUSEMOVE:
{
}
case WM_MOUSELEAVE:
{
}
case WM_SIZE:
{
GetClientRect(CreateMyWindow, &ClientRect);
ClientWidth = ClientRect.right - ClientRect.left;
ClientHeight = ClientRect.bottom - ClientRect.top;
BitmapWidth = ClientWidth;
BitmapHeight = ClientHeight;
buffer_sizes = BitmapWidth * BitmapHeight * sizeof(unsigned int);
if (buffer_memory) {
VirtualFree(buffer_memory, 0, MEM_RELEASE);
}
buffer_memory = VirtualAlloc(0, buffer_sizes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
buffer_bitmap.bmiHeader.biSize = sizeof(buffer_bitmap.bmiHeader);
buffer_bitmap.bmiHeader.biWidth = BitmapWidth;
buffer_bitmap.bmiHeader.biHeight = -BitmapHeight;
buffer_bitmap.bmiHeader.biPlanes = 1;
buffer_bitmap.bmiHeader.biBitCount = 24;
buffer_bitmap.bmiHeader.biCompression = BI_RGB;
}
return 0;
case WM_PAINT:
{
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
bool window::windowpro()
{
MSG msg = { };
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT) {
return false;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return true;
}
void window::backgroundcolor(int R, int G, int B)
{
unsigned int* pixel = (unsigned int*)buffer_memory;
for (int y = 0; y < BitmapHeight; y++) {
for (int x = 0; x < BitmapWidth; x++)
{
*pixel++ = (R << 16) + (G << 8) + B;
}
}
}
void window::stretchbit()
{
StretchDIBits(mydc, 0, 0, BitmapWidth, BitmapHeight, 0, 0, ClientWidth, ClientHeight, buffer_memory,&buffer_bitmap,DIB_RGB_COLORS,SRCCOPY);
}
Source.cpp
#include "../WindowStartup/Header/CreateWindow.h"
int main()
{
window mywindow;
bool running = true;
while (running == true)
{
mywindow.backgroundcolor(225, 225, 225);
mywindow.stretchbit();
if (!mywindow.windowpro())
{
running = false;
}
}
return 0;
}
I tried to double buffer it, but it didn't work, I tried using bitblit, but it's the same result. Also, when the window resizing, it's not fully painted. And when the window it's not at the full size, it does not look strippy, so my speculation is that either my program is writing the data to bitmap to slow for the refresh rate, or the bitmap it's copying to screen to slow. Sorry for my bad English, English it's not my first language, and I am new to program.
I fixed, all you have to do is change biBitcounts to 32, I don't know why that works, might be some to do with ARGB, although I am only using RGB which is 24bits or 3 bytes, if someone could explain this why I need to change biBitcounts to 32, that would be nice. Also thanks for all the people in the comment that are trying to help me.

std::atomic_flag throws exception when I try to write to it with std::atomic_flag::test_and_set()

I'm using the winapi to create a window. For ease of use, I want to wrap it in a C++ class.
When the destructor this class is called, I want to delete the window. However, if the window hasn't been loaded by the time the destructor is called, I.E the constructor of my wrapper class is called and immediately followed by the destructor, I have to need to wait for it to load.
To determine whether it's been loaded yet, I'm currently using an std::atomic_flag. I also tried using an atomic_bool but that had the same problem.
The exception is:
Exception thrown: write access violation.
this was nullptr.
It stems from test_and_set, as I said in the title, and happens somewhere in atomic_flag's exchange method.
The atomic_flag varirable is named "windowLoadedYet", and the offending piece of code is:
windowCreatedYet.test_and_set();
windowCreatedYet.notify_one();
Regrettably, This is my minimal producible result. I tried to make a smaller version, using atomic_bool in some test code, but unfortunately that code ran successfully.
#ifndef UNICODE
#define UNICODE
#endif
#include <time.h>
#include <windows.h>
#include <string>
//#include "events.h"
#include <functional>
//#include <opencv2/opencv.hpp>
#include <future>
#include <iostream>
#include <thread>
#include <atomic>
#include <map>
using std::cout;
using std::cin;
//int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {}
struct MirrageWindow {
MirrageWindow(HINSTANCE hInstance) : hInstance(hInstance){
activeInstancesLedger[hwnd] = this;
//cout << "thread id: " << std::this_thread::get_id() << "\n";
//std::jthread myThread(&MirrageWindow::maintainTheWindow, this);
//maintainTheWindow();
windowThread = std::jthread(&MirrageWindow::setup, this);
}
~MirrageWindow() {
if (!invalidated) {
activeInstancesLedger.erase(hwnd);
windowCreatedYet.wait(false);
SendMessageA(hwnd, WM_DESTROY, NULL, NULL);
cout << "posted message\n";
}
}
MirrageWindow(const MirrageWindow& other) = delete;
private:
bool invalidated = false;
HINSTANCE hInstance;
HWND hwnd = NULL;
std::atomic_flag windowCreatedYet = ATOMIC_FLAG_INIT;
std::jthread windowThread;
LRESULT CALLBACK windowProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
//cout << "Message Recieved, loud and clear.\n";
switch (uMsg)
{
case WM_CREATE:
//std::this_thread::sleep_for(std::chrono::seconds(1));
//windowCreatedYet = false;
windowCreatedYet.test_and_set();
windowCreatedYet.notify_one();
return 0;
case WM_DESTROY:
cout << "Quit message received\n";
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here, between BeginPaint and EndPaint.
HBRUSH myCol = CreateSolidBrush(RGB(1, 255, 255));
FillRect(hdc, &ps.rcPaint, myCol);
RECT b{ 0,0,90,200 };
//DrawText(hdc,L"It is disappointing when the toys you love don't love you back. This was Andy's struggle.",-1,&b, DT_BOTTOM | DT_WORDBREAK);
EndPaint(hwnd, &ps);
return 0;
}
case WM_NCHITTEST: {
std::string a = "hi";
time_t t = time(NULL);
a += std::to_string(t);
SetWindowTextA(hwnd, a.c_str());
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here, between BeginPaint and EndPaint.
HBRUSH filler = CreateSolidBrush(RGB(255, 255, 0));
FillRect(hdc, &ps.rcPaint, filler);
DeleteObject(filler);
RECT b{ 0,0,90,200 };
DrawText(hdc, L"You clicked. I knew you would.", -1, &b, DT_BOTTOM | DT_WORDBREAK);
EndPaint(hwnd, &ps);
return 0;
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
void registerWindowClassIfNotRegistered() {
if (windowClasses.find(hInstance)==windowClasses.end()) {
static uint_fast32_t id_counter = 0;
id_counter++;
std::wstring className = L"Class for Mirrage Windows in this hInstance";
className+=L" id-num: "+(std::to_wstring(id_counter));
windowClasses[hInstance] = className;
WNDCLASS wc{};
wc.hInstance = hInstance;
wc.lpfnWndProc = &redirectorBecauseFuckC;
wc.lpszClassName = className.c_str();
RegisterClass(&wc);
}
}
void createTheWindow() {
cout << std::string(windowClasses[hInstance].begin(), windowClasses[hInstance].end());
hwnd = CreateWindowEx(
WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,
windowClasses[hInstance].c_str(),
L"Learn to Program Windows, John.",
WS_POPUP,
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
throw std::runtime_error("Failed to create window.");
}
SetLayeredWindowAttributes(hwnd, RGB(1, 2, 3), 100, LWA_ALPHA);
ShowWindow(hwnd, SW_SHOW);
}
void maintainTheWindow() {
//cout << "running..\n";
MSG msg = { };
SetWindowDisplayAffinity(hwnd, WDA_EXCLUDEFROMCAPTURE);
SetWindowPos(hwnd, HWND_TOPMOST, -0, 0, GetSystemMetrics(0), GetSystemMetrics(1), SWP_FRAMECHANGED);
while (GetMessage(&msg, NULL, 0, 0) != 0)
{
//cout << "transmitting message\n";
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
void setup() {
registerWindowClassIfNotRegistered();
createTheWindow();
maintainTheWindow();
}
static inline std::map<HINSTANCE, std::wstring> windowClasses;
static inline std::map<HWND, MirrageWindow*> activeInstancesLedger;
static LRESULT CALLBACK redirectorBecauseFuckC(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
MirrageWindow* redirect = activeInstancesLedger[hwnd];
return redirect->windowProcessMessage(hwnd, uMsg, wParam, lParam);
}
};
int main() {
HINSTANCE hInstance = GetModuleHandle(NULL);
MirrageWindow myWindow(hInstance);
//std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}

Why is the Window not loading ? [Win32] [duplicate]

This question already has an answer here:
Win32 API - RegisterClassEx errors
(1 answer)
Closed 2 years ago.
Hi I am an Beginner learning DirectX 11 Api I have seen tutorial on Youtube and implemented this Wrapper for Windows But the Window is lot Loading The only debug string I get is just the Register Class Failed string in the INIT_WINDOw method . Nothing else Pops up . PLease help me with this .
DX_Wrapper.cpp
#include "Headers/Dx_Wrapper.h"
//Global One For Defining __stdcall for WindowProc
namespace
{
Wrapper* g_wrapper = nullptr;
}
//Main WINDOW Message processing Window Procedure
LRESULT CALLBACK WINDOWPROC(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
if (g_wrapper) return g_wrapper->MessageProcedure(hwnd, msg, wparam, lparam);
else return DefWindowProc(hwnd, msg, wparam, lparam);
}
//Constructor For the Instance
Wrapper::Wrapper(HINSTANCE hinstance)
{
m_hinstance = hinstance;
m_hwnd = NULL;
m_winheight = 600;
m_winwidth = 800;
m_wintitle = L"DirectX";
m_winstyles = WS_OVERLAPPEDWINDOW;
g_wrapper = this;
}
Wrapper::~Wrapper()
{
}
//Window Loop Manager Attached to the WINDOWPROC function
int32_t Wrapper::Run()
{
MSG message = { 0 };
while (WM_QUIT != message.message)
{
//Message Check If Quit
if (PeekMessage(&message, NULL, NULL, NULL, PM_REMOVE))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
//If Not Quit
else
{
UPDATE_FRM(5.5f);
}
}
return static_cast<int>(message.wParam);
}
bool Wrapper::Init()
{
if (!INIT_WINDOW() == true)
{
return false;
}
return true;
}
LRESULT Wrapper::MessageProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
}
void Wrapper::UPDATE_FRM(float delta_time)
{
}
void Wrapper::RENDER_FRM(float delta_time)
{
}
//Registering Classical Windows Class Function
bool Wrapper::INIT_WINDOW()
{
//THE CLASS REGISTERING STUFF
WNDCLASSEX win_class;
win_class.cbClsExtra = 0;
win_class.hInstance = m_hinstance;
win_class.cbWndExtra = 0;
win_class.style = CS_HREDRAW | CS_VREDRAW;
win_class.cbSize = sizeof(WNDCLASSEX);
win_class.lpfnWndProc = WINDOWPROC;
win_class.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
win_class.lpszMenuName = NULL;
win_class.lpszClassName = L"dx_main_class";
win_class.hCursor = 0;
RegisterClassEx(&win_class);
if (!RegisterClassEx(&win_class))
{
OutputDebugString(L"Registering Class Failed");
return false;
}
RECT r = {0,0,m_winwidth,m_winheight};
AdjustWindowRect(&r, m_winstyles, FALSE);
uint_fast16_t f_width = r.right - r.left;
uint_fast16_t f_height = r.bottom - r.top;
uint_fast16_t f_width_final = uint_fast16_t(GetSystemMetrics(SM_CXSCREEN) / 2 - f_width / 2);
uint_fast16_t f_height_final = uint_fast16_t(GetSystemMetrics(SM_CYSCREEN) / 2 - f_height / 2);
m_hwnd = CreateWindow(L"dx_main_class", m_wintitle, m_winstyles, f_width_final, f_height_final, f_width, f_height, NULL, NULL, m_hinstance, NULL);
if (!m_hwnd)
{
OutputDebugString(L"Window Could Not be Created ");
return false;
}
ShowWindow(m_hwnd, SW_SHOW);
return true;
}
DX_Wrapper.h
#include <Windows.h>
#include <stdint.h>
#include <string>
#define WIN32_LEAN_AND_MEAN
class Wrapper
{
public:
Wrapper(HINSTANCE hinstance);
virtual ~Wrapper();
int32_t Run();
virtual bool Init();
virtual LRESULT MessageProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
virtual void UPDATE_FRM(float delta_time) = 0;
virtual void RENDER_FRM(float delta_time) = 0;
protected:
HWND m_hwnd = nullptr;
HINSTANCE m_hinstance = nullptr;
uint_fast16_t m_winwidth = 0;
uint_fast16_t m_winheight = 0;
LPCWSTR m_wintitle = L"";
DWORD m_winstyles = 0;
protected:
bool INIT_WINDOW();
};
This is the Main Application CPP file
#include "Headers/Dx_Wrapper.h"
class Application : public Wrapper
{
public:
Application(HINSTANCE hinstance);
bool Init() override;
void UPDATE_FRM(float delta_time) override;
void RENDER_FRM(float delta_time) override;
};
Application::Application(HINSTANCE hinstance) : Wrapper(hinstance)
{
}
bool Application::Init()
{
return Wrapper::Init();
}
void Application::UPDATE_FRM(float delta_time)
{
}
void Application::RENDER_FRM(float delta_time)
{
}
int32_t WINAPI WinMain(__in HINSTANCE hinstance, __in_opt HINSTANCE hprevinstance, __in LPSTR cmdline, __in int cmdshow)
{
Application m_app(hinstance);
if (!m_app.Init()) return 1;
return m_app.Run();
}
My Problem is The OutputDebugString Just shows The Registering class failed string from the INIT_WINDOW function . I have no clue why it is wrong . The Window is not running .
First you called twice RegisterClassEx function, so you need to delete the first call:
//RegisterClassEx(&win_class);
if (!RegisterClassEx(&win_class))
{
OutputDebugString(L"Registering Class Failed");
return false;
}
Then you need to set each member of WNDCLASSEX.
So you can refer to the following code:
WNDCLASSEX win_class;
win_class.cbSize = sizeof(WNDCLASSEX);
win_class.style = CS_HREDRAW | CS_VREDRAW;
win_class.lpfnWndProc = WINDOWPROC;
win_class.cbClsExtra = 0;
win_class.cbWndExtra = 0;
win_class.hInstance = m_hinstance;
win_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
win_class.hCursor = 0;
win_class.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
win_class.lpszMenuName = NULL;
win_class.lpszClassName = L"dx_main_class";
win_class.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

How can I fix error in ListView?, can't select any items when I encapsulate the control

I'm working on a locker manager app (already did it on console) on win32api. The first build was in "win32 normal style code" (function definitions and declarations, no objects). It was a list view to show items and buttons to interact with it.
Now, this version works like a charm, I fill the listview via LVN_GETDISPINFO notify message and I have a split button which handles the BCN_DROPDOWN notify message as well.
After I saw it working, I wanted to make it encapsulating the windows and controls, and made simple wrappers for listview and button controls.
I handle the very same messages in the same way (LVN_GETDISPINFO and BCN_DROPDOWN notify messages) but in this case, the Listview seems disabled (no change of color), I just can't select anything!.
Now, if I remove the WM_NOTIFY message from the wndproc, and populate the list manually (without dispinfo), it works ok, but as soon as I add the WM_NOTIFY message to handle BCN_DROWDOWN for the split button, things go wrong again, can't select any item.
ListView.cpp:
#include "ListView.h"
#include <vector>
ListView::ListView()
:
hWnd(nullptr), x(0), y(0), width(0), height(0), id(0), nTotalItems(0)/*, lvi({0}), lvc({0})*/
{
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(icex);
icex.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&icex);
}
void ListView::Create(DWORD dwStyle, int x_in, int y_in, int width_in, int height_in, HWND hWndParent, int id_in)
{
assert(hWnd == nullptr);
x = x_in;
y = y_in;
width = width_in;
height = height_in;
id = id_in;
hWnd = CreateWindow(WC_LISTVIEW, "", dwStyle, x, y, width, height, hWndParent, (HMENU)id, GetModuleHandle(nullptr), nullptr);
assert(hWnd != 0);
}
BOOL ListView::InsertRows(int nRows)
{
LVITEM lvi = {};
lvi.mask = LVIF_TEXT | LVIF_STATE;
lvi.pszText = LPSTR_TEXTCALLBACK;
lvi.iSubItem = 0;
lvi.stateMask = 0;
lvi.state = 0;
for(int i = 0; i < nRows; ++i)
{
lvi.iItem = i;
if(ListView_InsertItem(hWnd, &lvi) == -1)
return FALSE;
}
return TRUE;
}
BOOL ListView::InsertColumns(int nCols)
{
LVCOLUMN lvc = {};
char textCol[] = "Columna";
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.cx = 100;
lvc.pszText = textCol;
lvc.fmt = LVCFMT_LEFT;
for(int i = 0; i < nCols; ++i)
{
lvc.iSubItem = i;
if(ListView_InsertColumn(hWnd, i, &lvc) == -1)
return FALSE;
}
return TRUE;
}
void ListView::SetSubItemText(int nRow, int nCol, const std::string& strText)
{
std::vector<char> tmpChar(strText.begin(), strText.end());
tmpChar.push_back('\0');
ListView_SetItemText(hWnd, nRow, nCol, &tmpChar[0]);
}
void ListView::SetSubItemText(int nRow, int nCol, char * szText)
{
ListView_SetItemText(hWnd, nRow, nCol, szText);
}
void ListView::SetExStyle(DWORD dwExStyle)
{
ListView_SetExtendedListViewStyle(hWnd, dwExStyle);
}
HWND ListView::Hwnd() const
{
return this->hWnd;
}
ListView.h
#ifndef _LISTVIEW_H_
#define _LISTVIEW_H_
#include "../stdafx.h"
#include <CommCtrl.h>
#include <cassert>
#include <string>
class ListView
{
public:
ListView();
void Create(DWORD dwStyle, int x_in, int y_in, int width_in, int height_in, HWND hWndParent, int id_in);
BOOL InsertRows(int nRows);
BOOL InsertColumns(int nCols);
void SetSubItemText(int nRow, int nCol, const std::string& strText);
void SetSubItemText(int nRow, int nCol, char* szText);
std::string GetSubItemText(int nRow, int nCol) const;
void SetExStyle(DWORD dwExStyle);
HWND Hwnd() const;
public:
private:
HWND hWnd;
int id;
int x;
int y;
int width;
int height;
int nTotalItems;
};
#endif
MainWindow.cpp
LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
{
OnCreate();
return 0;
}
break;
case WM_COMMAND:
{
if(HIWORD(wParam) == BN_CLICKED)
OnCommand(LOWORD(wParam));
return 0;
}
break;
case WM_NOTIFY:
{
OnNotify(lParam);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
break;
}
return TRUE;
}
void MainWindow::OnCreate()
{
lvMain.Create(WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT | LVS_ALIGNTOP | LVS_SHOWSELALWAYS | LVS_SINGLESEL,
11, 11, 438, 322, hWnd, ID_LISTVIEW_MAIN);
lvMain.SetExStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
btnAceptar.Create(IDS_ASIGNAR, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | BS_TEXT,
456, 11, hWnd, ID_BUTTON_ASIGNAR);
btnFiltro.Create(IDS_TODOS, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | BS_TEXT | BS_SPLITBUTTON,
456, 41, hWnd, ID_BUTTON_VERTODOS);
const int nColsWidths[] = { 50, 250, 80, 20, 20 };
lvMain.InsertColumns(3);
lvMain.InsertRows(3);
EnumChildWindows(hWnd, SetChildWndFontProc, (LPARAM)GetStockObject(DEFAULT_GUI_FONT));
}
void MainWindow::OnCommand(WORD wCmdId)
{
switch (wCmdId)
{
case ID_BUTTON_ASIGNAR:
{
MessageBox(Window(), "hola mundo!", "info", MB_OK);
}
break;
}
}
void MainWindow::OnNotify(LPARAM lParam)
{
switch ( ((LPNMHDR)lParam)->code)
{
case BCN_DROPDOWN:
{
if (((NMBCDROPDOWN*)lParam)->hdr.hwndFrom == btnFiltro.Hwnd())
{
RECT rcButton;
GetClientRect(btnFiltro.Hwnd(), &rcButton);
POINT pt;
pt.x = rcButton.left;
pt.y = rcButton.bottom;
ClientToScreen(btnFiltro.Hwnd(), &pt);
// Create a menu and add items.
HMENU hSplitMenu = CreatePopupMenu();
char szStringBuffer[255];
LoadStringA(GetModuleHandle(nullptr), IDS_ASIGNADOS, szStringBuffer, 255);
AppendMenu(hSplitMenu, MF_BYPOSITION, ID_MENU_VERASIGNADOS, szStringBuffer);
LoadStringA(GetModuleHandle(nullptr), IDS_SINASIGNAR, szStringBuffer, 255);
AppendMenu(hSplitMenu, MF_BYPOSITION, ID_MENU_VERSINASIGNAR, szStringBuffer);
// Display the menu.
TrackPopupMenu(hSplitMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_VERPOSANIMATION, pt.x, pt.y, 0, hWnd, NULL);
}
}
break;
case LVN_GETDISPINFO:
{
if (((LPNMHDR)lParam)->hwndFrom == lvMain.Hwnd() )
{
NMLVDISPINFO* plvdi = (NMLVDISPINFO*)lParam;
switch (plvdi->item.iSubItem)
{
case 0:
{
char buff[100];
strncpy_s(buff, "SubItem Index 0", 100);
plvdi->item.pszText = buff;
}
break;
case 1:
{
char buff[100];
strncpy_s(buff, "SubItem Index 1", 100);
plvdi->item.pszText = buff;
}
break;
case 2:
{
char buff[100];
strncpy_s(buff, "SubItem Index 2", 100);
plvdi->item.pszText = buff;
}
break;
default:
break;
}
}
}
break;
default:
break;
}
}
I'm expecting the same result as using funtcions and global variables to create and control the listview items and subitems. I don't know whats going wrong, Already tried to make the OnNotify handler static, but the same result, Items and subitems are there, but I cant select anything.
Hope you can help me, thank you for your help!.
By the way, this is the code that works ok:
Main.h
#ifndef _MAIN_H_
#define _MAIN_H_
//#define NDEBUG // for assert
//#define UNICODE
#define _WIN32_IE 0x0601
#define _WIN32_WINNT 0x0601
#include <sdkddkver.h>
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include <commctrl.h>
#include <cassert>
#include "LM/CLockManager.h"
#include "resource.h"
#define ID_LISTVIEW 1200
#define ID_BUTTON_ASIGNAR 1201
#define ID_BUTTON_VER_TODOS 1202
#define ID_BUTTON_BUSCAR 1203
#define ID_BUTTON_ELIMINAR 1204
#define ID_BUTTON_AGREGAR 1205
#define ID_MENU_VER_ASIGNADOS 1300
#define ID_MENU_VER_SIN_ASIGNAR 1301
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HWND CreateListView(HWND hWndParent);
HWND CreateButton(HWND hWndParent, const char* szBtnText, int x, int y, int id, DWORD dwStyle = 0);
BOOL InsertListViewColumns(HWND hWndLv_, int nColumns);
BOOL InsertListViewItem(HWND hWndLv_, unsigned int cItems);
BOOL InsertListViewRow(HWND hWndLv_, unsigned int nRows, unsigned int nCols);
void HandleWM_NOTIFY(LPARAM lParam);
#endif
WndProc.cpp
#include "main.h"
#include <string>
#include <vector>
#include <stdexcept>
char szDialogContent[256] = {};
HIMAGELIST hImage = nullptr;
HWND hWndTest = nullptr;
char columnas[3][255] =
{
{"Col 1"},
{"Col 2"},
{"Col 3"}
};
CLockManager manager("basedatos.txt");
std::vector<CLockers> lockers;
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CREATE:
{
manager.GetLockers(lockers, CLockers::Filter::All);
char szStringBuffer[256];
LoadString(GetModuleHandle(nullptr), IDS_ASIGNAR, szStringBuffer, 255);
HWND hWndBt = CreateButton(hWnd, szStringBuffer, 456, 11, ID_BUTTON_ASIGNAR);
LoadString(GetModuleHandle(nullptr), IDS_TODOS, szStringBuffer, 255);
HWND hWndBt2 = CreateButton(hWnd, szStringBuffer, 456, 41, ID_BUTTON_VER_TODOS, BS_SPLITBUTTON);
LoadString(GetModuleHandle(nullptr), IDS_BUSCAR, szStringBuffer, 255);
HWND hWndBt3 = CreateButton(hWnd, szStringBuffer, 456, 71, ID_BUTTON_BUSCAR);
LoadString(GetModuleHandle(nullptr), IDS_ELIMINAR, szStringBuffer, 255);
HWND hWndBt4 = CreateButton(hWnd, szStringBuffer, 456, 101, ID_BUTTON_ELIMINAR);
LoadString(GetModuleHandle(nullptr), IDS_AGREGAR, szStringBuffer, 255);
HWND hWndBt5 = CreateButton(hWnd, szStringBuffer, 456, 131, ID_BUTTON_AGREGAR);
HWND hWndLv = CreateListView(hWnd);
hImage = ImageList_Create(16, 16, ILC_COLOR32, 1, 1);
HICON hIcon = LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_ICONLOCKER));
ImageList_AddIcon(hImage, hIcon);
DestroyIcon(hIcon);
hIcon = LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_ICONDISABLED));
ImageList_AddIcon(hImage, hIcon);
DestroyIcon(hIcon);
ListView_SetImageList(hWndLv, hImage, LVSIL_SMALL);
BOOL ilvcResult = InsertListViewColumns(hWndLv, 5);
assert(ilvcResult == TRUE);
BOOL ilviResult = InsertListViewItem(hWndLv, lockers.size());
assert(ilviResult == TRUE);
EnumChildWindows(hWnd, SetFontProc, (LPARAM)GetStockObject(DEFAULT_GUI_FONT));
}
break;
case WM_COMMAND:
{
if(HIWORD(wParam) == BN_CLICKED)
{
switch(LOWORD(wParam))
{
case ID_BUTTON_VER_TODOS:
{
}
break;
case ID_MENU_VER_ASIGNADOS:
{
}
break;
case ID_MENU_VER_SIN_ASIGNAR:
{
}
break;
case ID_BUTTON_AGREGAR:
{
}
break;
case ID_BUTTON_BUSCAR:
{
}
break;
case ID_BUTTON_ASIGNAR:
{
}
break;
case ID_BUTTON_ELIMINAR:
{
}
break;
}
}
}
break;
case WM_NOTIFY:
switch( ((LPNMHDR)lParam)->code )
{
case LVN_GETDISPINFO:
{
if( ((LPNMHDR)lParam)->hwndFrom == GetDlgItem(hWnd, ID_LISTVIEW) )
{
HandleWM_NOTIFY(lParam);
}
}
break;
case BCN_DROPDOWN:
{
HWND hWndFiltro = GetDlgItem(hWnd, ID_BUTTON_VER_TODOS);
if( ((NMBCDROPDOWN*)lParam)->hdr.hwndFrom == hWndFiltro )
{
RECT rcButton;
GetClientRect(hWndFiltro, &rcButton);
POINT pt;
pt.x = rcButton.left;
pt.y = rcButton.bottom;
ClientToScreen(GetDlgItem(hWnd, ID_BUTTON_VER_TODOS), &pt);
// Create a menu and add items.
HMENU hSplitMenu = CreatePopupMenu();
char szStringBuffer[255];
LoadString(GetModuleHandle(nullptr), IDS_ASIGNADOS, szStringBuffer, 255);
AppendMenu(hSplitMenu, MF_BYPOSITION, ID_MENU_VER_ASIGNADOS, szStringBuffer);
LoadString(GetModuleHandle(nullptr), IDS_SINASIGNAR, szStringBuffer, 255);
AppendMenu(hSplitMenu, MF_BYPOSITION, ID_MENU_VER_SIN_ASIGNAR, szStringBuffer);
// Display the menu.
TrackPopupMenu(hSplitMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_VERPOSANIMATION, pt.x, pt.y, 0, hWnd, NULL);
}
}
break;
}
break;
case WM_DESTROY:
ImageList_Destroy(hImage);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
break;
}
return 0;
}
HWND CreateListView(HWND hWndParent)
{
RECT rcClient = {};
GetClientRect(hWndParent, &rcClient);
HWND hWndLv_ = CreateWindowEx(0, WC_LISTVIEW,
"", WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT |LVS_ALIGNTOP | LVS_SHOWSELALWAYS,
11,11, 438, 322,// rcClient.bottom - rcClient.top - 50,
hWndParent, (HMENU)ID_LISTVIEW, GetModuleHandle(nullptr), nullptr);
ListView_SetExtendedListViewStyle(hWndLv_, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
assert(hWndLv_ != nullptr);
return hWndLv_;
}
HWND CreateButton(HWND hWndParent, const char* szBtnText, int x, int y, int id, DWORD dwStyle)
{
HWND hWndBtn_ = CreateWindow("BUTTON", szBtnText,
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | dwStyle, x, y, 75, 23,
hWndParent, (HMENU)(long long)id, GetModuleHandle(nullptr), nullptr);
assert(hWndBtn_ != nullptr);
return hWndBtn_;
}
BOOL InsertListViewColumns(HWND hWndLv_, int nColumns)
{
LVCOLUMN lvc = {};
int iCol;
int wCols[] = {50, 250, 80, 20, 20};
LoadString(GetModuleHandle(nullptr), IDS_LOCKER, columnas[0], 255);
LoadString(GetModuleHandle(nullptr), IDS_USUARIO, columnas[1], 255);
LoadString(GetModuleHandle(nullptr), IDS_FECHA, columnas[2], 255);
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
for(iCol = 0; iCol < nColumns; ++iCol)
{
if(iCol < 1)
{
lvc.fmt = LVCFMT_RIGHT;
}
else
{
lvc.fmt = LVCFMT_LEFT;
}
lvc.iSubItem = iCol;
lvc.pszText = columnas[iCol];
lvc.cx = wCols[iCol];
if (ListView_InsertColumn(hWndLv_, iCol, &lvc) == -1)
return FALSE;
}
return TRUE;
}
BOOL InsertListViewItem(HWND hWndLv_, unsigned int cItems)
{
//HWND hWnd = GetParent(hWndLv_);
LVITEM lvi = {0};
lvi.pszText = LPSTR_TEXTCALLBACK;
lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
lvi.iSubItem = 0;
lvi.stateMask = 0;
lvi.state = 0;
//SendMessage((HWND)hWnd, WM_SETREDRAW, (WPARAM)FALSE, 0);
for(int i = 0; i < cItems; ++i)
{
lvi.iItem = i;
if(!lockers.at(i).Enabled())
lvi.iImage = 1;
else
lvi.iImage = 0;
if(ListView_InsertItem(hWndLv_, &lvi) == -1)
{
return FALSE;
}
}
return TRUE;
}
void HandleWM_NOTIFY(LPARAM lParam)
{
NMLVDISPINFO* plvdi;
switch ( ((LPNMHDR) lParam)->code )
{
case LVN_GETDISPINFO:
{
static constexpr size_t size = 256;
plvdi = (NMLVDISPINFO*)lParam;
switch(plvdi->item.iSubItem)
{
case 0:
{
std::string tmp = std::to_string( lockers.at(plvdi->item.iItem).GetLockerNumber());
char buff[size];
strncpy(buff, tmp.c_str(), size );
plvdi->item.pszText = buff;
}
break;
case 1:
{
std::string tmp = lockers.at(plvdi->item.iItem).GetAssignedUser();
char buff[size];
strncpy(buff, tmp.c_str(), size );
plvdi->item.pszText = buff;
}
break;
case 2:
{
std::string tmp = lockers.at(plvdi->item.iItem).GetDate();
char buff[size];
strncpy(buff, tmp.c_str(), size );
plvdi->item.pszText = buff;
}
break;
case 3:
{
char key[2][2] = {"N", "S"};
plvdi->item.pszText = key[lockers.at(plvdi->item.iItem).HasKey()];
}
break;
case 4:
{
char status[3][2] = {"B", "R", "M"};
plvdi->item.pszText = status[lockers.at(plvdi->item.iItem).GetStatusInt()];
}
break;
default:
break;
}
break;
}
}
}
Ok, I solved it.
Even though, MS Docs state that WM_NOTIFY doesn't return any value "except for notification messages that specify other wise", neither LVN_GETDISPINFO and BCN_DROPDOWN, return any value.
https://learn.microsoft.com/en-us/windows/win32/controls/wm-notify
https://learn.microsoft.com/en-us/windows/win32/controls/bcn-dropdown
https://learn.microsoft.com/en-us/windows/win32/controls/lvn-getdispinfo
So, WM_NOTIFY is supposed to not to return any value, right?....well, everything worked as soon as I added "return 0;" in the WM_NOTIFY case.
Everything is solved now :D I hope this can be useful for someone else
This is the only piece of code that was changed:
on MainWindow.cpp
case WM_NOTIFY:
{
OnNotify(lParam);
return 0;
}
break;
this is the working code in followup to my question. Thank you very much for your help. I hope is not too much code :D (it is the minimun I have, removed resource usage and other non relevant code for the problem):
Main.cpp
#include "stdafx.h"
#include "App.h"
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
App app;
return app.Run();
}
stdafx.h
#pragma once
#define _WIN32_WINNT 0X0601
#include <SDKDDKVer.h>
// Excluir material rara vez utilizado de encabezados de Windows
#ifdef UNICODE
#undef UNICODE
#endif
#ifdef _UNICODE
#undef _UNICODE
#endif
#define WIN32_LEAN_AND_MEAN
#define NORPC
#define NOMCX
#define NOTAPE
#define NOKERNEL
#define NOSERVICE
#define NOKANJI
#define NOPROFILER
#define NOPROXYSTUP
#define NOMINMAX
#define STRICT
// Archivos de encabezado de Windows
#include <windows.h>
#include <CommCtrl.h>
#pragma comment(lib, "ComCtl32.lib")
BaseWindow.h
#pragma once
#include "stdafx.h"
#include <cassert>
template <class DERIVED_T>
class BaseWindow
{
public:
BaseWindow() : hWnd(nullptr), hInst(GetModuleHandle(nullptr)), wcx({}) {}
BOOL Create(LPCSTR lpWindowName, DWORD dwStyle, DWORD dwExStyle = 0,
int x = CW_USEDEFAULT, int y = CW_USEDEFAULT, int width_in = CW_USEDEFAULT, int height_in = CW_USEDEFAULT,
HWND hWndParent = 0, HMENU hMenu = 0)
{
RECT wr = {};
wr.left = x;
wr.right = width_in + x;
wr.top = y;
wr.bottom = height_in + y;
AdjustWindowRect(&wr, dwStyle, FALSE);
width = wr.right - wr.left;
height = wr.bottom - wr.top;
wcx.cbSize = sizeof(wcx);
wcx.hInstance = hInst;
wcx.lpszClassName = ClassName();
wcx.lpfnWndProc = DERIVED_T::WndProc;
wcx.hbrBackground = (HBRUSH)COLOR_WINDOW;
wcx.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcx.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
ATOM aResult = RegisterClassEx(&wcx);
assert(aResult != 0);
hWnd = CreateWindowEx(
dwExStyle, ClassName(), lpWindowName, dwStyle,
x, y, width, height,
hWndParent, hMenu, hInst, this
);
assert(hWnd != 0);
return (hWnd ? TRUE : FALSE);
}
void Show(int nCmdShow) { ShowWindow(hWnd, nCmdShow); }
HWND Window() const { return hWnd; }
protected:
virtual LPCSTR ClassName() const = 0;
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
protected:
HWND hWnd;
HINSTANCE hInst;
int width;
int height;
WNDCLASSEX wcx;
public:
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DERIVED_T* pThis = nullptr;
if (uMsg == WM_NCCREATE)
{
CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
pThis = (DERIVED_T*)pCreate->lpCreateParams;
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
pThis->hWnd = hWnd;
}
else
{
pThis = (DERIVED_T*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
}
if (pThis)
{
return pThis->HandleMessage(uMsg, wParam, lParam);
}
else
{
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
};
MainWindow.h
#pragma once
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#include "BaseWindow.h"
#include "button.h"
#include "ListView.h"
class MainWindow : public BaseWindow<MainWindow>
{
public:
MainWindow();
public:
LPCSTR ClassName() const override { return "Window Example"; }
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
static BOOL CALLBACK SetChildWndFontProc(HWND hWndChild, LPARAM font);
private:
void OnCreate();
void OnCommand(WORD wCmdId);
void OnNotify(LPARAM lParam);
private:
enum CONTROLS_ID { ID_LISTVIEW_MAIN = 1200, ID_BUTTON_ASIGNAR, ID_BUTTON_VERTODOS, ID_BUTTON_BUSCAR,
ID_BUTTON_ELIMINAR, ID_BUTTON_AGREGAR, ID_MENU_VERASIGNADOS, ID_MENU_VERSINASIGNAR };
ListView lvMain;
Button btnAceptar;
Button btnFiltro;
char buff[3][255] = {
{"HELLO WORLD!"},
{"HOW ARE YOU?"},
{"FINE, THX!"}
};
};
Mainwindow.cpp
#pragma once
#include "MainWindow.h"
MainWindow::MainWindow()
: BaseWindow<MainWindow>()
{
}
LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
{
OnCreate();
return 0;
}
break;
case WM_COMMAND:
{
if(HIWORD(wParam) == BN_CLICKED)
OnCommand(LOWORD(wParam));
return 0;
}
break;
// here lies the problem
case WM_NOTIFY:
{
OnNotify(lParam);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
break;
}
return TRUE;
}
BOOL CALLBACK MainWindow::SetChildWndFontProc(HWND hWndChild, LPARAM font)
{
SendMessage(hWndChild, WM_SETFONT, (WPARAM)font, TRUE);
return TRUE;
}
void MainWindow::OnCreate()
{
lvMain.Create(WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT | LVS_ALIGNTOP | LVS_SHOWSELALWAYS | LVS_SINGLESEL,
11, 11, 438, 322, hWnd, ID_LISTVIEW_MAIN);
lvMain.SetExStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
btnAceptar.Create("Asign", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | BS_TEXT,
456, 11, hWnd, ID_BUTTON_ASIGNAR);
btnFiltro.Create("View all", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | BS_TEXT | BS_SPLITBUTTON,
456, 41, hWnd, ID_BUTTON_VERTODOS);
const int nColsWidths[] = { 50, 250, 80 };
lvMain.InsertColumns(3);
lvMain.InsertRows(3);
// Uncomment the following three lines to set manually the subitem text
// and comment the WM_NOTIFY message
// string casted to char* just for testing purposes
//lvMain.SetSubItemText(0, 0, std::string("HELLO WORLD!"));
//lvMain.SetSubItemText(1, 1, (char*)"HOW ARE YOU!?");
//lvMain.SetSubItemText(2, 2, std::string("FINE THX!"));
EnumChildWindows(hWnd, SetChildWndFontProc, (LPARAM)GetStockObject(DEFAULT_GUI_FONT));
}
void MainWindow::OnCommand(WORD wCmdId)
{
switch (wCmdId)
{
case ID_BUTTON_ASIGNAR:
{
MessageBox(Window(), "Hello hello!!", "info", MB_OK);
}
break;
}
}
void MainWindow::OnNotify(LPARAM lParam)
{
switch ( ((LPNMHDR)lParam)->code)
{
case BCN_DROPDOWN:
{
if (((NMBCDROPDOWN*)lParam)->hdr.hwndFrom == btnFiltro.Hwnd())
{
RECT rcButton;
GetClientRect(btnFiltro.Hwnd(), &rcButton);
POINT pt;
pt.x = rcButton.left;
pt.y = rcButton.bottom;
ClientToScreen(btnFiltro.Hwnd(), &pt);
// Create a menu and add items.
HMENU hSplitMenu = CreatePopupMenu();
AppendMenu(hSplitMenu, MF_BYPOSITION, ID_MENU_VERASIGNADOS, "menu item 1");
AppendMenu(hSplitMenu, MF_BYPOSITION, ID_MENU_VERSINASIGNAR, "menu item 2");
// Display the menu.
TrackPopupMenu(hSplitMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_VERPOSANIMATION, pt.x, pt.y, 0, hWnd, NULL);
}
}
break;
// parent window receives the LVN_GETDISPINFO msg and populates the list view
// in this example, 3 rows and 3 columns
case LVN_GETDISPINFO:
{
if (((LPNMHDR)lParam)->hwndFrom == lvMain.Hwnd() )
{
NMLVDISPINFO* plvdi = (NMLVDISPINFO*)lParam;
switch (plvdi->item.iSubItem)
{
case 0:
{
char buff[100];
strncpy_s(buff, "SubItem Index 0", 100);
plvdi->item.pszText = buff;
}
break;
case 1:
{
char buff[100];
strncpy_s(buff, "SubItem Index 1", 100);
plvdi->item.pszText = buff;
}
break;
case 2:
{
char buff[100];
strncpy_s(buff, "SubItem Index 2", 100);
plvdi->item.pszText = buff;
}
break;
default:
break;
}
}
}
break;
default:
break;
}
}
App.h
#pragma once
#include "MainWindow.h"
class App
{
public:
App();
App(const App&) = delete;
App& operator=(const App&) = delete;
~App();
int Run();
private:
HINSTANCE hInst;
MainWindow win;
};
App.cpp
#include "App.h"
App::App()
: hInst(GetModuleHandle(nullptr))
{
win.Create("My app", WS_OVERLAPPEDWINDOW, 0, CW_USEDEFAULT, CW_USEDEFAULT, 542, 343);
win.Show(SW_SHOWDEFAULT);
}
App::~App()
{
}
int App::Run()
{
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
Button.h
#ifndef _BUTTON_H_
#define _BUTTON_H_
#include "stdafx.h"
#include <cassert>
class Button
{
public:
Button();
void Create(LPCSTR lpszText, DWORD dwStyle, int x_in, int y_in, HWND hWndParent, int id_in);
void Create(int idString, DWORD dwStyle, int x_in, int y_in, HWND hWndParent, int id_in);
HWND Hwnd() const;
public:
private:
HWND hWnd;
int id;
int x;
int y;
static constexpr int width = 75;
static constexpr int height = 24;
static constexpr int nMaxLoadString = 100;
};
#endif
Button.cpp
#include "Button.h"
Button::Button()
:
hWnd(nullptr), x(0), y(0), id(0)
{
}
void Button::Create(LPCSTR lpszText, DWORD dwStyle, int x_in, int y_in, HWND hWndParent, int id_in)
{
// we don't want to create another button with the same object
assert(hWnd == nullptr);
x = x_in;
y = y_in;
id = id_in;
hWnd = CreateWindow("Button", lpszText, dwStyle, x, y, width, height, hWndParent, (HMENU)id, GetModuleHandle(nullptr), 0);
assert(hWnd != 0);
}
void Button::Create(int idString, DWORD dwStyle, int x_in, int y_in, HWND hWndParent, int id_in)
{
char szButtonName[nMaxLoadString] = {};
LoadString(GetModuleHandle(nullptr), idString, szButtonName, nMaxLoadString);
Create(szButtonName, dwStyle, x_in, y_in, hWndParent, id_in);
}
HWND Button::Hwnd() const
{
return hWnd;
}
ListView.h
#ifndef _LISTVIEW_H_
#define _LISTVIEW_H_
#include "stdafx.h"
#include <CommCtrl.h>
#include <cassert>
#include <string>
class ListView
{
public:
ListView();
void Create(DWORD dwStyle, int x_in, int y_in, int width_in, int height_in, HWND hWndParent, int id_in);
BOOL InsertRows(int nRows);
BOOL InsertColumns(int nCols);
BOOL InsertColumn(int iCol, char* szText, int nWidthCol = 50);
void SetSubItemText(int nRow, int nCol, const std::string& strText);
void SetSubItemText(int nRow, int nCol, char* szText);
std::string GetSubItemText(int nRow, int nCol) const;
void SetExStyle(DWORD dwExStyle);
HWND Hwnd() const;
public:
private:
HWND hWnd;
int id;
int x;
int y;
int width;
int height;
int nTotalItems;
};
#endif
ListView.cpp
#include "ListView.h"
#include <vector>
ListView::ListView()
:
hWnd(nullptr), x(0), y(0), width(0), height(0), id(0), nTotalItems(0)
{
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(icex);
icex.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&icex);
}
void ListView::Create(DWORD dwStyle, int x_in, int y_in, int width_in, int height_in, HWND hWndParent, int id_in)
{
// we don't want to create another listview with the same object
assert(hWnd == nullptr);
x = x_in;
y = y_in;
width = width_in;
height = height_in;
id = id_in;
hWnd = CreateWindow(WC_LISTVIEW, "", dwStyle, x, y, width, height, hWndParent, (HMENU)id, GetModuleHandle(nullptr), nullptr);
assert(hWnd != 0);
}
BOOL ListView::InsertRows(int nRows)
{
LVITEM lvi = {};
lvi.mask = LVIF_TEXT | LVIF_STATE;
// remove LPSTR_TEXTCALLBACK and replace with a non const string if you want
// to manually insert the items and subitems
lvi.pszText = LPSTR_TEXTCALLBACK;
lvi.iSubItem = 0;
lvi.stateMask = 0;
lvi.state = 0;
for(int i = 0; i < nRows; ++i)
{
lvi.iItem = i;
if(ListView_InsertItem(hWnd, &lvi) == -1)
return FALSE;
}
return TRUE;
}
BOOL ListView::InsertColumns(int nCols)
{
LVCOLUMN lvc = {};
char textCol[] = "Test name"; // test name for the columns
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.cx = 100; // hardcoded for testing purposes
lvc.pszText = textCol;
lvc.fmt = LVCFMT_LEFT;
for(int i = 0; i < nCols; ++i)
{
lvc.iSubItem = i;
if(ListView_InsertColumn(hWnd, i, &lvc) == -1)
return FALSE;
}
return TRUE;
}
// another method to insert columns
BOOL ListView::InsertColumn(int iCol, char * szText, int nWidthCol)
{
LVCOLUMN lvc = {};
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.cx = nWidthCol;
lvc.pszText = szText;
lvc.iSubItem = iCol;
lvc.fmt = LVCFMT_LEFT;
if (ListView_InsertColumn(hWnd, iCol, &lvc) == -1)
return FALSE;
return TRUE;
}
void ListView::SetSubItemText(int nRow, int nCol, const std::string& strText)
{
std::vector<char> tmpChar(strText.begin(), strText.end());
tmpChar.push_back('\0');
ListView_SetItemText(hWnd, nRow, nCol, &tmpChar[0]);
}
void ListView::SetSubItemText(int nRow, int nCol, char * szText)
{
ListView_SetItemText(hWnd, nRow, nCol, szText);
}
void ListView::SetExStyle(DWORD dwExStyle)
{
ListView_SetExtendedListViewStyle(hWnd, dwExStyle);
}
HWND ListView::Hwnd() const
{
return this->hWnd;
}