Drag and drop in MFC Dialog - c++

I'm trying to add drag and drop functionality to a control on a property page which is programmatically "wrapped" inside an MFC dialog at runtime. This "wrapping" involves instantiating the property page as a member of the dialog, and then resizing it and positioning it to the dimensions of a picture control which is on the dialog in the .rc file. I'm not sure why things were done this way, other than to maybe standardise the appearance of property pages in the app, but in any case I end up with this complicated hierarchy of windows, a control inside a property page inside a picture control inside a dialog.
The control which I want to accept dragged files has DragAcceptFiles invoked on it, but I think the WM_DROPFILES message for the control is being discarded before it reaches the control, because one of the parent windows doesn't accept dragged files. I considered calling DragAcceptFiles on all the parents, and passing WM_DROPFILES down the chain of windows, but I don't want to do that because they won't have handlers to accept the files, and will show the icon indicating files can be dropped on them regardless.
Any help on figuring out how to get the WM_DROPFILES message to my control would be much appreciated.

At the parent level, implement an override for virtual CDialog::PreTranslateMessage(MSG* pMsg)
This gives you a chance to see the WM_DROPFILES message and redirect them to a handler in the property page.

I figured this out in the end by calling CWnd::BringWindowToTop() on both the control I wanted to drag files onto, and also the dialog that the control was on.

Related

Drag and drop in my MDI is intercepted by CRichEditView and crashes in CRichEditView::GetDocument - how do I override it?

I have written a MDI app in MFC, where a document window contains a CRichEditView among other things.
When I drag a file into the document window, onto the CRichEditView, it crashes here (from afxrich.inl):
_AFXRICH_INLINE CRichEditDoc* CRichEditView::GetDocument() const
{
AFXASSUME(m_pDocument != NULL);
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CRichEditDoc))); <<< crash!
return (CRichEditDoc*)m_pDocument;
}
The crash happens because m_pDocument is not a CRichEditDoc (it is a class of mine derived by CDocument).
However, I don't care about the RichEditView/Ctrl, what I want is that when I drag and drop a file into the application, it is simply opened as a document. This already happens if my MDI app is empty: I drag and drop a file into the app and it gets opened. If I try the same drag and drop when a document window is already opened, it crashes as described above instead of opening it.
I tried multiple solutions to override this behavior, but none works:
intercepting OnDragEnter from (my extended) CRichEditView
using ON_NOTIFY_REFLECT(EN_DROPFILES, OnEnDropFiles)
calling DragAcceptFiles(false) on the CRichEditCtrl from the CRichEditView
calling RevokeDragDrop() on both the CRichEditCtrl and CRichEditView handles
calling DragAcceptFiles(true) from the CMainFrame/CMDIFrameWndEx
none of these is able to prevent the RichEdit from taking the drop operation (and crashing), they seem to have no effect at all.
All I want is for the CRichEditView/CRichEditCtrl not to intercept the dropped file, and to let it intercept from the CMainFrame/CMDIFrameWndEx instead.
Does anyone know how to solve this?
A CRichEditView requires a CRichEditDoc.
If you don't need a CRichEditView use your own CView implementation and implemnt the messages you need.
Here is a good MFC drag&drop sample.
And here another detailed description.
After a lot of trial and error, this is what worked for me in the end:
use CRichEditDoc instead of CDocument as suggested by xMRI, this gets rid of some of the issues
Add DragAcceptFiles(false) in the OnCreate of the class extending CRichEditView
Implement QueryAcceptData in the class extending CRichEditView and let it return S_FALSE
Add an OnDropFiles handler in the class extending CRichEditView (WM_DROPFILES)
With all these in place now whenever I drop files in the application, even if they are on the RichEdit, they are still intercepted by the main frame and opened as new documents in the MDI. This also suppresses drag and drop where you are not dropping files (e.g. you drop some text you drag from notepad).

How can I open an *modal* file dialog with IFileOpenDialog?

