0
votes

Question:

Is there a way to catch the <Tab> character / event in an MFC CEdit so that I can override the default tab-order behaviour? In other words: is there a way to programmatically register and unregister a <Tab> handler?

Context:

I am aware of MFC's tab-ordering mechanism in which you specify an order, at compile time, in which to tab through visible controls. What I want to do is subclass CEdit so that pressing <Tab> while in this CEdit cycles the adjacent label (read-only CEdit) through a pre-set list of strings, and once the list is finished, pressing <Tab> again moves focus out and lets MFC's default tab-ordering take over again.

My gut tells me this should be possible, but I can't find any useful documentation. If MFC has a way to dynamically register and unregister for <Tab> events (or arbitrary key events) then this would be really easy, but I haven't found it.

Summary

Basically, let's say I have 4 edit boxes e1, e2, e3, e4; and a list of 3 strings; str1, str2, str3 that go into a label somewhere else on the dialog. If we start with the cursor in e1, I want the tab order to go like this:

  • e1
  • e2
  • e3
  • e3, str1
  • e3, str2
  • e3, str3
  • e4

Is this possible? Are there other ways of obtaining the same behaviour?

2

2 Answers

1
votes

There may be other 'elegant' ways to do this, but, I think this will work if I understand your description. You can use PreTranslateMessage like this to accomplish what you want.

enter image description here

BOOL CMFCApplication6Dlg::PreTranslateMessage(MSG* pMsg)
    {
    static int i = 0;

    if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_TAB)
            {
            CWnd* pFocusControl = GetFocus();
            if (pFocusControl->GetDlgCtrlID() == IDC_EDIT3)
                {
                if (i < 3)
                    {
                    CString msg;
                    msg.Format("Test %d", i++);
                    GetDlgItem(IDC_EDIT4)->SetWindowText(msg);
                    return TRUE;
                    }
                }
            }

        return CDialog::PreTranslateMessage(pMsg);
        }

In the above example code, I used 5 edit boxes placed horizontally across a dialog. The tab starts in the first edit on the left and will tab till it gets to the edit control before the disabled edit box. Hitting tab again will display 3 text messages (one at a time) in the disabled edit box. After the third message, the next tab will move to the fifth edit box on the right.

0
votes

Lets say you have 4 edit controls e1, e2, e3, e4. You want them to tab in this order: 3,2,4,1

Override OnInitDialog() and move them like so:

e3.SetWindowPos(&CWnd::wndTop,   0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
e2.SetWindowPos(&e3, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
e4.SetWindowPos(&e2, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
e1.SetWindowPos(&e4, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

It also works if you move them in reverse Tab order:

e1.SetWindowPos(0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
e4.SetWindowPos(0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
e2.SetWindowPos(0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
e3.SetWindowPos(0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

If you have to move/resize the controls later on then add SWP_NOZORDER flag to prevent changes to tab order.