CDialog - EndDialog from a MODELESS dialog? - winapi

The MS documentation (and others) "clearly" states:
... Because the normal OnOk and OnCancel member functions of a CDialog
object would call EndDialog, make sure your modeless dialog box does
not call those functions and instead overrides
Since CDialog::OnOk effectively calls CDialog::EndDialog, and that method looks like:
void CDialog::EndDialog(int nResult)
{
ASSERT(::IsWindow(m_hWnd));
if (m_nFlags & (WF_MODALLOOP|WF_CONTINUEMODAL))
EndModalLoop(nResult);
::EndDialog(m_hWnd, nResult);
}
we can also check the docs for ::EndDialog which again "clearly" state:
Dialog boxes created by the DialogBox, DialogBoxParam,
DialogBoxIndirect, and DialogBoxIndirectParam functions must be
destroyed using the EndDialog function. An application calls EndDialog
from within the dialog box procedure; the function must not be used
for any other purpose.
Yet, I have a CDialog derived class that has it's default behavior wrt. OnOKand seemingly everything is working when I use it non-modal / modeless.
That is:
* When I close the (modeless) dialog, it is closed/removed from view.
* The application doesn't show any memory leaks. (MFC debug build)
So what? Do I need to prevent EndDialog and call DestroyWindow myself or not?
Note: I know what the docs and "the web" says. It's just that I haven't yet found why I need to do it differently, and this one class should be usable for modeless and modal mode, so not having to do anything different might be handy.

The MSDN Docs for CDialog::OnOK clearly states
If you implement the OK button in a modeless dialog box, you must
override the OnOK method and call DestroyWindow inside it. Do not call
the base-class method, because it calls EndDialog which makes the
dialog box invisible but does not destroy it
So you would need to override CDialog::OnOK and call DestroyWindow() inside -- here's a modified example from MSDN:
class CDlg : public CDialog
{
...
BOOL m_bModal;
...
}
CDlg::CDlg(CWnd* pParent /*=NULL*/)
: CDialog(CDlg::IDD, pParent)
{
...
m_bModal = FALSE;
...
}
INT_PTR CDlg::DoModal()
{ m_bModal = TRUE;
const INT_PTR rval = CDialog::DoModal();
m_bModal = FALSE;
return rval;
}
void CDlg::OnOK()
{
if (!UpdateData(TRUE))
{
TRACE(_T("UpdateData failed during dialog termination\n"));
// The UpdateData routine will set focus to correct item
return;
}
if (m_bModal)
EndDialog(IDOK);
else
DestroyWindow();
}
void CDlg::OnCancel()
{
if (m_bModal)
EndDialog(IDCANCEL);
else
DestroyWindow();
}

Related

Outlook VSTO Handling SelectionChange correctly (currently doubleclick crashes Addin)