I've been trying to implement a file dialog into my C++ application for a while now, and I achieved good success with the code described in this article (It is german, but the code should be understandable):
https://msdn.microsoft.com/de-de/library/windows/desktop/ff485843(v=vs.85).aspx
However, using this code in my window class, which is a CDialogImpl, I just can't find out how to make this file picker modal. If I use this code, I can just spawn multiple file picker.
Of course, I could just keep track of the state by adding a member variable representing the state, but it would still not solve the problem of being able to click around in the main window while the dialog is opened.
Is there any way which would allow me to make this window modal? I've been trying to scan through all available methods, but I couldn't find anything. I didn't find any flags which could be passed in the creation, neither any options which I could set after creation.
Any help is appreciated!
The example you link to is very simple and has no UI other than the file dialog. Your program is more complex, having a window from which the file dialog is invoked.
You need to provide an owner for the file dialog. When you do that the owner is disabled, part of what makes the dialog modal. Failing to pass an owner means that the other windows are not disabled and so still respond to user input.
The example code provides no owner, but since there are no other windows in that program, that is benign. Modality is only meaningful when there are multiple windows.
So, to solve the problem, pass the owner, the handle of your window, to the Show method of the file dialog.
Disabling owner windows is one of the key parts of a modal dialog. You will find that any API for modal dialogs expects you to specify an owner. Get into the habit of expecting to provide that ownwr window, and looking for the means to do so.

Display and use the same MFC CList control in multiple dialogs

I am coding a test application for a windows CE device. This is the first time I am programming for a handheld device. I use MFC VC++ on Visual Studio 2008. I have found that there are many restrictions in the controls and what I could do with them when running the program on a handy versus when I run a similar program on a desktop computer.
Now, the device I am currently deploying my test program to, does not have a touchscreen and has few extra keys other that the numberpad 0-9 keys. So, I have to do with a simple GUI that uses keydowns to call specific functions like add, edit, delete etc... It also forces me to use separate dialogs for each of these functions so as to avoid unnecessary mouse cursor usage.
This leads me to my current problem: The 'ADD' dialog of my test app adds some user data to a CListCtrl that is on the 'MAIN' dialog. The 'EDIT/DELETE' dialog is to allow the user to select the desired data from its own CListCtrl and press the "ENTER" key, which thereby deletes the selected data from the 'MAIN' dialog's CListCtrl. Thus, both the main dialog and the 'EDIT/DELETE' dialog have CListCtrl with the exact same data. So, instead of having to use 2 separate list controls and using loops to copy the data to and fro among them, is there a way in which i could use the exact same CListCtrl (one and only one instance of the CListCtrl exists), but display it on 2 separate dialogs? This would remove all the copying code, as well as halve the amount of data in memory.
I tried passing a pointer to the MAIN dialog's CListCtrl to the 'EDIT/DELETE' dialog in hopes that I could redraw the control there, but in vain. I could call the RedrawWindow, RedrawItems commands, but they seem to have no effect in the 'EDIT/DELETE' dialog (I think it is because the control itself is not present on the edit/delete dialog). Any other suggestions?
You could temporarily change the parent of the ListCtrl using CWnd::SetParent to the EDIT/DELETE dialog, and set the position with CWnd::SetWindowPos to where you want to have it. When the dialog gets closed, set the parent back to the MAIN dialog.

Create a property sheet using a dialog template

