No LVIF_TEXT in LVN_GETDISPINFO message using LVS_OWNERDATA mode - c++

I've encountered strange behavior in MFC when using list control (CListCtrl) with LVS_REPORT style in "virtual mode" i.e. with LVS_OWNERDATA style. List view itself is placed on a modeless dialog.
In dialog's OnInitDialog method I add two columns -- "Column 1" and "Column 2" in two different ways -- using LVCOLUMN and ListView_InsertColumn macro (1) and using CHeaderCtrl class with HDITEM structure (2).
When using first method (1), in LVN_GETDISPINFO message handler (message is handled by dialog, simple ON_NOTIFY) I receive a mask (NMLVDISPINFO.item.mask member) with different bits set (LVIF_TEXT, LVIF_IMAGE, LVIF_STATE, LVIF_INDENT etc.), after filling appropriate fields in NMLVDISPINFO.item structure, everything works fine.
But, when using second method (2) involving CHeaderCtrl class, the only bit in mask that is set is LVIF_INDENT, I never receive mask with anything else set.
Here are pieces of code that I use to add those columns:
Method one (1):
LVCOLUMN col;
col.mask = LVCF_TEXT | LVCF_WIDTH;
col.pszText = _T("Column 1");
col.cx = 100;
ListView_InsertColumn(m_MyList, 0, &col); //m_MyList is of CListCtrl type
col.pszText = _T("Column 2");
ListView_InsertColumn(m_MyList, 1, &col);
And method two (2):
HDITEM hdItem;
hdItem.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
hdItem.fmt = HDF_STRING;
hdItem.cxy = 100;
hdItem.pszText = _T("Column 1");
hdItem.cchTextMax = _tcslen(hdItem.pszText);
m_MyList.GetHeaderCtrl()->InsertItem(0, &hdItem);
others_info.pszText = _T("Column 2");
others_info.cchTextMax = _tcslen(hdItem.pszText);
m_MyList.GetHeaderCtrl()->InsertItem(1, &hdItem);
What could possibly cause such behavior?

Method 2 is incorrectly using others_info for some operations but not others. It looks like the first InsertItem is using a text length that is possibly 0. The second call to InsertItem reuses the same hdItem structure which hasn't been changed since the first call.

Related

MFC CListCtrl - how to move item text to the left

