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'
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));
}
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;
}