From what I understand you need to track Activation and Deactivation of the Explorers. During activation, you need to add SelectionChange event handlers for the current explorer.
This seems to work perfectly for single clicks on AppointmentItems. But it crashes the Addin when double-clicking on an appointment series and selecting a single Appointment.
Here is the source:
On class level
private Outlook.Explorer currentExplorer = null;
private Outlook.AppointmentItem currentAppointmentItem = null;
within Startup:
currentExplorer = this.Application.ActiveExplorer();
((Outlook.ExplorerEvents_10_Event)currentExplorer).Activate +=
new Outlook.ExplorerEvents_10_ActivateEventHandler(
Explorer_Activate);
currentExplorer.Deactivate += new
Outlook.ExplorerEvents_10_DeactivateEventHandler(
Explorer_Deactivate);
The event handlers:
void Explorer_Activate()
{
currentExplorer.SelectionChange += new Outlook.ExplorerEvents_10_SelectionChangeEventHandler(Selection_Change);
}
void Explorer_Deactivate()
{
currentExplorer.SelectionChange -= new Outlook.ExplorerEvents_10_SelectionChangeEventHandler(Selection_Change); ;
}
private void Close_Explorer()
{
}
private void Selection_Change()
{
Outlook.MAPIFolder selectedFolder = currentExplorer.CurrentFolder;
if (currentExplorer.Selection.Count > 0)
{
Object selObject = currentExplorer.Selection[1];
if (selObject is Outlook.AppointmentItem)
{
currentAppointmentItem = (Outlook.AppointmentItem)selObject;
}
else
{
currentAppointmentItem = null;
}
}
}
What am I overlooking? Is the form of deregistering a problem?
Try to add try/catch blocks to the event handlers. The Outlook object model can give you unpredictable results sometimes. It is worth adding them and find where an exception is thrown.
currentExplorer.Selection.Count
Also, you may subscribe to the SelectionChange event in the NewExplorer event and don't switch between explorers when they are activated or deactivated. The event is fired whenever a new explorer window is opened, either as a result of user action or through program code.
The only thing which I added was a handler for NewInspector and InspectorClose events along with Marshal.ReleaseComObject(). The only thing which I can imagine that double clicking while debugging I got in some kind of race condition (because double clicking also triggers the Selection_Change event). But this is only a guess.
You do not need to add and remove event handlers as an explorer is activated / deactivated. Are you trying to support multiple explorers? In that case, create a wrapper class that hold the Explorer object as it member and uses its methods as event handlers.

Which Windows API in C++ will help me in identifying which Dialog Class the Current ComboBox is using?

How can I know that for a particular ComboBox which Dialog Style is being used? Is there any Win32 API which can give me that information?
I am using CDialog for a few ComboBox, CDialogEx for some, and an in-house Dialog class, let's say Ctl3dDialogEx, for others. GetClassName() will return the Class name of the ComboBox (if I am passing a ComboBox Handler) which can be "CComboBox".
Is there any Win32 API where I will pass the ComboBox Handler and it will return back to me the Dialog class name, for eg : "CDialog", "CDialogEx", "Ctl3dDialogEx", etc?
Below code will help to understand maybe:
void ComboBox_DoSomeManipulation( HWND hldg , int n )
{
/*"hldg" is the handler of the Current ComBobox */
LPTSTR lpClassName;
int nMaxCount = 256;
/*This will return "CComboBox" as value in lpClassName */
GetClassName(hldg , lpClassName , _count_of(nMaxCount));
/*Is there any WIN API like above which can give */
/* Dialog class information like CDialog, CDialogEx */
/* which the ComboBox is using ? */
}
If your combo-box can somehow get hold of a genuine pointer to its parent window, then you can use dynamic_cast<CDialogEx*>(pParent) to see if it's CDialogEx (returns nullptr if not). You will need several separate checks, starting from the most derived class! So, if your Ctl3dDialogEx is derived from CDialogEx, then:
. . .
CWnd *pParent = pCombo->GetParent(); // This, as is, is not valid code!
if (dynamic_cast<Ctl3dDialogEx*>(pParent) != nullptr) {
// It's a Ctl3dDialogEx parent
}
else if (dynamic_cast<CDialogEx*>(pParent) != nullptr) {
// It's a CDialogEx
}
else { // Assuming no other options …
// It's a CDialog
}
I would recommend making an accessible (static?) copy of the parent window's this pointer during initialisation, if you can. But there are other ways …
For example, assuming you have control over the definition of ComboBox_DoSomeManipulation and when it's called, change the first argument from an HWND to a CWnd* and, when you call it, use this rather than this->m_hwnd. (But this depends on the structure of your code!)
There is no Windows API help since all those dialogs will be subclassing the Windows DIALOG class. If this is all in process, and you are using the same MFC instance, you might be able to do this:
CWnd* pWnd = CWnd::FromHandlePermanent(hdlg);
if (pWnd != NULL)
{
if (pWnd->GetRuntimeClass() == RUNTIME_CLASS(CDialog))
{
}
else if (pWnd->GetRuntimeClass() == RUNTIME_CLASS(CDialogEx))
{
}
else if (pWnd->GetRuntimeClass() == RUNTIME_CLASS(CDialogxyz))
{
}
}
Back in the old days, MS compilers used with MFC didn't play well with dynamic_cast<>, so generally, when using MFC, I don't use it. I probably should have more trust in it, but I was stuck using Visual C++ 6 until 2008, so I am probably a little jaded. The more "standard" "MFC way" is to use the MFC macros...
Another possible ways is something like:
if (CDialogxyz* pDlgxyz = DYNAMIC_DOWNCAST(CDialogxyz, pWnd))
{
}
else if (CDialogEx* pDlgEx = DYNAMIC_DOWNCAST(CDialogEx, pWnd))
{
}
else if (CDialog* pDlg = DYNAMIC_DOWNCAST(CDialog, pWnd))
{
}

