CComboBox not selecting CurSel when dropped down - c++

I have an alphabetically sorted combobox in a dialog. This combo contains multiple strings, but some are duplicated with different cases. i.e. we have an 'On' and an 'ON', an 'Off' and an 'OFF'. This may seem redundant but there is a reason, although this is not important right now.
The duplicates obviously appear one after the other in the list, with the capitalized strings first. i.e.:
OFF
Off
ON
On
When the user selects the 'On' (lower case), the correct index is set as CurSel and the correct string is displayed. However, when I click on the arrow of the combobox to drop down the list, it does not highlight the CurSel, but the one previous to it, the capitalized string. See images below.
This is was is selected in the dropdown:
This is what is selected in the combobox when expanding the dropdown.
I have captured the ON_CBN_DROPDOWN message, and checked the cursel value and it is as I expected.
I have also already subclassed this combobox so that I can search for strings in this list in a case-sensitive way, as I know its not implemented normally, so it may be what is causing my issue.
But I don't understand why the string would be overriding the cursel value at this stage? Should the CurSel value not be the one used to select the relevant item?
Any ideas on how I can fix this would be greatly appreciated.
EDIT:
I have tried to capture the CBN_DROPDOWN message by overwriting the OnWndMsg. When this message occurs, I get the currently selected item (which is the correct item) before dropping down the menu. I then drop the menu, and call SetCurSel to what I retrieved before.
BOOL CMyComboBox::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT *pResult)
{
if(message == CBN_DROPDOWN)
{
int nCurSel = GetCurSel();
if(nCurSel != CB_ERR)
{
ShowDropDown();
SetCurSel(nCurSel);
return TRUE;
}
}
return CComboBox::OnWndMsg(message, wParam, lParam, pResult);
}
This kind of works but when I kill focus, or click on the dropdown arrow again to hide the dropdown, the wrong item is displayed in the text box. Is this a valid method, or am I completely off base here?
What message is sent when the drop down is collapsed?
EDIT 2:
I have implemented the case-sensitive combobox from code project and it works great.

Further to my comment. I think you will find that the internal mechanics is using SelectString to set the index when it is a dropdown style.
The side effect is that it may not pick the right entry for you from the list. Therefore, given the nature of the content in your combo, please try this:
int iIndex = m_cbData.FindStringExact(-1, "On");
m_cbData.SetCurSel(iIndex);
Or
int iIndex = m_cbData.FindStringExact(-1, "OFF");
m_cbData.SetCurSel(iIndex);
However, be warned, the document for FindStringExact says the search is not case sensitive. But SelectString (default behaviour) is even worse.
An alternative, which may resolve all of this, is to use SetWindowText and do it that way. This way, it does not matter what is in the listbox component. Eg:
m_cbData.SetWindowText("On");
m_cbData.SetWindowText("ON");
And get the value for the variable by either mapping to a string, or directly using GetWindowText.
UPDATE: Someone has done the work already! Here is a Case Sensitive ComboBox class:
http://www.codeproject.com/Articles/1363/Case-sensitive-ComboBox

Related

How to make checkboxes exclusive?

I'm a beginner with the WinAPI and trying to modify code from someone else, making a pair of checkboxes exclusive which were non-exclusive previously.
My initial try was to uncheck the other immediately if one was checked, but this obviously works in one direction only. If IDC_2 is checked and I try to check IDC_1 it triggers the first 'if' statement again and it fails.
This is the code I have:
static WDL_DLGRET dlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (IsDlgButtonChecked(hwndDlg, IDC_1)) {
// do something
// try to make exclusive
CheckDlgButton(hwndDlg, IDC_2, BST_UNCHECKED);
}
if (IsDlgButtonChecked(hwndDlg, IDC_2)) {
// do something
// try to make exclusive
CheckDlgButton(hwndDlg, IDC_1, BST_UNCHECKED);
}
}
I know about radio buttons, but in this case the choices can also be non mutually exclusive. It's possible to have non of the two boxes checked (but not both at once) so I think radio buttons wouldn't be the right choice here, or ?
You have two solutions that immediately present themselves
Radio buttons but add a third "none" option
react to check-box activation (being ticked) and clear out your other check-boxes.
Take option 1. Principle of least suprise. It is a bad idea to subvert common (well known and functionally accepted) user interfaces.
You could keep the checkboxes if this is a setup that later validates the input, e.g. in a dialog or form that you then submit, but you will need to add a description along the lines of:
select up to one option below
and then perform validation, refusing to accept until a maximum of one are selected.
Just take option 1.

