Dynamically generate labels- MFC - mfc

In my MFC project, I want to generate the labels dynamically.
Eg:
I have to generate 4 edit controls and corresponding labels for them- say "Label1" "Label2"....
CStatic *label[MAX_THREAD];
for (int i=0; i< dynamic_number ; i++)
{
label[i] = new CStatic;
label[i]->Create(L"Name_of_label", WS_CHILD | WS_VISIBLE,
CRect(10, (10+i*30), 70, (30+i*30)), this);
}
I want "Label1" "Label2"... etc in place of "Name_of_label"
I am not very sure how to go about it.
Thanks in advance,

How about something like this:
CString str;
CStatic *label[MAX_THREAD];
for (int i = 0; i < dynamic_number ; i++)
{
str.Format("Label%d", i);
label[i] = new CStatic;
label[i]->Create(str,
WS_CHILD | WS_VISIBLE,
CRect(10, (10+i*30), 70, (30+i*30)),
this);
}

Related

Why would LVSCW_AUTOSIZE_USEHEADER stretch my first column to match width of listview control's width?

I'm working with Comctl32.dll's list-view control and attempting to divide the width of it amongst 10 columns, auto-sizing to match the strings in their respective headers. I am using the ListView_SetColumnWidth macro with the LVSCW_AUTOSIZE_USEHEADER value, and it's working with every column except the first one:
Here's the code I think is relevant, but please let me know if it's not enough:
HWND hListView;
hListView = CreateWindowEx(
WS_EX_CLIENTEDGE,
WC_LISTVIEW,
L"",
WS_CHILD | WS_VISIBLE |
LVS_REPORT,
200, 10, 800, 150,
hwnd,
(HMENU)IDC_MAIN_LV,
GetModuleHandle(NULL),
NULL);
if (hListView == NULL) {
MessageBox(hwnd, L"Could not create list box.", L"Error!", MB_OK | MB_ICONERROR);
}
LVCOLUMN lvc;
WCHAR buffer[256];
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
for (int col = 0; col < C_COLUMNS; col++) {
lvc.iSubItem = col;
lvc.pszText = buffer;
lvc.cx = 100;
lvc.fmt = LVCFMT_CENTER;
LoadString(
GetModuleHandle(NULL),
IDS_HP + col,
buffer,
sizeof(buffer));
if (ListView_InsertColumn(hListView, col, &lvc) == -1) {
MessageBox(hwnd, L"Could not create list box.", L"Error!", MB_OK | MB_ICONERROR);
}
ListView_SetColumnWidth(hListView, col, LVSCW_AUTOSIZE_USEHEADER);
}
SetFocus(hListBox);
This is my first project after learning the basics of C++ so I'm sure I'm doing everything shown here the worst way possible. (: Specifically, I'd like to know what's causing this particular error please. Thanks for reading!
From the ListView_SetColumnWidth documentation:
LVSCW_AUTOSIZE_USEHEADER
Automatically sizes the column to fit the header text. If you use this value with the last column, its width is set to fill the remaining width of the list-view control.
When you use this for the first time, there's only one column, so it is the last column and therefore gets resized to the full width of the listview.
The solution is to add all your columns first and then autosize them.
for (int col = 0; col < C_COLUMNS; col++) {
lvc.iSubItem = col;
lvc.pszText = buffer;
lvc.cx = 100;
lvc.fmt = LVCFMT_CENTER;
LoadString(
GetModuleHandle(NULL),
IDS_HP + col,
buffer,
sizeof(buffer));
ListView_InsertColumn(hListView, col, &lvc);
}
for (int col = 0; col < C_COLUMNS; col++) {
ListView_SetColumnWidth(hListView, col, LVSCW_AUTOSIZE_USEHEADER);
}

How to Properly Delete a row of Control and dynamically create a new one in that position?

My goal is to create a row of dynamic controls every time i click the Add button (on run-time), like this:
| Combo box | |Add Button|
|Static Ctrl| |Edit Ctrl| |Edit Ctrl| |Edit Ctrl| |Delete Button|
|Static Ctrl| |Edit Ctrl| |Edit Ctrl| |Edit Ctrl| |Delete Button|(*)
|Static Ctrl| |Edit Ctrl| |Edit Ctrl| |Edit Ctrl| |Delete Button|
and for example if i clicked on the Delete Button(*) , it will delete the whole row (including that Delete button). Then after that when click Add again, the newly created row will appeared at the same position, or even better if i could make all the rows below to move up, an the newly added row will appeared at the bottom.
Here are some code i wrote:
int CSettingDlg::Getid() // increase the id by 1 each time it was called
{
id = id + 1; // int id = 4000 in the '.h' file
return id;
}
int CSettingDlg::AddControlSet() // Add a row of control
{
int index = 0;
indexStr.Format(_T("%d"), index + 1);
GetDlgItem(IDC_TEST1)->GetWindowRect(&rcCtrl);
ScreenToClient(&rcCtrl);
for (;;)
{
rcCtrl.top = rcCtrl.top + index * 35;
rcCtrl.bottom = rcCtrl.bottom + index * 35;
StaticText = new CStatic;
EditBox = new CEdit;
EditBox2 = new CEdit;
EditBox3 = new CEdit;
Delete = new CButton;
StaticText->Create((indexStr), WS_CHILD | WS_VISIBLE | ES_READONLY | SS_NOTIFY, CRect(rcCtrl.left -= 163, rcCtrl.top += 5, rcCtrl.right -= 270, rcCtrl.bottom), this, Getid());
EditBox->Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(rcCtrl.left += 28, rcCtrl.top -= 5, rcCtrl.right += 134, rcCtrl.bottom), this, Getid());
EditBox2->Create(WS_CHILD | WS_VISIBLE | WS_BORDER | ES_READONLY, CRect(rcCtrl.left += 135, rcCtrl.top, rcCtrl.right += 136, rcCtrl.bottom), this, Getid());
EditBox3->Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(rcCtrl.left += 135, rcCtrl.top, rcCtrl.right += 172, rcCtrl.bottom), this, Getid());
Delete->Create(_T("Del"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, CRect(rcCtrl.left += 191, rcCtrl.top, rcCtrl.right += 101, rcCtrl.bottom), this, Getid());
index++;
return TRUE;
nCount++;
}
}
And here is my basic idea to delete the row:
void CSettingDlg::OnBnClickedDeleteSettingDlg(UINT nID)
{
nID == Getid();
switch (nID)
{
case 3005: //3005 is the 1st Delete Button's ID
for (; nID > 3000; nID--)
GetDlgItem(nID)->DestroyWindow(); //destroy all controls that have ID from 3001 to 3005
nCount--; //This variable is not relevant
break;
case 3010: //The 2nd Delete Button's ID
...
}
I know my codes are pretty bad, so if anybody have a solution, hint to my question or at least know to make my code a little better i would be very appreciated.
You can use a vector to dynamically add/remove buttons. Note that each new CWnd pointer needs a corresponding delete to avoid resource leaks, so make sure to clean up when buttons are deleted. For simplicity you can put the controls for each row in a structure, as shown in the example below. You also have to reposition the controls when a row is added or deleted.
struct control_set
{
CStatic st;
CEdit e1, e2, e3;
CButton bn;
};
std::vector<control_set*> vec;
CSettingDlg::~CSettingDlg()
{
//for(auto &e : vec) delete e; <- remove this
}
void CSettingDlg::PostNcDestroy() // <- add this
{
CDialog::PostNcDestroy();
for(auto &e : vec)
delete e;
vec.clear();
}
void CSettingDlg::AddControlSet()
{
vec.push_back(new control_set);
vec.back()->st.Create(_T("text"), WS_CHILD | WS_VISIBLE, CRect(0, 0, 0, 0), this, 0);
vec.back()->e1.Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(0, 0, 0, 0), this, 0);
vec.back()->e2.Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(0, 0, 0, 0), this, 0);
vec.back()->e3.Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(0, 0, 0, 0), this, 0);
vec.back()->bn.Create(_T("del"), WS_CHILD | WS_VISIBLE, CRect(0, 0, 0, 0), this, 0);
resize_controls();
}
void CSettingDlg::OnBnClickedDeleteSettingDlg(UINT id)
{
UINT row = id / 100;
//add more checks to make sure this is the ID from delete buttons
if(row < 0 || row >= vec.size()) return;
delete vec[row];
vec.erase(vec.begin() + row);
resize_controls();
}
void CSettingDlg::resize_controls()
{
CRect rc(10, 20, 10 + 50, 20 + 14);
MapDialogRect(&rc);
for(size_t i = 0; i < vec.size(); i++)
{
std::vector<CWnd*> tmp{
&vec[i]->st, &vec[i]->e1, &vec[i]->e2, &vec[i]->e3, &vec[i]->bn};
CRect r = rc;
for(size_t j = 0; j < tmp.size(); j++)
{
tmp[j]->MoveWindow(r);
tmp[j]->SetDlgCtrlID(i * 100 + j);
tmp[j]->SetFont(GetFont());
r.OffsetRect(rc.Width() + 2, 0);
}
rc.OffsetRect(0, rc.Height() + 2);
}
}
And you want to add message handler for delete buttons:
ON_COMMAND_RANGE(4, 904, OnBnClickedDeleteSettingDlg)
Alternatively you can set the position for the control as follows. First, put 5 dummy static controls in the dialog resource, with the following IDs:
| IDC_REF_STATIC | IDC_REF_EDIT1 | IDC_REF_EDIT2 | IDC_REF_EDIT3 | IDC_REF_BUTTON |
These controls can be hidden.
Then use the coordinates of these dummy controls to position the new controls. You can just offset the rectangle to go to the next row.
void CSettingDlg::resize_controls()
{
CWnd *st = GetDlgItem(IDC_REF_STATIC);
CWnd *e1 = GetDlgItem(IDC_REF_EDIT1);
CWnd *e2 = GetDlgItem(IDC_REF_EDIT2);
CWnd *e3 = GetDlgItem(IDC_REF_EDIT3);
CWnd *bn = GetDlgItem(IDC_REF_BUTTON);
ASSERT(st && e1 && e2 && e3 && bn);
CRect r, rc;
st->GetWindowRect(&rc);
ScreenToClient(&rc);
for(size_t i = 0; i < vec.size(); i++)
{
//reposition the static control
st->GetWindowRect(&r);
ScreenToClient(&r);
r.MoveToY(rc.top);
vec[i]->st.MoveWindow(r);
//edit1
e1->GetWindowRect(&r);
ScreenToClient(&r);
r.MoveToY(rc.top);
vec[i]->e1.MoveWindow(r);
//edit2
e2->GetWindowRect(&r);
ScreenToClient(&r);
r.MoveToY(rc.top);
vec[i]->e2.MoveWindow(r);
//edit3
e3->GetWindowRect(&r);
ScreenToClient(&r);
r.MoveToY(rc.top);
vec[i]->e3.MoveWindow(r);
//button
bn->GetWindowRect(&r);
ScreenToClient(&r);
r.MoveToY(rc.top);
vec[i]->bn.MoveWindow(r);
//move rc down by one row
rc.OffsetRect(0, rc.Height() + 2);
//Set the font for each control
//Also set id for each control, based on the row
vec[i]->st.SetDlgCtrlID(i * 100 + 1);
vec[i]->st.SetFont(GetFont());
vec[i]->e1.SetDlgCtrlID(i * 100 + 2);
vec[i]->e1.SetFont(GetFont());
vec[i]->e2.SetDlgCtrlID(i * 100 + 3);
vec[i]->e2.SetFont(GetFont());
vec[i]->e3.SetDlgCtrlID(i * 100 + 4);
vec[i]->e3.SetFont(GetFont());
vec[i]->bn.SetDlgCtrlID(i * 100 + 5);
vec[i]->bn.SetFont(GetFont());
}
}