Hide an MFC dialog window

I have written an MFC dialog based application which is launched by some another application. For now, I have not added any code. It is just the default files that I got. The other application can successfully launch my application.
I am trying to hide the window of my application when the other application launches it.
BOOL CMyApp::InitInstance()
{
CMyAppDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
}
else if (nResponse == IDCANCEL)
{
}
return FALSE;
}
I tried to use:
dlg.ShowWindow(SW_HIDE)
but it still does not hide the window.
How can I accomplish this task?
I'd suggest you have another problem someplace.
If you create a totally new, blank MFC app (Visual Studio 2010) then in App::InitInstance, setting SW_HIDE rather than SW_SHOW does cause the resultant window to be hidden.
BOOL CProj1App::InitInstance()
{
// boilerplate code
. . .
// The one and only window has been initialized, so show and update it
m_pMainWnd->ShowWindow(SW_HIDE); // WORKS!
m_pMainWnd->UpdateWindow();
return TRUE;
}
As soon as you call DoModal your dialog is doomed to be shown. There is only one workaround that successfully avoids focus/flicker problems. See my answer here: Hiding an MFC dialog box
Hence, your code should look like this:
BOOL CMyApp::InitInstance()
{
CMyAppDlg dlg;
dlg.SetVisible(FALSE); // Sets m_visible flag to FALSE.
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
}
else if (nResponse == IDCANCEL)
{
}
return FALSE;
}
Solution to the above issue. The InitInstance code should be as follows:
BOOL CMyApp::InitInstance()
{
CWinApp::InitInstance();
AfxEnableControlContainer();
CMyAppDlg dlg;
dlg.Create(IDD_MyAppUI_DIALOG,NULL);
dlg.ShowWindow(SW_HIDE);
dlg.UpdateWindow();
m_pMainWnd = &dlg;
return TRUE;
}
First of all let me address some issues with previous solutions.
chintan s:
Indeed dialog will be killed when function goes out of scope. It would be a valid solution if dialog was declared as a member variable of the app class.
Vikky:
No need to call Windows API, since dialog is derived from CWnd and it inherits ShowWindow member that take only one parameter: show command.
ixe013:
This solution will work, however, before dialog hides, it will flash, since ShowWindow is called before OnInitDialog is called.
Pete:
This won’t work, since, modal dialog starts before m_pMainWnd has any value assigned to it.
The solution is pointed by ixe013.
This is so far the only solution that works but you will have to declare member variable in you dialog class, as described in the article.
You must hide the dialog from the inside.
Overload OnInitDialog
Call CDialogEx::OnInitDialog()
Hide your window and return
Here is the code
BOOL CMyAppDlg::OnInitDialog()
{
BOOL result = CDialogEx::OnInitDialog();
this->ShowWindow(SW_HIDE);
return result; // return TRUE unless you set the focus to a control
}
There is another method with a sentinel value, YMMV.
The showWindow method has 2 variable.
handle of window
nCmdShow(Controls how the window is to be shown)
BOOL WINAPI ShowWindow(
In HWND hWnd,
In int nCmdShow
);
HWND hWnd = GetSafeHwnd();
ShowWindow(hWnd,SW_HIDE);
See HERE

