Adding accelerators(shortcuts) in MFC - HOW? - c++

I found this link: http://support.microsoft.com/kb/222829
But I can't understand that much.
Ok, I understood I need to add this to my header file:
HACCEL m_hAccelTable;
and then this:
m_hAccelTable = LoadAccelerators(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_ACCELERATOR1));
to my main .cpp
But where does this go?
BOOL CAboutDlg::PreTranslateMessage(MSG* pMsg) {
if (m_hAccelTable) {
if (::TranslateAccelerator(m_hWnd, m_hAccelTable, pMsg)) {
return(TRUE);
}
}
return CDialog::PreTranslateMessage(pMsg);
}
I need around 6 shortcuts (CTRL + U to load something, CTRL + O to load smth else), I can't understand how this works, need a little bit of help

Now, MSDN article is misleading. It shows how to add accelerators to About box and only About box will be able to handle accelerator that is in this case equivalent of pressing the button with IDC_BUTTON1 ID.
You need to do something very different allowing all objects in your application to get a chance to handle this message. This is done for you in MDI/SDI apps.
Once you create accelerator table in the resource, you have to add accelerators: Key combination paired Accelerator key combination, when used generates command message with appropriate ID. Once you are done adding, you have to create command message handlers for each of the ID.
When accelerator is used the handler is invoked and you can add the code you need.
Now do this:
Declare HACCEL type variable to your app class. In the InitInstance call LoadAccelerators.
Use wizard to insert PreTranslateMessage override in your application class. Add following:
if (m_hAccelTable)
{
if (::TranslateAccelerator(*m_pMainWnd, m_hAccelTable, pMsg))
{
return(TRUE);
}
}
This will allow the main dialog to handle accelerators. Note *m_pMainWnd. It is your dialog handle (automatically casted).
Now you can add handlers for any accelerator to the dialog or to the application class. You can also route command messages to any window in your application using OnCmdMsg.
My advice for the future.
When you decide to make your app a dialog based, consider creating SDI application with CFormView derived class.
You can change frame style to not allow resizing and it will look like dialog based but. . .
You will have ability to use a toolbar a menu for free and most importantly you will have all accelerator and command routing for free.

The page you referenced describes adding an accelerator table to a dialog based applicaion.
Are you creating a dialog based application or just a normal MFC frame based application with a menu bar?
If you are doing the former then as the page you referenced suggest you need to override the PreTranslateMessage dialog box method.
If you are doing the later then you only need to call the CFrameWnd::LoadAccelTable function.

Related

MFC dialog-based app with tab control - Visual Studio bugs/restrictions make recommended path impossible?

