I have an array of CSliderCtrl's in my windows form that I need to receive notifications from. I am using the ON_NOTIFY_RANGE declaration to map the slider updates to the appropriate handler. My problem is that the only event that gives me a notification is the NM_RELEASEDCAPTURE event. So my code looks like this:
BEGIN_MESSAGE_MAP(CTheThingDlg, CDialogEx)
ON_NOTIFY_RANGE(NM_RELEASEDCAPTURE, start_id, end_id, handler)
END_MESSAGE_MAP()
void MyClass::handler(UINT p_id, NMHDR* p_notify_msg_ptr, LRESULT* p_result_ptr)
{
//Do Stuff
}
I have tried using the WM_H/VSCROLL, TB_THUMBTRACK, TB_LINEUP/DOWN, and other events, but none give me the notification whether I use the mouse or keyboard to scroll. They are just simple horizontal scroll bars created with the following code:
slider_ctrl.Create(WS_CHILD | WS_VISIBLE | TBS_HORZ | TBS_BOTTOM | TBS_FIXEDLENGTH,
CRect(x1, y1, x2, y2),
this,
id);
A penny for your thoughts.
You need to handle the WM_HSCROLL message. TB_THUMBTRACK and the other TB notifications are not messages, they are passed to the WM_HSCROLL message handler in the nSBCode parameter.
Related
I primarily come from a .NET background, but there is one small task that I have to accomplish for a Windows Control, specifically a ComboBox.
I understand that the CBS_SORT message is needed for the ComboBox to sort items alphabetically. This works just fine for me when I add strings to the combobox using the CB_ADDSTRING flag.
I'm trying to make the ComboBox use a reverse (or other custom) sorting order.
If I use CreateWindow to make my combobox, is there a way I can take the HWND it returns and hook up a custom method that processes the WM_COMPAREITEM message?
This is the article from Microsoft that talked about processing the WM_COMPAREITEM message. https://msdn.microsoft.com/en-us/library/windows/desktop/bb775791(v=vs.85).aspx
Edit:
I suppose one workaround is to use the CB_INSERTSTRING flag for custom sorting, but I'd really prefer to implement something similar to a CompareTo in other languages if I can.
Edit:
It's worth mentioning that class which calls CreateWindow (w/o using the CBS_HASSTRINGS flag) has a message handler for the WM_COMMAND message. It switches in the HIWORD to handle notifications like CBN_SELCHANGE.
I looked at the documentation for Combo box and noticed a slight difference between CBN_SELCHANGE and WM_COMPAREITEM.
The CBN_SELCHANGE notification code is sent when the user changes the current selection in the list box of a combo box. The user can change the selection by clicking in the list box or by using the arrow keys. The parent window of the combo box receives this notification in the form of a WM_COMMAND message with CBN_SELCHANGE in the high-order word of the wParam parameter.
And here's what it says about WM_COMPAREITEM
The system sends the WM_COMPAREITEM message to determine the relative position of a new item in the sorted list of an owner-drawn combo box or list box. Whenever the application adds a new item, the system sends this message to the owner of a combo box or list box created with the CBS_SORT or LBS_SORT style.
As far as I can tell, the DWORD Style that is passed into the CreateWindow method is WS_VISIBLE | WS_CHILD | WS_VSCROLL | CBS_AUTOHSCROLL | CBS_DROPDOWNLIST | CBS_SORT
Perhaps the problem is that the combobox that is created is not an "owner-drawn" combobox?
If that is the case, I would need to find a way to do that, but only handle the WM_COMPAREITEM case and not any other custom painting code.
Update
I've created the ComboBox without the CBS_HASSTRINGS and with the CB_OWNERDRAWFIXED (Which then lets me process the WM_COMPAREITEM message).
Here's the message handling code
LRESULT MyComboBoxParent::OnCompareItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
//umsg is always wm_compareitem
COMPAREITEMSTRUCT* pCompareItemStruct = (COMPAREITEMSTRUCT*)lParam;
ATLASSERT(pCompareItemStruct->CtlType == ODT_COMBOBOX); //this assert works
int iItemId1 = pCompareItemStruct->itemID1;
int iItemId2 = pCompareItemStruct->itemID2;
LPCTSTR item1 = (LPCTSTR)pCompareItemStruct ->itemData1;
ATLASSERT(item1 != NULL);
LPCTSTR item2 = (LPCTSTR)pCompareItemStruct->itemData2;
ATLASSERT(item2 != NULL);
int iStringCompareResult = _tcscmp(item1, item2);
//should reverse the sorting order
int iReturn = -1 * iStringCompareResult;
return iReturn;
}
//and the existing code snippet that adds the values to the combobox
::SendMessage(m_hwndCombo,CB_ADDSTRING,0,(LPARAM) ((wchar_t*)bstr_tDisplayedValue))
The data coming in from the COMPAREITEMSTRUCT is really strange.
I add the following values to the combo box in order.
aaaa
bbbb
this triggers wm_compareitem
iItemId1 is 0
iItemId2 is -1
item1 is 0x00000000 (null ptr)
item2 is L"bbbb"
(this causes the string compare to fail, so I just force return of -1)
cccc
iItemId1 is 0
iItemId2 is -1
item1 is 0x00000000 (null ptr)
item2 is L"cccc"
zzzz
yyyy
xxxx
z,y and x all follow the same pattern. I may have found one other guy on the internet with the same problem here. But hard to say for sure.
I need to prevent resizing of some items in a Win32 header control. No problem to process the HDN_BEGINTRACK notification message and cancel it - the problem is in the cursor indicating that the item can be resized. For instance, if the first item can't be resized, I see this:
, but I'd prefer to see this:
I can ignore the cursor change by suppressing the WM_SETCURSOR message, but the problem is how to know the header item WM_SETCURSOR is generated for. I can detect the item under the mouse pointer in WM_MOUSEMOVE using the HDM_HITTEST message, but WM_MOUSEMOVE is sent to window procedure only after WM_SETCURSOR. I analyzed all notification messages for the Win32 header control, and it seems, it does not have an equivalent of the MouseEnter event that is sent to the window procedure before WM_SETCURSOR.
Any ideas how to solve this problem?
You need to sub-class the header control if you haven't already.
In the sub-class, intercept the WM_SETCURSOR message, and use GetMessagePos() to get the coordinates of the mouse. These are in screen coordinates, so you need to convert them to client coordinates for the header control hit test.
// in the window sub-class
if (uMsg == WM_SETCURSOR)
{
DWORD dwPos = GetMessagePos();
HDHITTESTINFO hti;
hti.pt.x = GET_X_LPARAM(dwPos);
hti.pt.y = GET_Y_LPARAM(dwPos);
ScreenToClient(hWnd, &hti.pt);
SendMessage(hWnd, HDM_HITTEST, 0, reinterpret_cast<LPARAM>(&hti));
if (...) // test for items we want to block
{
SetCursor(LoadCursor(0, IDC_ARROW));
return TRUE;
}
// pass through to regular WndProc
}
I am writing a c program to do some calculations.It would really help me if I was able to get responses by clicks of mouse.
How can i do this also If it is not possible then using which functions or libraries of C only would I be able to do that.
Ncurses has support for GPM (mouse library).
Excerpt from Ncurses interfacing with the mouse how-to:
Once a class of mouse events have been enabled, getch() class of functions return KEY_MOUSE every time some mouse event happens. Then the mouse event can be retrieved with getmouse().
The code approximately looks like this:
MEVENT event;
ch = getch();
if(ch == KEY_MOUSE)
if(getmouse(&event) == OK)
. /* Do some thing with the event */
.
.
getmouse() returns the event into the pointer given to it. It's a structure which contains
typedef struct
{
short id; /* ID to distinguish multiple devices */
int x, y, z; /* event coordinates */
mmask_t bstate; /* button state bits */
}
The bstate is the main variable we are interested in. It tells the button state of the mouse.
Then with a code snippet like the following, we can find out what happened.
if(event.bstate & BUTTON1_PRESSED)
printw("Left Button Pressed");
I'm on a roll today with MFC! :D
I have a text box and a list view control.
When the user presses the VK_UP and VK_DOWN keys in the text box, I would like this to happen:
Do some stuff.
Have the list view control also process the message (to highlight the previous/next item).
I want the list view to wrap around the current selection, if the key press is the first in its sequence.
Do some more stuff.
I tried subclassing my edit box in my dialog:
class MyEditBox : public CWnd
{
bool allowWrap;
afx_msg void OnKeyUp(UINT, UINT, UINT) { this->allowWrap = true; }
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CListCtrl &listView = static_cast<CListView *>(
this->GetParent()->GetDlgItem(IDC_LIST_VIEW))->GetListCtrl();
if (nChar == VK_UP || nChar == VK_DOWN)
{
int iSelBefore = listView.GetNextItem(-1, LVNI_SELECTED);
this->GetParent()->GetDlgItem(IDC_LIST_VIEW)
->OnKeyDown(nChar, nRepCnt, nFlags); //Oops! Protected member :(
int iSelAfter = listView.GetNextItem(-1, LVNI_SELECTED);
if (iSelBefore == iSelAfter && // Did the selection reach an end?
this->allowWrap) // If so, can we wrap it around?
{
int i = a == 0 ? listView.GetItemCount() - 1 : 0;
listView.SetItemState(i, LVIS_SELECTED | LVIS_FOCUSED,
LVIS_SELECTED | LVIS_FOCUSED);
}
}
this->allowWrap = false;
}
}
but OnKeyDown() is a protected member, so I can't just call it on another control.
Is there a better way to solve this than manually sending the command with SendMessage? Should I change my design, e.g. subclass something else, etc.?
Your intention is to select previous or next item in list control, right? Then you should call the method to do that directly instaed of asking the CListCtrl to "process" your message.
You may call CListCtrl::SetSelectionMark and CListCtrl::SetItemState to select next or previous keystroke. Example:
cListCtrl.SetSelectionMark(nIndex);
cListCtrl.SetItemState(nIndex, LVIS_SELECTED | LVIS_FOCUSED, 0xFF);
You can handle Key Down, Key Up as well as Page Down, Page Up, End, Home or any any key from edit box. You need to do calculation, though.
Or you can just SendMessage. There is no need to call OnKeyDown directly. Let the framework call it for you when you send the message.
I am seeing also other ways to solve this:
Derive a class from CListCtrl called MyListCtrl and choose one of two things:
1.1 Declare MyEditBox as a friend and now you can call the protected methods on MyEditBox
1.2 Add public methods CallOnKeyDown(...) and CallOnKeyup(...) to it that only do what is needed.
And when creating the control, instance a MyListCtrl instead of a CListCtrl. Also replace the listView variable you have shown here to be a MyListCtrl and use the methods you have now available
Use PreTranslateMessage(...). I think this "hammer" solution is worse than sending a message.
I'm subclassing a WTL combobox and I'm owner-drawing the items of the combobox. The control has the attributes CBS_DROPDOWNLIST | CBS_HASSTRINGS | CBS_OWNERDRAWVARIABLE and I'm using the mix-in class COwnerDraw to implement DrawItem() and MeasureItem(). When the drop down list is down, the items are drawn correctly. However when the drop down list is up, the combobox control is empty and the item isn't drawn. What am I doing wrong?
The WTL class looks like this:
class CMyComboBox :
public CWindowImpl<CMyComboBox, CComboBox>,
public COwnerDraw<CMyComboBox>
{
public:
BEGIN_MSG_MAP_EX(CMyComboBox)
CHAIN_MSG_MAP(COwnerDraw<CMyComboBox>)
CHAIN_MSG_MAP_ALT(COwnerDraw<CMyComboBox>, 1)
END_MSG_MAP()
void DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
CDCHandle dc = lpDIS->hDC;
dc.FillSolidRect(&lpDIS->rcItem, lpDIS->itemID == 0 ?
RGB(255,0,0) : RGB(0,255,0));
}
void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
lpMeasureItemStruct->itemWidth = 12;
lpMeasureItemStruct->itemHeight = 12;
}
};
The class is used on a dialog and is subclassed like this:
m_cbMy.SubclassWindow(GetDlgItem(IDC_COMBO1));
m_cbMy.AddString(_T("Item 1"));
m_cbMy.AddString(_T("Item 2"));
Changing the control attributes to CBS_OWNERDRAWFIXED doesn't change anything.
Edit:
Thanks to the help of najmeddine I figured out that I have to handle WM_PAINT to draw the actual combobox, and not only the items in the drop-down list. Unfortunately now I have to also draw the combobox control all by myself. Is there a way to let the GDI draw the border and drop arrow so that I only have to draw the "insides" of the control?
To draw the combobox control you should use the theme APIs in your WM_PAINT handler (in XP+ - you don't say what Windows versions you need to support.) Specifically, use DrawThemeBackground, and pass in one of the CB_ values for iPartId.
You will also need to use the buffered paint APIs to handle transitions on Vista, which can complicate your paint handler - this and other drawing problems when custom painting a combobox control are explained here in a fair amount of detail. I'd suggest using that forum thread as your main reference implementing this.
On DrawItem you filling a rect with some color. But where is DrawText or something like it?
Example of custom DrawItem.
To draw the comboBox control (not the list), you should also handle the WM_PAINT message and do your painting there.
the DrawItem event only paints the drop list and it's items.