Dealing with a table of buttons (WinAPI)

I'm quite new to WinAPI, so I sometimes make a lot of basic mistakes. You've been warned, let's move to my problem :P/
I want to make something like a grid. If the user click one of the grid's fields, there should appear a bitmap. I've made a bitmap of field that I want to use as a button. At the start user inputs a size of the grid, so I've made a dynamic library of buttons with that bitmap. Unfortunately, I have no idea how to deal with them when they are clicked. Here's my code:
//there I create my window. I also make a global variable bool* table and HWND next.
table = new bool[x*y];
for (int i = 0; i < x*y; ++i)
table[i] = 0;
//the table represents if the fields are already filled or not.
HWND* buttons = new HWND[x*y];
next = CreateWindowEx(4, _T("BUTTON"), _T("MOVE"), WS_CHILD | WS_VISIBLE, x * 12 - 25, (y + 4) * 25 - 90, 100, 50, hwnd, NULL, hThisInstance, NULL);
for (int i = 0; i < x; ++i)
{
for (int j = 0; j < y; ++j)
{
buttons[i + j * x] = CreateWindowEx(0, _T("BUTTON"), NULL, WS_CHILD | WS_VISIBLE | BS_BITMAP, 0 + i * 25, 0 + j * 25, 25, 25, hwnd, NULL, hThisInstance, NULL);
SendMessage(przyciski[i + j * x], BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)field);
}
}
And now in WindowProcedure():
switch (message)
{
case WM_COMMAND:
if ((HWND)lParam == next) /* there will be some code in the future ;) */;
else
{
//So here I need to set correct the value in table to 1
//I have a handle to clicked button (HWND)lParam, but I don't know how to get it's position in the table
}
break;
I tried to do some struct with HWND and int x, y, but I still don't know how to manage this. BTW, the code might look very old, I need to create an app that Windows XP can run (it's a project for school, do not inquire :P) and in addition I use a very old tutorial.
Assign an ID for each button using the HMENU parameter. The ID cannot be zero, therefore add an arbitrary offset, for example 100:
buttons[i + j * x] = CreateWindowEx(0, _T("BUTTON"), NULL,
WS_CHILD | WS_VISIBLE | BS_BITMAP, 0 + i * 25, 0 + j * 25, 25, 25,
hwnd, HMENU(100 + i + j * x), hThisInstance, NULL);
You can also extract the row and column from button_index, knowing the total rows and total columns. Example:
case WM_COMMAND:
{
if (HIWORD(wParam) == BN_CLICKED)
{
int id = LOWORD(wParam);
int button_index = id - 100;
if (button_index >= 0 && button_index < x * y)
{
int row = button_index / x;
int column = button_index % x;
...
}
...
}
break;
}