Create a dialog-based application in Visual Studio 2019. Insert a new dialog in the resources. Place a control (or two) on that dialog that you will later (try to) hook up to a variable (e.g. an edit box to enter your name).
Prepare to add that control variable by creating a class for this second dialog. If you inherit from CDialogEx (or perhaps other classes too - not tested), you can go on to add a variable for the control you added - easy, normal.
But if you had the "tab control" context from the title above in your mind, and chose to inherit from CMFCPropertyPage instead, can you add a variable subsequently? No you cannot - the class added makes no reference to the ID of the dialog resource, and so the Add Variable process has no basis to find a class to add the variable to.
I think this is a bug in Visual Studio... but I'm not 100% sure because I have always found the documentation around property sheets and property pages somewhat confusing. Specifically:
Do the classes implementing the tabs contents on a tab control "contain" the controls "on" the tab... or is the tab control really just a way to provide visual cues to show/hide sets of controls, and all of those controls and associated variables reside in one class?
I believe it's intended to be the former, but I can imagine that one uses a tab control because there are strong similarities between tab contents, and therefore potential benefits in implementing all the control variables in one place so as to avoid duplication. I just wish it was stated explicitly somewhere what the intention was.
Wider context: I'm trying to implement a dialog-based app with a tab control dominating that dialog. MS documentation says to implement a tab control using CPropertySheet and CPropertyPage to implement the tabbed dialog and tab contents. However, there is no (direct) way to create a dialog-based application whose main dialog inherits from CPropertySheet. When you look for examples of tab controls at the application level, you readily find things that deviate from the documented path considerably - using neither CPropertySheet nor CPropertyPage and using the WM_LBUTTONDOWN event instead of TCN_SELCHANGE, both without obvious reasons.
Any tips on (a) how to repair the apparent Visual Studio bug and/or (b) how to inherit from CPropertySheet for my application dialog and/or (c) where to find a clearer and more conventional example of tab control use at the top level would be greatly appreciated.
Given that there appears to be no clear and consistent way of approaching this, if (like me) you think "how hard can it be?", you might naively set out to see if you can create a new dialog-based application based on CDialog and manually convert it to CPropertyPage. You might fare better; I quickly hit a weird problem with the app compiling but not running - it could not load the window caption from the resource ID, despite the caption literally being there in black and white. Due to the weirdness, this was a red flag, and so to my mind problem (b) seems to be not worth the effort.
Without a CPropertySheet at the top level, there is no point having any kind of CPropertyPage or CMFCPropertyPage involved, making problem (a) pointless. That said, if you manually convert from (default) CDialogEx to CPropertyPage for your tab classes, it seems to have no problem compiling and running - the tabs just don't have any functionality that integrates with the main dialog.
Instead, I can now recommend working through the details of the video example even though it does strange things at first glance. You can achieve the desired result in more or less the same way as follows:
Use plain CDialogEx throughout, and so provide all the tab switching/showing/hiding/control work yourself (which the example demonstrates).
Create your main dialog and all tabs in the resource editor, adding the CDialog-based classes from there.
If you create your tab dialog resources via Insert Dialog (generic) instead of Add Resource (uses specific templates) then you will have to manually set certain dialog properties for each tab:
Border: Thin (or I preferred None)
Style: Child
System Menu: false
Title Bar: false
You can simplify the repositioning of the tabs (see below; it barely justifies the separate SetRectangle() function)
You should (probably) drive the tab changing from the TCN_SELCHANGE event instead of WM_LBUTTONDOWN
As noted, repositioning the tabs can be simplified to something like:
CRect tabRect;
m_tabControl.GetWindowRect( &tabRect );
for ( int i = 0; i < m_totalTabs; i++ )
{
m_pTabPage[ i ]->SetWindowPos( &wndTop, tabRect.left, tabRect.top, 0, 0,
i == m_currentTab? SWP_NOSIZE|SWP_SHOWWINDOW : SWP_NOSIZE|SWP_HIDEWINDOW );
}
Additional calls to ShowWindow() for each tab shown in the example are redundant.
One time, I had made on application as such. I don't remember if I used the Visual Studio new project wizard. Most probably, I did. But I will assist you on how to do it if you can not reach how to do it via the assistant.
The essential things:
Define the main dialog in the resources and the respective class in its H and CPP files.
In the YourAppClass::InitInstance you will need to have something like:
YourMainDlgClass dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
}
else if (nResponse == -1)
{
TRACE(traceAppMsg, 0, "Warning: dialog creation failed, so application is terminating unexpectedly.\n");
TRACE(traceAppMsg, 0, "Warning: if you are using MFC controls on the dialog, you cannot #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS.\n");
}
Insert a CMFCTabCtrl element in your dialog for tabs, instead of using a CPropertySheet.

Possible to Superclass a Dialog Box in Win32?

I'm using raw Win32 and C++ for a project. As I understand it, I am able to superclass Windows controls by retrieving the class information, replacing the procedure, then registering this as a new class and using it when creating a new window. Subclassing is done by replacing the window's procedure after the window is created. The advantage of superclassing is that you are able to process messages before CreateWindow() returns.
I'm looking to see if it's possible to superclass a dialog box created with CreateDialog() because I'd like to use a resource file for the dialog layout. The problem is that I don't know how I would provide my superclass when I create a dialog box. Is it even possible? Any idea how MFC handles this?
If you use an extended dialog box template to create your dialog, you can specify a custom window class as part of the DLGTEMPLATEEX definition.
The dialog manager will create and layout your dialog as normal, and call your window procedure for any dialog messages. You can use the DefDlgProc function to obtain default processing for any dialog messages you don't want to handle yourself.

Ole drag & drop troubleshooting