I'm trying to implement single-column CListCtrl (or CMFCListCtrl, doesn't matter) in a way, that some rows might have checkboxes and some might not (I don't want to use neither CListBox, nor CCheckListBox, because in the future I'm planning to use multiple columns). I'm using LVS_EX_CHECKBOXES style, but that forces every item to have a checkbox. Then I manually delete the checkbox with a custom draw handler, but then I'm having trouble moving the item's text to the left side so that it takes place of the erased checkbox.
This is what my list control looks like:
But I need it to look like this (item2 is aligned to the left border, taking place of the erased checkbox):
I create my list control dynamically like this:
list->Create(WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT | LVS_NOCOLUMNHEADER
rect, this, SOME_ID);
list->SetExtendedStyle(list->GetExtendedStyle() | LVS_EX_CHECKBOXES);
And my custom draw handler function looks like this:
I create my list control dynamically like this:
void MyCListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
*pResult = CDRF_DODEFAULT;
LPNMLVCUSTOMDRAW lpn = (LPNMLVCUSTOMDRAW)pNMHDR;
if (CDDS_PREPAINT == lpn->nmcd.dwDrawStage)
{
*pResult = CDRF_NOTIFYITEMDRAW; // get notification for every row (item)
}
else if (CDDS_ITEMPREPAINT == lpn->nmcd.dwDrawStage)
{
int row = lpn->nmcd.dwItemSpec;
if (row == 1) { // we are in the first row (item2)
lpn->nmcd.rc.left -= 20; // doesn't do anything
lpn->rcText.left -= 20; // doesn't do anything
// this erases checkbox from the current row
SetItemState(row, INDEXTOSTATEIMAGEMASK(0), LVIS_STATEIMAGEMASK);
}
}
}
Is there any way to achieve the desired result? Am I doing it the right way, or is it better to use CListCtrl without the LVS_EX_CHECKBOXES and draw the checkboxes myself where I want to? If so, how? Thanks in advance.
You could try indentation with minus value (I didn't tried though):
LV_ITEM lvItem;
lvItem.iItem = nYourItem;
lvItem.iSubItem = 0;
lvItem.mask = LVIF_INDENT;
lvItem.iIndent = -1;
VERIFY(SetItem(&lvItem));
If it would be an option for you, why don't you just leave it as it is?
The first version looks much more uncluttered, anyway.
The second version breaks guidance of the eyes.

GTK+2.0 Combo Box - How to get the position of an entry?

#include <gtk/gtk.h>
//Defined a comboBox like this
combo = gtk_combo_box_new_text();
//Added entries like this
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "string");
//How to get a particular entry deleted from the list of entries in the ComboBox combo?
How do I delete a particular entry in the comboBox? For example - The ComboBox dropdown has entries like "Foo", "Boo", "Hoo" etc. Now I want to select "Boo" and then remove it? How do I do that ?
//Segmentation fault while removal error
mcve -
fixed = gtk_fixed_new();
combo = gtk_combo_box_new_text();
gtk_fixed_put(GTK_FIXED(fixed), combo, 50, 50);
label = gtk_label_new("Rooms");
gtk_fixed_put(GTK_FIXED(fixed), label, 50, 30 );
gtk_table_attach_defaults (GTK_TABLE (table), fixed, 2, 3, 0, 2);
g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(combo_selected), (gpointer) label);
In this callback function I just copy the current selection to a globally declared string. Next, in some other function I try to run this -
gtk_combo_box_remove_text (GTK_COMBO_BOX(combo), gtk_combo_box_get_active (GTK_COMBO_BOX(combo))); //statement where seg fault occurs
The gtk_combo_box_get_active gives me the appropriate index entry to be removed. Note - GtkWidget *combo is globally defined in the program.
The id of the active(that is currently selected) entry in a combo box can be obtained via gtk_combo_box_get_active and it can be removed with help of gtk_combo_box_text_remove or gtk_combo_box_remove_text
(Depending on which version of the api is used. The former(gtk_combo_box_text_remove) is more up to date).
gtk_combo_box_get_active returns -1 if there is no active item and should therefore be checked before passing it to one of the remove functions. This is especially important if, as in the case presented above, the removing happens in a signal handler. According to the documentation the "changed" signal
[...] is emitted when the active item is changed. This can be due to the user selecting a different item from the list, or due to a call to gtk_combo_box_set_active_iter(). It will also be emitted while typing into the entry of a combo box with an entry. [...]
As far as i understand this includes when the item is deselected(which will presumably happen if it is deleted).
Your handler should therefore look somehow like this:
void combo_selected(GtkComboBox *widget, gpointer user_data)
{
int active_id=gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
if(active_id!=-1)
{
//do real work...
}
}

MFC RIbbon - Multiple CMFCRibbonComboBox on the same panel, respond to selectitem action performed on any onComboBox

I have 2 CMFCRibbonComboBox on the same Panel in a Ribbon- Eg:
CMFCRibbonComboBox *individualComputers =
new CMFCRibbonComboBox(-1,FALSE, 100, "Individual Computers", -1);
individualComputers->AddItem("Computer 1");
individualComputers->AddItem("Computer 2");
individualComputers->AddItem("Computer 3");
individualComputers->SelectItem(0);
CMFCRibbonComboBox * groupNames =
new CMFCRibbonComboBox (-1, FALSE, 100, "Computer Group Names", -1);
groupNames->AddItem("GROUP 1");
groupNames->AddItem("GROUP 2");
groupNames->AddItem("GROUP 3");
groupNames->SelectItem(0);
CMFCRibbonPanel* pComputerGroups = cComputerGroups->AddPanel("All Groups");
//cComputerGroups is a Category
pComputerGroups->Add(individualComputers);
pComputerGroups->Add(groupNames);
The problem is that , when I select "Group 1" in the groupNames comboBox from the UI(USer Interface) , then even "Computer 1" from the group individualComputers is selected. How do I make each combobox group independent of the other? Thanks.
I suspect you didn't want add your combobox to itself individualComputers->Add(individualComputers); should perhaps be pComputerGroups->Add(individualComputers);
Otherwise your bug is probably elsewhere in your command or updateUI handling code not shown. This is likely as you are using the same ID of -1 to identify both combo boxes.
Furthermore there is no overloaded constructor for CMFCRibbonComboBox which takes an additional two parameters as you have shown for groupNames.
In the future please show actual code which follows SSCCE
Edit: Made previously unaddressed comment bold as it is likely to be your remaining issue. Consider using const UINT CB_COMP_ID = 1; and const UINT CB_GROUP_ID = 2; then you can reference each combo box separately using CB_COMP_ID or CB_GROUP_ID in the message map etc.

Set the size of a window in MFC application

