Link to home
Start Free TrialLog in
Avatar of imladris
imladrisFlag for Canada

asked on

Handling a double click on a header control

I am writing a little explorer like program. To that end I have a view derived from CListView. I have the list control operating in REPORT style, so there are nice header boxes at the top of the control. At present there are three columns, one for icon and name, one for size and one for type. So far, so good.

Now, I wish to be able to resort the list based on size versus name. The obvious interface mechanism for this is to click on the header of the size column to sort by size, and click on the header of the name column to sort by name.

The class wizard helpfully shows a "=HDN_ITEMDBLCLICK" entry for my view class. MSDN shows this to be a WM_NOTIFY message sent to the parent window to notify it the user double clicked the header control. The wizard showed "[Reflected from control}: Indicates that the user has double clicked on", and you're left to assume the dropped text is "the header control".

However, the method the class wizard created for this message in the view class is never executed! (A Trace statement never shows a result, nor does a breakpoint trip). A quickie experiment for click (as opposed to double click) produced the same result.

Can you suggest what I might be doing wrong? Alternatively or additionally can you suggest how to go about debugging such a situation? (Spy++ shows a bunch of notify messages occuring when a header is double clicked, but I am at a loss as to what to do with that information)
Avatar of webbingxia
webbingxia
Flag of China image

imladris,
try to use OnNotify to do so,the code like below:
BOOL YourClass::OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult )
{
  if( wParam==0 && ((NMHDR*)lParam)->code ==HDN_ITEMDBLCLICK)
  {
      //add your code here
  }
  return TRUE;
}

AND

only HeadCtrl(in listctrl) that has HDS_BUTTONS style will send ITEMDBLCLICK.
Avatar of peterchen092700
peterchen092700

Hi!
you can either check in OnNotify manually, or do the following trick:

CListCltr * lc = .. // your list control
// lc MUST be in report mode, or GetheaderCtrl returns NULL!
lc->GetHeaderCtrl()->SetDlgCtrlID( lc->GetDlgCtrlID() );

just after creating the lc.
What happens is the following: MFC expects the HDN_ notifications to use the ctrl id of the List Control. However, the header, being a sub window, uses another ID by default. There was a KB article about this problem, but with a solution that did not help in all cases.

Peter
Avatar of imladris

ASKER

Peter,

the SetDlgCtrlID solution is the kind of thing I've come to expect. Alas, I can't get it to work. I tried putting it in OnInitialUpdate in the view class, and in OnUpdate, but neither caused the method for HDN_ITEMDBLCLICK to be executed.

Unfortunately, and even worse, the notify solution doesn't seem to work either. I found OnNotify listed in the wizard, so I selected that. It provided a method, to which I added a couple of TRACE statements:

BOOL CWindirtView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
     // TODO: Add your specialized code here and/or call the base class
     
     NMHDR *pNMH=((NMHDR *)lParam);
TRACE("NMH: %d %d %d\n",pNMH->idFrom,pNMH->code,HDN_ITEMDBLCLICK);
TRACE("ON: %d %d\n",pNMH->code==HDN_ITEMDBLCLICK,pNMH->code==HDN_ITEMCLICK);
     return CListView::OnNotify(wParam, lParam, pResult);
}

When the program initially comes up, the traces show:

NMH: 0 -12 -303
ON: 0 0

after a single click in the leftmost header button I get:

NMH: 0 -12 -303
ON: 0 0
NMH: 0 -322 -303
ON: 0 0
NMH: 0 -16 -303
ON: 0 0
NMH: 0 -12 -303
ON: 0 0

and after a double click:

NMH: 0 -12 -303
ON: 0 0
NMH: 0 -322 -303
ON: 0 0
NMH: 0 -16 -303
ON: 0 0
NMH: 0 -12 -303
ON: 0 0
NMH: 0 -323 -303
ON: 0 0

This appears to suggest that a single click is indicated by code -322, and double click by -323. But HDN_ITEMDBLCLICK is shown as -303 (which is presumably why the comparisons keep coming up 0). How should I proceed?

