1

I've successfully subclassed a CFileDialog and added an area with a few controls that control load/save file format using SetTemplate(). My control message handlers are being called correctly.

Depending on the file name typed, my controls may need to update. I'm getting OnFileNameChange() when the file list is clicked, and OnTypeChange() when the file type combo box is altered.

However, when a file name is simply typed, how can I get notification?

I've tried adding a PreTranslateMessage() to this CFileDialog subclass, but it's not getting called for anything. I know how to check pMsg->message == WM_KEYDOWN but if I detect one, how would I know that it was a key pressed in the file input field? And since the key hasn't gotten to the control yet, GetEditBoxText(), GetFileName() etc. won't work...

I've also tried add the following to my constructor:

OPENFILENAME& ofn = GetOFN();
ofn.lpfnHook = &OFNHook;

With the function:

UINT_PTR CALLBACK OFNHook( HWND hdlg, UINT uiMsg,
                           WPARAM wParam, LPARAM lParam ) {

   if ( uiMsg == WM_KEYDOWN )  
       MyLogging( "here" );

   return 0;
}

OFNHook() was called a lot, but uiMsg never equaled WM_KEYDOWN . So same questions as before: how do I know the key is for the file field, how do I get that file field's value AFTER the key has been applied, etc.

Swiss Frank
  • 1,985
  • 15
  • 33
  • 1
    Presumably, you would want to listen for [EN_CHANGE](https://msdn.microsoft.com/en-us/library/windows/desktop/bb761676.aspx) notifications. Otherwise you wouldn't get notified, if the user pastes a filename using the mouse interface. – IInspectable Mar 18 '18 at 19:47
  • OK, but what control ID do I use? – Swiss Frank Mar 18 '18 at 19:49
  • 1
    The explorer-style control identifiers are [documented](https://msdn.microsoft.com/en-us/library/windows/desktop/ms646960.aspx). – IInspectable Mar 18 '18 at 19:51
  • I've found "Explorer-Style Control Identifiers," and found Dlgs.h to get the defines for the control IDs on that chart. Adding ON_CONTROL( EN_CHANGE, cmb13, OnUpdate ) to my subclass message map is not getting OnUpdate called. – Swiss Frank Mar 18 '18 at 20:02
  • 1
    Shouldn't this be `edt1`? – IInspectable Mar 18 '18 at 20:22
  • haha, I wasn't sure so I did cmb13 AND edt1. I also tried ON_EN_CHANGE( edt1, OnUpdate ) and ON_EN_CHANGE( cmb13, OnUpdate ) ... no calls to my function... – Swiss Frank Mar 18 '18 at 20:24
  • btw seems to have 3 kinds of dialogs: "old-style," "explorer-style" and "Vista-style." Is that correct? I believe I have "explorer-style", so the file field is a combo box with drop-down for matches as well as keyboard input. that's why I was thought cmb13 was the answer. – Swiss Frank Mar 18 '18 at 20:27
  • if it's germane, I know my controls are NOT siblings of the pre-existing controls. For instance, just as a test I changed the "File Name:" static control to say "hello." I had to GetParent()->GetDlgItem( stc3 ) to find it, though it worked. So can my message map even GET messages for controls that are actually my "dialog"'s siblings, not its direct children? – Swiss Frank Mar 18 '18 at 20:32
  • Might you have to subclass the `edt1` control with your own and handle the event in the subclassed handler? – Andrew Truckle Mar 18 '18 at 21:04
  • *"old-style," "explorer-style" and "Vista-style."* - You are using the old-style. The new method since Vista is using `IFileDialog` and `IFileDialogCustomize`. The new method is recommended. – Barmak Shemirani Mar 19 '18 at 05:30
  • 1
    Spy++ reports the control ID to be 1148, which is 0x4ED, which is cmb13. (edt1 is 0x480.) That makes sense as I see a combo box. Andrew thx for resources you edited into my answer but I think that was for the "old-style" dialog with edt1, not the "explorer-style" I'm using with cmb13, so I reverted it. – Swiss Frank Mar 19 '18 at 06:39
  • How can you tell I'm using old style, not explorer-style? I see Vista-style "is recommended" but it also discusses COM, something I know nothing about. (I'm mostly a Unix programmer.) The current method is working nearly perfectly and I have no wish to change horses mid-stream. (The only glitch is that if the user manually types or changes the file extension, rather than selecting it by clicking a file or selection file type from the type menu, I cannot complain he has the wrong format info until he hits OK. But that works.) If you have a COM example I'd happily check it out though! – Swiss Frank Mar 19 '18 at 07:30
  • 2
    For the combo box you'd have to handle the [CBN_EDITCHANGE](https://msdn.microsoft.com/en-us/library/windows/desktop/bb775812.aspx) notification. Although I seem to remember, that I was struggling with this same issue years ago, also failing to receive change notifications. I wound up with the ugly hack to set up a timer, and repeatedly query for the control contents. – IInspectable Mar 19 '18 at 08:16
  • OK: Spy++ can see WM_KEYDOWN and WM_PASTE messages going to the ComboBox, in response to my typing. HOW CAN I SEE THOSE MYSELF??!! Neither my subclass's PreTranslateMsg(), nor my ofn.lpfnHook callback, is getting either kind of callback. (I think that in response to either event, I could then msg myself with PostMessage( WM_APP ); upon receipt then the edit in question should be done??? – Swiss Frank Mar 19 '18 at 11:28
  • The keyboard messages are processed by the edit control child of the combo box, but handling these is the wrong approach. Try to catch `EN_CHANGE` by your combo box subclass. In this case the combo box is the right place to receive the notification, as the notification is send to the parent of the edit control. – zett42 Mar 19 '18 at 12:02
  • your combo box subclass---> I don't subclass the combo box? Spy++ is not reporting EN_CHANGE for either the stock combo box nor the special one I added to let the user specify file encoding. It IS reporting the keys and pastes, but HOW is Spy++ getting them while I can't? Does the app have some app-wide PreTranslateMsg or something that I can just scan for these special control IDs? – Swiss Frank Mar 19 '18 at 12:22

1 Answers1

0

This solution doesn't feel good but this is what I ended up with:

1) Make a timer:

BOOL WinTableEditFileDialog::OnInitDialog() {

  // There doesn't seem to be a way to get events for changes in the edit
  // field, so instead we check it on a timer.
  timerid = SetTimer( 1, 100, NULL );
}

2) In timer, use GetPathName() to get drive, directory path, and sometimes filename. Then use GetWindowText() to get the exact text in the text field if the user is editing it. Sometimes GetPathName() returns the live edit (it seems, when no file is selected above) and sometimes not. So, I inspect both halves a bit then make a result out of one or the other or both.

void WinTableEditFileDialog::OnTimer( UINT_PTR nIdEvent ) {

  CComboBox* pcombo = (CComboBox*) GetParent()->GetDlgItem( cmb1 );

  // GetPathName() often doesn't update on text field edits, such as when
  // you select a file with the mouse then edit by hand.  GetWindowText()
  // does respond to edits but doesn't have the path.  So we build the full
  // name by combining the two.

  char szFull[1024];
  strncpy( szFull, sizeof( szFull ), GetPathName() );
  szFull[ sizeof( szFull ) - 1 ] = '\0';
  char* pcFile = strrchr( szFull, '\\' );
  if ( pcFile )
      pcFile++; // skip slash
  else
      pcFile = szFull;

  CComboBox* pcomboFileName = (CComboBox*) GetParent()->GetDlgItem( cmb13 );
  pcomboFileName->GetWindowText( pcFile, sizeof( szFull ) - ( pcFile-szFull ) );

  // If the user has typed a full path, then we don't need GetPathName();
  if ( isalpha( pcFile[0] ) && pcFile[1] == ':' )
      pcomboFileName->GetWindowText( szFull, sizeof( szFull ) );
Swiss Frank
  • 1,985
  • 15
  • 33