c++ ListView - Cannot insert new items after using ListView_DeleteAllItems

I have a ListView control with 4 columns that is initialized in the WM_CREATE proc.
hListView1 = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, NULL, WS_CHILD|WS_VSCROLL|WS_HSCROLL|WS_VISIBLE|LVS_REPORT|LVS_SHOWSELALWAYS, 230, 20, 300, 250, hwnd, (HMENU)ID_EDIT1, GetModuleHandle(NULL), NULL);
ListView_SetExtendedListViewStyle(hListView1, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP);
lvCol.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvCol.fmt = LVCFMT_LEFT;
lvCol.iSubItem=0;
lvCol.cx=30;
lvCol.pszText="";
ListView_InsertColumn(hListView1, 0, &lvCol);
lvCol.iSubItem=1;
lvCol.cx=150;
lvCol.pszText="Name";
ListView_InsertColumn(hListView1, 1, &lvCol);
lvCol.iSubItem=2;
lvCol.cx=50;
lvCol.pszText="Size";
ListView_InsertColumn(hListView1, 2, &lvCol);
lvCol.iSubItem=3;
lvCol.cx=80;
lvCol.pszText="Modified";
ListView_InsertColumn(hListView1, 3, &lvCol);
Then i have a function that will insert the items (it works fine until i call deleteallitems)
...
LVITEM lvItem;
j = 0;
while(FindNextFile(hFind,&FindFileData)){
lvItem.iItem = j;
lvItem.iImage = 1;
if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
lvItem.iImage = 0;
}
ListView_InsertItem(hListView1, &lvItem);
ListView_SetItemText(hListView1, j, 1, FindFileData.cFileName);
ListView_SetItemText(hListView1, j, 2, msg1);
ListView_SetItemText(hListView1, j, 3, msg2);
j++;
}
But then whenever i call
ListView_DeleteAllItems(hListView1);
if after i call my function that insert items, my listview is cleared (the columns are still there) but no new items are insered..
I heard about indexes that are not cleared but i couldnt figured it out.
Thanks in advance ;-)
Solution :
Added
lvItem.mask = LVIF_IMAGE | LVIF_STATE;
lvItem.state = 0;
lvItem.stateMask = 0;
lvItem.iSubItem = 0;
You are not setting lvItem.mask, so ListView_InsertItem doesn't know which fields are valid and which aren't.
Try something like this:
...
LVITEM lvItem;
lvItem.mask = LVIF_IMAGE | LVIF_DI_SETITEM;
j = 0;
...