Hm, never  tried this with a doc/view app (in a dialog it works like a charm). just gimme a moment....
ASKER CERTIFIED SOLUTION
Avatar of peterchen092700
peterchen092700

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Fantastic. That works flawlessly. Doubled points.

Will double again if you can explain how you figured that out. I have been debugging programs successfully for 20 years, but I am at a loss with this stuff. There seems to be such a large morass of behaviour in the windows/mfc "cloud" that is impenetrable/unknowable. The documentation appears to be hopelessly inadequate. Where do you look? What kinds of things do you try? How did you determine issues a) and b) existed? Where did SetDlgCtrlID come from (it does not appear to be documented), i.e. how did you know it existed and what it did? Why on earth are there different notification codes for Ansi or Unicode builds?
Thanks for the kudos ;-) Just for BG: I started Windows programming with MFC, but when I delved into ATL (and some directX programming), I learnt the underlying principles of Windows, that, in turn, let me better understand what the MFC guys did.

>> Where do you look?
MSDN, mostly; and the seaching the sources (e.g. nearly al all windows constants are #define XXXXX, with a single space, so it#s easy to find them using "find in files")

MSDN: Typically use Index or Search to locate some article in the correct chapter, then use "sync" button to get the contents for the topic. For controls (both windows and common), I usually refer to the platform SDK docs, since they are more consistent and up-to-date.

For notification problems, the first thing I do is override OnNotify virtual function, and trace the notifs out that come in (you can also use Spy on this, but in the OnNotify override you can filter better). First thing that caught my eye was the header notifications (-399 .. 300) come in with control ID 0. Since the header control is available as separate common control, I concluded it's simply re-used as child window of the list control. The ClassWizard-generated message map entry uses the list controls ID, so the handler doesn't get called. (Clazz wizard simply assumes the notification comes from the list control directly - but the control id sent with the WM_NOTIFY message is that of the header). Setting the control ID to the same as the list control resulted in my first tip, and works on dialogs or forms perfectly. (I did this some time ago, for a similar Q)

Now in your case, I saw it didn't work, the major difference being the notification is not handled in the parent of the list (which is standard in Windows), but in the list itself. To make this possible, MFC guys invented "Message Reflection". In short, a control can see and filter notificaitons that normally would only be seen by their parent. ("Reflection" is a bad term, since the control sees it BEFORE the parent. MSDN Index: ON_NOTIFY, takes you to an Technical Note describing Notification Handling and Reflection in MFC). You see, in the message map, ON_NOTIFY_REFLECT entries, instead of ON_NOTIFY.

First I just replaced this with ON_NOTIFY(HDN_ITEMDBLCLICK, 1, OnFoo), and the GetHeaderCtrl()->SetDlgCtrlID(1), to handle the header notification as normal notification from a child window. Didn't work. Hm, Again, looked at the OnNotify trace, to see if any header-doubleclick events came in. (To get their values, I just used "find in files" on all .h files in VC directory, for "#define HDN_", works on almost all windows constants). Which told me it were unicode versions, which in turn reminded me of the KB article about duplicating the message map entriesto catch both ANSI and UNICODE versions (I think on an Win9x machine, you get the ansi ones).

Unicode notifications need to be different from Ansi notifications, at least when they contain a string pointer. (Sender could be unicode, but receiver not, and vice versa; and unicode text needs to be handled differently from ansi text). And here's where wisdom ends. Why, the hell, the Header Control is created as Unicode, noone knows.

Peter
Thanks enormously Peter. Both for the solution and the explanation.

While obviously the explanation is not going to apply universally, I gained both comfort and insight. Comfort in that it looks like there is no central area of knowledge that I am missing and reassurance on how to establish the value of a windows define. It looks like this stuff is going to have to be ferretted out the hard way with time and experience. It looks like this is at least in part because windows and MFC have been "grown" adhoc, and often don't follow central or consistent principles.
The insight from your description being that it looks like key bits are exposing the message trail, knowing the meaning of the core information in the messages, working through the meaning and use of CWnd methods (that's where SetDlgCtrlID came from!).

Thanx again Peter.