Touch event handling in Blackberry

I try to implement simple touch event handling on Blackberry 9550 emulator, but it doesn't work. Actually, touchEvent never gets called, 'cos no text ever appears in the console. Also, I get an annoying "Full Menu" which appears on touching the screen.
Here's the code:
package mypackage;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.EventInjector.TouchEvent;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.VirtualKeyboard;
import net.rim.device.api.ui.container.MainScreen;
public class MyScreen extends MainScreen
{
public MyScreen()
{
super(NO_SYSTEM_MENU_ITEMS);
getScreen().getVirtualKeyboard().setVisibility(VirtualKeyboard.HIDE_FORCE);
add(new HandleTouch());
}
class HandleTouch extends Field {
protected void layout(int width, int height) {
setExtent(width, height);
}
public void paint(Graphics graphics) {
graphics.drawBitmap(0, 0, this.getWidth(), this.getHeight(), Bitmap.getBitmapResource("bg.png"), 0, 0);
}
public boolean isFocusable() { return true;}
protected boolean touchEvent(TouchEvent message) {
switch( message.getEvent() ) {
case TouchEvent.CLICK:
System.out.println("----------------------------->CLICK");
return true;
case TouchEvent.DOWN:
System.out.println("----------------------------->DOWN");
return true;
case TouchEvent.MOVE:
System.out.println("----------------------------->MOVE");
return true;
}
System.out.println("PRINT ME SOMETHING IN ANY CASE");
return false;
}
public HandleTouch() {
}
}
}
1). First of all, with this code
protected void layout(int width, int height) {
setExtent(width, height);
}
you are actually setting a VERY large size of the field. This because the BB UI framework passes max available/possible dimentions to layout(int width, int height) so the field should use some part within the passed values. In this specific case the width will be the width of the display (360 px) and the height is the max possible height of the VerticalFieldManager (the one your are adding screen fields to, it is implicitly present in the screen's internals) (1073741823 px). So, finally this may result in a very large Bitmap object that is required with the field in order to be painted and you can get an uncaught error "Bitmap is too large" (I did on Storm 9530).
So, the layout() should use some relatively small values, e.g.:
protected void layout(int width, int height) {
setExtent(Math.min(width, 360), Math.min(height, 480));
}
2).
Actually, touchEvent never gets called
Well, actually it does get called. To see that you should simply touch (versus click). Left button of the mouse simulates clicks (a sequence of TouchEvent.DOWN > TouchEvent.CLICK > TouchEvent.UNCLICK > TouchEvent.UP), right button simulates touches (a sequence of TouchEvent.DOWN > TouchEvent.UP).
3).
Also, I get an annoying "Full Menu" which appears on touching the screen.
This is because your field does not consume TouchEvent.UNCLICK event. For instance, with this code your field will not show the popup:
protected boolean touchEvent(TouchEvent message) {
return true;
}
But, that is a bad solution for the popup. It is better to understand what really causes the popup. If TouchEvent.UNCLICK event is not consumed then BB UI framework calls getContextMenu(int instance) and makeContextMenu(ContextMenu contextMenu, int instance) methods of the field. So in order to disable the popup (which is actually a ContextMenu created by the getContextMenu(int instance) you should override the getContextMenu(int instance) to be smth like this:
public ContextMenu getContextMenu(int instance) {
// just in case check if a context menu is requested
// in order not to disable other types of menu
boolean isContextMenu = (Menu.INSTANCE_CONTEXT == instance);
return isContextMenu ? null : super.getContextMenu(instance);
}
4). Finally I'd recommend to not change native/default behavior of touchEvent(TouchEvent message) method. You can just watch/log it, but don't change (always call its super version). This is because touch events handling is more complicated than it may look at first. It is very easy to get a tricky bug here. I do believe most programmers should not change the native behavior of touchEvent(TouchEvent message) unless they really want to create some custom UI component to work with touch gestures. Normally they just want to react on a click (to behave as a ButtonField), however for that you can simply override navigationClick(int status, int time) or navigationUnclick(int status, int time). The BB UI framework will call those methods when user clicks your field on a touch screen.
I would like to add extra info to Arhimed's answer, since this seems to be a landing page for googling touch events...
My experiences are not to contradict him, but to add possible solutions for future readers. I am using BB OS 5.0. My experiences have been with the Storm simulator, and a Torch device. My app was originally written for OS 4.5, so it might be running in some sort of compatibility mode.
1) As explained in his point 4, a Touch Event gets passed along to a Navigation Click event, if touchEvent(TouchEvent) returns false. If navigationClick(int, int) returns false, this prompts the system to display a ContextMenu.
2) On my system, I could not find a method getContextMenu(int). So I could not test his point 3. I presume this gets added in BB6 or later.
3) I did find getContextMenu() - i.e. it takes no parameters. I tried to override that method to return null.
The strange thing is that this method only gets called after the initial context menu popup is shown! The initial context menu popup (?) gets shown, with a button on it for "Full Menu". When that button is pressed, this method gets called, and can be used to populate the MainMenu that appears. ... strange...
However, it means that overriding that method did not solve my problem.
4) I was unable to get a solution by returning true in touchEvent(TouchEvent). I agree that this would have been bad form (hack), but I have learnt to hack a lot on the BB platform. However, scrolling list fields need to have the touch event passed up, so that the scroll works.
5) Eventually I found something similar to the OP's problem with TouchEvent.UNCLICK. It has taken me 18 months to find the method navigationUnClick(int, int). Similar to my point 1 above, an unhandled UNCLICK becomes a navigationUnClick(int, int) call, which can also lead to the context menu being shown.
So by adding similar logic to both navigationClick(int, int) & navigationUnClick(int, int), I was able to get my lists & touches to interact nicely.
This is just supplemental info, that may add to the accepted anser.