How to change the selection from one item to the selected item in tree control?

I had a tree control which is being populated some values.And In this few tree nodes consists of
check boxes whereas few doesn't. My problem is Initially when the tree is enumerated the default
selection is on the first root node and this root node consists of three children where each child
consists of check box. Here is my problem exactly is,
Now when I expand the tree and tried to click on the child item which consist of check box(clicking on the check box)
Until the mouse left button down the focus comes to the item we are checking and when the left button click up then
the selection is reverting back to the root item (or to the item which is previously selected).
In order to resolve this this is something I tried,
//TVN_ITEMCHANGED
void CDriverSetupView::OnTvnItemChangedTree(NMHDR *pNMHDR, LRESULT *pResult)
{
NM_TREEVIEW* pNewTreeView = (NM_TREEVIEW*)pNMHDR;
if(NULL != pNewTreeView->itemNew.hItem)
{
m_TreeCtrl.Select(pNewTreeView->itemNew.hItem,TVGN_CARET | TVGN_FIRSTVISIBLE | TVGN_DROPHILITE);
m_TreeCtrl.SelectItem(pNewTreeView->itemNew.hItem);
m_TreeCtrl.SelectDropTarget(pNewTreeView->itemNew.hItem);
}
}
If I am doing like this then I am able to get the selection to whatever the child I am checking everything is cool.
But Initially when launch my application then the tree is is getting expanded and the selection is not on the root item
,it is moving on to the last item .
Please find the below images for better understanding,
When the tree is enumerated without the piece of code in OnTvnItemChangedTree,the tree looks like this,
Now when mouse left button down on the first checkbox the selection seems that it changed to checkbox
item ,
Now when mouse left button up on the checkbox the selection again revert back to the previous node,
Now when I use the piece of code within OnTvnItemChangedTree then I am able to get the selection to the checked node but initially when I launch my application then the tree is getting expanded and the selection is on to the last child item which is as follows,
In the above image I am able to get what I want but the tree is getting expanded and the selection is on to the last item,I know this is because in the pNewTreeView->itemNew.hItem at last after the initialization that item consists handle to the last item ,but how can we make such that initially the tree should not get expanded and selection should properly work when I check on any node then the selection should get changed to the checked item.
Can anyone please let me know how can make the selection to be remained on to the root item and the tree Initial status should
be as not expanded.
I'm probably misunderstanding the question because the solution seems so obvious, but why don't you just collapse the tree and set the focus to the first item when you create it? So after you've added all children?
Finally I solved it .In order to avoid that expand while at the time Initialization and moving the focus from one item to the checked item can be achieved as follows,
For NM_CLICK notification add this piece of code ,which works like charm.
void CMyTreeCtrl::OnNMClickTree(NMHDR *pNMHDR, LRESULT *pResult)
{
if(NULL != pNewTreeView->itemNew.hItem)
{
m_TreeCtrl.Select(pNewTreeView->itemNew.hItem,TVGN_CARET | TVGN_FIRSTVISIBLE | TVGN_DROPHILITE);
m_TreeCtrl.SelectItem(pNewTreeView->itemNew.hItem);
}
}
The only mistake I had done was added this code in OnTvItemChanged Instead I added in OnNMClickTree ,Now finally I achieved what exactly I want.

How to make focus remain on all list control on same dialog box?

I have 3 list control on one dialog box but only one is showing focus.
if i clicked on 2nd list control then focus disaappear from 1st one.
Means at a time only one list showing focus.
How to make focus remain on all list control on same dialog box?
I don't think that this is technically possible. 'Focus' is an attribute that can only be applied to an individual element.
Think of it in terms of 'focus' is the element that the user is currently interacting with. How would a user be expected to interact with 3 distinct elements at the same time?
As Brian says - focus can only be on one control at time. I'm guessing you are trying to change the other list controls based on the first list box. One way to do it is to associate a variable with each list control, like mListCtrl1, mListCtrl2. Then add a handler for the NM_CLICK event, and have some code like this:
void CTabTestDlg::OnNMClickList3(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = (LPNMITEMACTIVATE)(pNMHDR);
// TODO: Add your control notification handler code here
*pResult = 0;
UpdateData(true);
DWORD dwData = mListCtrl1.GetItemData(pNMItemActivate->iItem);
int max = mListCtrl2.GetItemCount();
for (int i=0;i<max;i++)
{
DWORD dwData2 = mListCtrl2.GetItemData(i);
if (dwData==dwData2)
{
mListCtrl2.SetItemState(i,LVIS_SELECTED,LVIS_SELECTED);
break;
}
}
UpdateData(false);
}
Note that I have the control set to "Always show selection", and "Single selection"