Specific docking to the frame

Now my bars located on frame thanks to nDockBarID = AFX_IDW_DOCKBAR_LEFT
ForcesBar* m_forcesBar[3];
for (int i=0; i<3; i++)
{
m_forcesBar[i]->SetBarStyle(m_forcesBar[i]->GetBarStyle() |
CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
m_forcesBar[i]->EnableDocking(CBRS_ALIGN_ANY);
}
pMainFrame->EnableDocking(CBRS_ALIGN_ANY);
pMainFrame->DockControlBar(m_forcesBar[0], AFX_IDW_DOCKBAR_LEFT);
pMainFrame->DockControlBar(m_forcesBar[1], AFX_IDW_DOCKBAR_LEFT);
pMainFrame->DockControlBar(m_forcesBar[2], AFX_IDW_DOCKBAR_LEFT);
While I want to receive such bar combination:
I think that it will be good to use lpRect in
void DockControlBar(
CControlBar* pBar,
UINT nDockBarID = 0,
LPCRECT lpRect = NULL
);
but unfortunately it does not work.
Can you give code example for making such specific doking (secon picture).
(In project I use CSizingControlBar http://www.datamekanix.com/sizecbar/manual.html)
I tested it and using rectangle works fine.
One difference is that in my test app I have declared m_forcesBar in MainFrm.h as
ForcesBar m_forcesBar[3];
Avoiding allocation on the heap and remembering memory release.
The code I used (snippet from OnCreate):
EnableDocking(CBRS_ALIGN_ANY);
m_wndToolBar1.EnableDocking(CBRS_ALIGN_ANY);
m_wndToolBar2.EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar1);
DockControlBar(&m_wndToolBar2);
CRect rectWnd;
GetClientRect(rectWnd);
ClientToScreen(rectWnd);
for(int iIndx = 0; iIndx < 3; iIndx++)
{
if (!m_forcesBar[iIndx].Create(_T(""), this, 120 + iIndx))
{
TRACE0("Failed to create mybar\n");
return -1; // fail to create
}
m_forcesBar[iIndx].SetBarStyle(m_forcesBar[iIndx].GetBarStyle() |
CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
m_forcesBar[iIndx].EnableDocking(CBRS_ALIGN_LEFT);
DockControlBar(&m_forcesBar[iIndx], AFX_IDW_DOCKBAR_LEFT, rectWnd);
}
That is it.