This question may be considered as naive for many users but I am new to MFC and I need an indication. I want to set a specific size to a SDI window but I fail to do it. I try the code in the CMainFrame class:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWndEx::PreCreateWindow(cs) )
return FALSE;
cs.style = WS_OVERLAPPED | WS_SYSMENU | WS_BORDER;
cs.cy = 600;
cs.cx = 600;
cs.y = ((cs.cy * 3) - cs.cy) / 2;
cs.x = ((cs.cx * 3) - cs.cx) / 2;
return CFrameWnd::PreCreateWindow(cs);
}
The window still keeps its size though without any change.
As far as I am concerned, I can override the respective function in CWnd class to get to the same result. Given that I have not used the CWnd class till now in my program, how can I do that?
Move your code above the call of CFrameWndEx::PreCreateWindow
Is your class really derived from CFrameWndEx? And is that the class you have in the IMPLEMENT_DYNANIC / IMPLEMENT_DYNCREATE line? And in the BEGIN_MESSSAGE_MAP line?
Think also on adding a ON_WM_CREATE to your message map and its OnCreatemethod for allowing other possibilities:
Do a similar thing to what you are doing int this new method.
Use SetWindowPos here

MFC - Printing - Changing page orientation from a custom pagesetup dialog

I am developing a custom print dialog and page setup using MFC and VS2008 for my Win32 program. Since the code is legacy, I can't take much advantage from MFC view/doc architecture. As a result, I wrote a printing code completely from scratch.
I setup CPrintInfo, instantiate my custom print dialog box and hook this dialog box to the CPrintInfo I just created. When my custom print dialog is up, I have a radio button to let a user toggles the page orientation. For some reasons, I couldn't modify the current DEVMODE at the run-time. As a result, every page I print will end up as a portrait.
Even if I manually set pDevMode->dmOrientation to DMORIENT_LANDSCAPE from the event handler of the custom print dialog, the printing result is still ended up as portrait. I am really not sure why this is happening and how to modify the DevMode after the print dialog is up.
Thank you in advance for any help.
Here is the code I have:
void PrintSomething(CWnd* currentWnd) {
// Create CPrintInfo
CPrintInfo* pPrintInfo = new CPrintInfo;
SetupPrintInfo(pPrintInfo); // simply setup some member variables of CPrintInfo
// Create a custom print dialog
CustomPrintDlg* pCustomPrtDlg = new CustomPrintDlg(FALSE, PD_ALLPAGES | PD_USEDEVMODECOPIES | PD_NOPAGENUMS
| PD_HIDEPRINTTOFILE | PD_NOSELECTION, pPrintInfo, currentWnd);
SetupPrintDialog(pPrintInfo,pCustomPrtDlg);
if ( AfxGetApp()->DoPrintDialog(pCustomPrtDlg) == IDOK ) {
... // proceed a print loop
}
}
Code for setting up the custom print dialog:
void SetupPrintDialog(CPrintInfo* pPrintInfo,CustomPrintDlg* pCustomPrtDlg) {
delete pInfo->m_pPD;
pInfo->m_pPD = pCustomPrtDlg;
pInfo->m_pPD->m_pd.hInstance = AfxGetInstanceHandle();
pInfo->m_pPD->m_pd.lpPrintTemplateName = MAKEINTRESOURCE(IDD_CUSTOM_PRTDLG);
// Set the Flags of the PRINTDLG structure as shown, else the
// changes will have no effect.
pInfo>m_pPD->m_pd.Flags |= PD_ENABLEPRINTTEMPLATE;
// Set the page range.
pInfo>m_pPD->m_pd.nMinPage = 1; // one based page numbers.
pInfo>m_pPD->m_pd.nMaxPage = 0xffff; // how many pages is unknown.
}
When a user toggles the radio button to Landscape, this function will be invoked:
void CustomPrintDlg::OnLandscapeChecked() {
// set the current Devmode to landscape
LPDEVMODE pDevMode = GetDevMode();
GlobalUnlock(pDevMode);
pDevMode->dmOrientation = DMORIENT_LANDSCAPE;
}
A pseucode for the custom print dialog class:
class CustomPrintDlg: public CPrintDialog {
... // just override some methods from CPrintDialog
};
Thanks again,
Unagi
I figured out the solution:
All I need is to call GlobalLock to obtain a pointer to the Devmode before changing the current DevMode.
void CustomPrintDlg::OnLandscapeChecked()
{
// set the current Devmode to landscape
LPDEVMODE pDevMode = GetDevMode();
GlobalLock(pDevMode);
pDevMode->dmOrientation = DMORIENT_LANDSCAPE;
GlobalUnlock(pDevMode)
}
Thanks again for helping me.
Nowhere in your example code do you show how you're creating the DC for printing. When you call CreateDC, you must pass a pointer to a DEVMODE structure; this defines whether the printing will be portrait or landscape.