C++/MFC: Handling multiple CListCtrl's headers HDN_ITEMCLICK events

I'm coding an MFC application in which i have a dialog box with multiple CListCtrls in report view. I want one of them to be sortable.
So i handled the HDM_ITEMCLICK event, and everything works just fine .. Except that if i click on the headers of another CListCtrl, it does sort the OTHER CListCtrl, which does look kind of dumb.
This is apparently due to the fact that headers have an ID of 0, which make the entry in the message map look like this :
ON_NOTIFY(HDN_ITEMCLICK, 0, &Ccreationprogramme::OnHdnItemclickList5)
But since all the headers have an id of zero, apparently every header of my dialog sends the message.
Is there an easy way around this problem ?
EDIT: Maybe i wasn't clear, but i did check the values inside the NMHDR structure. The HwndFrom pointer is different depending on which header is clicked, which doesn't help me a lot since it's value is obviously different at each runtime. The idFrom value is 0, for the very reasons i explained above, because that's the id of every header. Thanks
EDIT2: The hwnd pointer values do also not correspond to the CListCtrl, probably because it's coming from a different object entirely.
Check the values of the NMHDR structure.
http://msdn.microsoft.com/en-us/library/bb775514%28VS.85%29.aspx
Ok i found a solution, though i find it a bit dirty but it works, so i'll post it for future reference.
You can get the Header through the GetHeaderCtrl member function of CListCtrl. You can then get it's handler thru m_hWnd. So all you got to do is to test if that handler is the same as the one in the NMHDR structure, so the code looks like this :
void Ccreationprogramme::OnHdnItemclickList5(NMHDR *pNMHDR, LRESULT *pResult)
{
if (pNMHDR->hwndFrom == LC_gen_schedules.GetHeaderCtrl()->mhWnd)
{
// Code goes here
}
*pResult = 0;
}
Thanks all for the help
The LPARAM passed to your message handler is actually a pointer to an NMHEADER structure, which contains an NMHDR structure, which in turn contains the HWND and control ID of the control which sent the message. You may be able to compare that to your list controls' HWNDs to determine which window's header control was clicked.
Alternatively, you could derive a class from CListCtrl and reflect the HDN_ITEMCLICK messages back to the list control. That way, each list control object handles its own header's notifications.

Programmatically change combobox

I need to update a combobox with a new value so it changes the reflected text in it. The cleanest way to do this is after the comboboxhas been initialised and with a message.
So I am trying to craft a postmessage to the hwnd that contains the combobox.
So if I want to send a message to it, changing the currently selected item to the nth item, what would the postmessage look like?
I am guessing that it would involve ON_CBN_SELCHANGE, but I can't get it to work right.
You want ComboBox_SetCurSel:
ComboBox_SetCurSel(hWndCombo, n);
or if it's an MFC CComboBox control you can probably do:
m_combo.SetCurSel(2);
I would imagine if you're doing it manually you would also want SendMessage rather than PostMessage. CBN_SELCHANGE is the notification that the control sends back to you when the selection is changed.
Finally, you might want to add the c++ tag to this question.
A concise version:
const int index = 0;
m_comboBox.PostMessage(CBN_SELCHANGE, index);
What might be going wrong is the selection is being changed inside the selection change message handler, which result in another selection change message.
One way to get around this unwanted feedback loop is to add a sentinel to the select change message handler as shown below:
void onSelectChangeHandler(HWND hwnd)
{
static bool fInsideSelectChange = 0;
//-- ignore the change message if this function generated it
if (fInsideSelectChange == 0)
{
//-- turn on the sentinel
fInsideSelectChange = 1;
//-- make the selection changes as required
.....
//-- we are done so turn off the sentinel
fInsideSelectChange = 0;
}
}
if you fx want to change the title - which is the line shown when combobox is closed, then you can do following:
m_ComboBox.DeleteString(0); // first delete previous if any, 0 = visual string
m_ComboBox.AddString(_T("Hello there"));
put this in fx. in OnCloseupCombo - when event close a dropdownbox fires
ON_CBN_CLOSEUP(IDC_COMBO1, OnCloseupCombo)
This change is a new string not a selection of already assigned combobox elements