I have created a Dialog using WTL, inheriting from CDialogImpl class, inside the main dialog I have created another dialog and inside this second child dialog a list control. I wanted to add drag& drop functionality to the list control, I have read on the web about the WM_DROPFILES method and about the Ole Drop Target, I have chosen the later one.
I have implemented the IDropTarget interface methods in accordance to this.
I have set the accept files option to TRUE in all my dialogues, I have instantiated the IDropTarget in the child dialogue class and called the following methods in its WM_INITDIALOGUE handler:
//drag& drop
lpDropTarget = (LPDROPTARGET)new TDropTarget(m_hWnd);
CoLockObjectExternal(lpDropTarget, true, true);
// register the HWND as a drop target
RegisterDragDrop(m_hWnd, lpDropTarget);
lpDropTarget is instantiated as follows:
LPDROPTARGET lpDropTarget;
And that's about it. Haven't really used Ole before so I have tried the following scenarios in search of luck:
I have registered with RegisterDragDrop for the List control HWND and set it's parent dialogue to receive the notification WM_OLEDROP WM_USER + 1 that I send. Ok the cursor for drag & drop showed but no calls to the implementation of the functions of IDataTarget.
RegisterDragDrop for child dialogue & notifications for it also.
RegisterDragDrop for the main Dialog & notifications for it also.
In the later cases ( 2 & 3) the cursor for drag& drop didn't even appeared, it showed me the no-drag & drop allowed one.
Now, all the dialogues and list control have been set to accept files from the visual studio designer.
I am limited to using only ATL /WTL/WINAPI, no MFC.
What am I doing wrong?
For you WM_DROPFILES is the only thing you need to handle. No need to fiddle with IDropTarget etc.
If your application does not need to run in Admin mode, do not run it in admin mode. Disable the linker setting for the same (UAC Execution Level = asInvoker). Also, run VS in non-Admin mode, so that your application also starts are non-Admin process. This way, Explorer.exe would be able to send WM_DROPFILES message to your application.
If your application need to run as Admin mode, you need to allow other applications to send set of few messages to your application (window). Do the following:
ChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD);
ChangeWindowMessageFilter(WM_COPYDATA, MSGFLT_ADD);
ChangeWindowMessageFilter(0x0049, MSGFLT_ADD);
You may also want to use newer function: ChangeWindowMessageFilterEx.
Note that, if your application must run as Admin, and you need to dynamically locate one of these function using GetProcAddress, so that your application can run on OS where this function is not available (eg. Windows XP).

Ampersand accelerators cause a beep in a Win32 dialog

I have a dynamically created toolbar on a plain Win32 dialog. My buttons are added with & shortcuts which correctly puts underscore to characters following ampersand but pressing Alt+(char) causes a beep and the button is not clicked.
It has been a while since I have done Win32 API development. Is there something that needs to be done to a dynamically created child window (toolbar) in order for the accelerator keys to work?
This could be something really obvious that I am missing...
Well... You have to write code to handle these keypresses and convert them into WM_COMMAND messages. The traditional way to do this is to define an accelerator table and process them using TranslateAccelerator() - but of course, you're free to do it however you like... Just make sure the keys you handle jibe with the keys you underline!
You might also find this KB article helpful: How to use accelerator keys within a modal dialog box in Visual C++... Or, for a more in-depth (and MFC-free) look at implementing custom message processing in dialogs, check out Raymond Chen's articles on the dialog manager, specifically part 4: The dialog loop and part 9: Custom accelerators in dialog boxes (but seriously, read the whole thing, you know you want to...)
The beep indicates that the command isn't handled by any window in your app.
Since you created the toolbar dynamically, I would guess that the toolbar window isn't set up properly as a child window of your main window (i.e., it's parent and owner window are not set).
To test: click on the toolbar so it has the focus, then press Alt- and it should work.

How to create and add a custom made component to a Dialog based app (MFC)?

I want to make a custom made component (a line chart), that would be used in other applications.
I don't know 2 things:
Where should I use (within component class!) the methods for drawing, like FillRect
or PolyLine? In OnPaint handler that I should define and map it in MESSAGE MAP? Will
it (OnPaint handler) be called from OnPaint handler of the dialog of the application
or where from?
How to connect the component, once it is made, to the test application, which will
for example be dialog based? Where should I instantiate that component? From an
OnCreate method of the MyAppDialog.cpp?
I started coding in MFC few days ago and I'm so confused about it.
Thanks in advance,
Cheers.
Painting the control is handled exactly like it would be if it wasn't a control. Given that you're using MFC, that (at least normally) means you do the drawing in the View class' OnDraw (MFC normally handles OnPaint internally, so you rarely touch it).
Inserting the resulting ActiveX control in the host application will be done like inserting any other ActiveX control. Assuming you're doing your development in Visual Studio, you'll normally do that by opening the dialog, right clicking inside the dialog box, and clicking "Insert ActiveX Control..." in the menu that pops up. Pick your control from the list, and it'll generate a wrapper class for the control and code to create an object of that class as needed. From the viewpoint of the dialog code, it's just there, and you can use it about like any other control.
For create new component in MFC, you must create a class from the window class (CWND),
after that you can have your MessageMap for the component and your methods and also can override CWND::OnDraw method to draw the thing you want.
Before that I suggest you to take a look to device context
http://msdn.microsoft.com/en-us/library/azz5wt61(VS.80).aspx
Good Luck friend.