I need to extend an existing MFC app with a UI that will end up very cluttered unless I use a tab control. However, the nature of the UI is that there are some controls that are global, and only some that can be localised to a particular tab.
The standard use of tab controls (CPropertySheet + CPropertyPage) more or less expects there only to be CPropertyPage instances (tabs) visible on the CPropertySheet object, and nothing else. There is a Microsoft Example Project that shows a single additional window painted outside the area occupied by the tab control... but it's not immediately clear how it is created/drawn/handled, and it is only one single additional window that generates few events (I guess it is painted, so there must be a WM_PAINT event handler lurking somewhere).
Is it possible to lay out a bunch of controls with the MS Dialog Editor, including a tab control, and create the CPropertySheet using that template, hook up event handlers in a nice way, etc... or some equivalent way of getting the MFC framework to do as much of the creating, drawing and event handling as possible when it comes to a situation like this?
Yes it is possible to create dialog templates and use them in a CPropertyPage.
Each CPropertyPage behaves nearly like a dialog and handles all events for the controls on it.
Also there are features like OnApply that help you to manage the data exchange between the controls and your internal storage.
The central CPropertySheet only creates the dialog that get active. So OnInintDialog for a page is called the first time when the page gets active.
In the MFC since 2010 are more possibilities than a CPropertySheet. You can create tabbed views, that again may be CFormViews. I don't like CDialog based applications so I would prefer a tabbed view in a standard frame with toolbar and menus if appropriate for the application. So another method to unclutter your UI is to choose the MDI interface with tabbed documents... but multiple documents maybe isn't what you want.
Here is a sample of an SDI application with multiple tabbed views.
Also Coeproject shows some more samples here and with splitters and tabs here.
There are at least three solutions paths:
Try to squeeze the situation into the CPropertySheet + CPropertyPage framework which does not naturally allow for additional dialog controls on the CPropertySheet object, and so you will get no framework support this
Place a tab control on an ordinary dialog, and then use the TCN_SELCHANGE messages to fire code that manually hides & shows individual dialog controls subject to the tab control (again no framework support, but this time "within" the tab control instead of outside it)
Follow Mark Ransom's observation that if you can embed one kind of CWnd-based control on a CPropertySheet then you can probably embed any such object, including a CDialog-based object that has been developed in the MFC Dialog Editor
All of these approaches are going to encounter challenges, and it will depend on the specifics of the situation as to which is better. But first you should really consider whether there is a cleaner UI design which would lend itself to a simpler solution.
In my specific case, I saw no cleaner design alternatives, and found it easiest to take the second approach. It left me with some reasonably simple calls to ShowWindow() to show/hide the controls inside the tab control.

DLL plugin that creates a parented window doesn't handle messages correctly

I'm creating a plugin framework, where my application loads a series of plugin DLL's, then creates a new window and pass this new window's handle to the plugin. The plugin can, then, use this handle to create their own GUI.
Everything seems to be working very well. The only problem is that when I press TAB on a plugin widget (An editbox, for example), it doen't jump to another widget. I figured out that some Windows messages are passed, and some others aren't. The WM_KEYDOWN is passed for other keys, because I can type on the editbox, but this message doesn't handle TAB key.
Hope somebody has a hint.
I'm using Borland VCL with CBuilder, but I think I could use any framework under WIN32 to create these plugins, since they never know how their parent windows were created.
It's very complex matter indeed.
When you hit TAB focus jumps to another control only when these controls belong to a Modal Dialog Box. In fact there are some buttons like ESC, LEFT, RIGHT, DOWN, UP, TAB which modal dialog message function treats in a special way. If you want these keys to behave in similar way with modeless dialog box or any other window you should change you message processing function and use IsDialogMessage inside. You'll find more information about IsDialogMessage function in MSDN also to better understand this stuff you may check as well Dialog Boxes section.
And, as was mentioned before, you should set WS_TABSTOP and WS_GROUP styles when needed.
Good luck!
I believe you'll have to take the following steps:
Subclass your edit controls (and other controls as needed).
Capture the WM_KEYDOWN message in your edit control's WndProc.
Check to see if the shift key is currently held down (using GetKeyState or similar).
Call GetWindow, passing in a handle to your edit control and either GW_HWNDPREV or GW_HWNDNEXT depending on whether shift is held down. This will give you the handle to the window that should receive focus.
Call SetFocus and pass in the window handle you got in step 4.
Make sure you handle the case where your edit controls are multiline, as you might want to have a real tab character appear instead of moving to the next control.
Hope that helps!
I believe you suffer from having a different instance of the VCL in each of your dlls and exes. Classes from the dll are not the same as the ones from your exe, even if they are called the same. Also global variables (Application, Screen) are not shared between them. Neither is the memory since they both have their own memory manager.
The solution is to have the dlls and the exe share the VCL library and the memory manager. I am not a BCB developer, but a Delphi developer. In Delphi we would just use the rtl and the vcl as runtime packages. Maybe you could do the BCB equivalent.
A DLL has its own TApplication object.
to provide uniform key handling. when the DLL Loads.
assign the DLL::TApplication to the EXE::TApplication
Be sure to do the reverse on exit.
--
Michael