James Kent
James Kent

Reputation: 5933

MFC C++ on screen keyboard as parent dialog

I want to extend the functionality of a third party application which uses keyboard shortcuts for use on a touch screen monitor. My plan to achieve this was to make an on screen keyboard to feed keystrokes (virtual) to the application and to change the window style to embed it into the keyboard.

As the application (Mach3) supports extending the functionality with plugins this is how I wanted to achieve my goal.

For the embedding I modified the code from here so that on loading mach3, the application dialog is immediately embedded into my dialog.

For the keyboard functionality I used code from here to give the window the WS_EX_NOACTIVATE style and handle being moved correctly.

The problem that I have is that when I give focus to another window (for instance notepad) and press a button to generate a keypress, the keypress is received correctly by the other application, however when I give the focus to the embedded window and press a button, the focus is shifted to the button and no keypress is received by the child dialog.

How do I prevent the focus from being changed like this? Is this a simple thing I'm missing or do I need to drastically change my approach?

I would post some code, but there is rather a lot of it and I'm not sure which bits would be the most relevant to solving this problem.

Upvotes: 0

Views: 949

Answers (1)

James Kent
James Kent

Reputation: 5933

I figured out the cause of my problem, which now allows me to create an embedded dialog with the parent dialog acting as an on screen keyboard.

firstly as commented above I needed to make some changes to my keyboard code, specifically the overridden OnNcLButtonDown which the article says should be:

void COnScreenKeyboardDlg::OnNcLButtonDown(UINT nHitTest, CPoint point)
{
 if (!m_hForegroundWnd)
 {
  // store current foreground window
  m_hForegroundWnd = ::GetForegroundWindow();
  // temporarily make this dialog an activatable window
  ModifyStyleEx(WS_EX_NOACTIVATE,0);
  // make this dialog foreground
  SetForegroundWindow();
 }
 CDialog::OnNcLButtonDown(nHitTest, point);
}

but should actually be:

void CWrapperDialog::OnNcLButtonDown(UINT nHitTest, CPoint point) {
    SetForegroundWindow();
    CDialog::OnNcLButtonDown(nHitTest, point);
}

secondly the article says to override OnMouseMove with:

void COnScreenKeyboardDlg::OnMouseMove(UINT nFlags, CPoint point)
{
 if (m_hForegroundWnd)
 {
  // make the previous foreground window active
  ::SetForegroundWindow(m_hForegroundWnd);
  // make this dialog non-activating
  ModifyStyleEx(0,WS_EX_NOACTIVATE);
  // set it to NULL to mark that we do not need to do this again
  m_hForegroundWnd = NULL;
 }
 CDialog::OnMouseMove(nFlags, point);
}

the comment below "corrects" this to:

void CWrapperDialog::OnMouseMove(UINT nFlags, CPoint point) {
    ::SetActiveWindow(0);
    CDialog::OnMouseMove(nFlags, point);
}

however I found that with this code I would get flashing from the focus changing whenever I moved the mouse, so I omitted it entirely.

Lastly the keyboard example used DoModal to create the dialog, whereas I was using Create

I created a wrapper function I could call with AfxBeginThread:

UINT CWrapperDialog::ThreadDoModal(LPVOID pParam){
    CWrapperDialog* dlg;
    dlg = ((CWrapperDialog*)pParam);
    dlg->DoModal();
    return 0;
}

and called it:

dlg = new CWrapperDialog;
AfxBeginThread(dlg->ThreadDoModal, dlg);

and now the messages are being passed correctly.

Thanks to o_weisman for the tip regarding the changes to the example as this made a massive difference.

Upvotes: 1

Related Questions