How to handle key press events with the QPlainTextEdit

I've been developing with QT for around a week now and am pleased to say that I'm picking it up really fast. I'm an intermediate C++ programmer but picking up some parts of QT is proving to be challenging. I need to process key press events from the QPlainTextEdit when the user presses enter and I presume that the solution will involve sub classing the widget. Can any of you smart guys give me a potential implementable solution?
To really understand Qt and event handling there are two key areas of the documentation you should read. The first is the overview on The Event System and the second is a very important bit which is a cleverly hidden link on that page for QCoreApplication::notify. They should really move that to the main page of the Event System documentation as it really makes things quite clear (to me at least).
If you only need to handle some messages sent to the control - like the key-presses - there is no need to subclass it. You can alternatively use the event filtering mechanism. Here is a simple example:
Provide virtual eventFilter method in one of your QObject-based classes (e.g. the window form class).
bool MyWindow::eventFilter(QObject *watched, QEvent *event)
{
if(watched == ui->myTargetControl)
{
if(event->type() == QKeyEvent::KeyPress)
{
QKeyEvent * ke = static_cast<QKeyEvent*>(event);
if(ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter)
{
// [...]
return true; // do not process this event further
}
}
return false; // process this event further
}
else
{
// pass the event on to the parent class
return QMainWindow::eventFilter(watched, event);
}
}
Install your class as the event filter for the target control. Form constructor is usually a good place for this code. In the following snippet this refers to the instance of class in which you implemented the eventFilter method.
ui->myTargetControl->installEventFilter(this);
i would try subclassing QPlainTextEdit and reimplementing QWidget::keyPressEvent:
void YourTextEdit::keyPressEvent ( QKeyEvent * event )
{
if( event->key() == Qt::Key_Return )
{
// optional: if the QPlainTextEdit should do its normal action
// even when the return button is pressed, uncomment the following line
// QPlainTextEdit::keyPressEvent( event )
/* do your stuff here */
event->accept();
}
else
QPlainTextEdit::keyPressEvent( event )
}
please try :
if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter){
//do something
}
in your keyPressEvent() function.

Resources