Board index » cppbuilder » Setting focus on next active control...

Setting focus on next active control...


2004-07-10 06:58:20 AM
cppbuilder1
Hello to all...
I'd like to set focus on the next enabled control in an modal when user
press a key like an 'enter' in one control...
These controls are commonly edit boxes...
I try to use TWinControl::SelectNext, but with no success... Compiler
told that it's not accessible...
Any ideas?
Thanks for the help...
Eduardo Jauch.
 
 

Re:Setting focus on next active control...

Eduardo Jauch < XXXX@XXXXX.COM >wrote:
Quote
I'd like to set focus on the next enabled control in an
modal when user press a key like an 'enter' in one control...
These controls are commonly edit boxes...
Is this a form that you have created? If not, what is the
source of the window?
~ JD
 

Re:Setting focus on next active control...

JD wrote:
Quote
Is this a form that you have created? If not, what is the
source of the window?

~ JD

I don't know if i understand your question...
I have a window (main form put by bcb5 when i create a new application)...
From this form, i call another (that i put after in my project with the
comand 'new form') with this line of code, when the user press a button:
MyForm->ShowModal();
In this form (MyForm), i have some controls (TEdit and TCheckBox)
What i want is move the focus from one TEdit or TCheckBox to the next in
the Tab Order when the user press enter in one of then...
If i couldn't give you your answer forgive me, but i really don't
understand what you mean with source of window... I'me new to windows
program... Many of the concepts are confuse to me many times... And i'm
still trying to develop my skills...
Thanks for the reply.
Eduardo Jauch.
 

{smallsort}

Re:Setting focus on next active control...

Quote
I'd like to set focus on the next enabled control in an modal when
user
press a key like an 'enter' in one control...
These controls are commonly edit boxes...

I try to use TWinControl::SelectNext, but with no success...
Compiler
told that it's not accessible...

Add a OnKeyDown handler to your form and insert this code (assuming
you have an OK button)
if (Key == VK_ENTER){
if (ActiveControl == OKButton)
ModalResult = mrOk;
else
PostMessage(this->Handle, WM_NEXTDLGCTL); // allow enter
key to move to next control
}
Regards,
Bruce
 

Re:Setting focus on next active control...

Eduardo Jauch < XXXX@XXXXX.COM >wrote:
Quote
[...] From this form, i call another [...]

MyForm->ShowModal();

[...] What i want is move the focus from one TEdit or
TCheckBox to the next in the Tab Order when the user press
enter in one of then...
You have a couple of options available to you. One is to add
an Application wide message handler that looks for the enter
key. The other is to add a form wide event that does basically
the same but is implemented a little differently.
To use the application wide method, you declare the function
in the main form's header and assign the function to the
global TApplication::OnMessage event:
class TMainForm : public TForm
{
__published:
private:
void __fastcall AppOnMessage( TMsg &Msg, bool &Handled );
public:
__fastcall TMainForm(TComponent* Owner);
};
//-------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent* Owner)
: TForm(Owner)
{
Application->OnMessage = AppOnMessage;
}
//-------------------------------------------------------------
void __fastcall TMainForm::AppOnMessage( TMsg &Msg, bool &Handled )
{
if( Msg.message == WM_KEYDOWN || Msg.message == WM_KEYUP )
{
if( Msg.wParam == VK_ENTER )
{
if( Screen->ActiveForm == MyForm )
{
if( you want to tab to the next control )
{
// see below for more
// change the key to VK_TAB
Msg.wParam = VK_TAB;
}
}
}
}
}
//-------------------------------------------------------------
If you have a control in the form that makes use of the enter
key, you'll need to use the global Screen->ActiveControl
variable to know what control has focus. You can brute force
the testing by checking against every control:
if( Screen->ActiveControl == MyForm->Edit1 )
{
}
else if( Screen->ActiveControl == MyForm->Edit2 )
{
}
else if( Screen->ActiveControl == MyForm->CheckBox1 )
{
}
or you can do something like:
TEdit* pEdit = dynamic_cast<TEdit*>( Screen->ActiveControl );
if( pEdit )
{
// the active control is an edit
}
else
{
TGroupBox* pBox = dynamic_cast<TGroupBox*>( Screen->ActiveControl );
if( pBox )
{
// the active control is a TGroupBox
}
else
{
// continue testing for the other classes
}
}
or you can:
if( Screen->ActiveControl->ClassNameIs("TEdit") )
{
// the active control is a TEdit
}
The other choice that you have is to add an OnKeyDown event to
the form as Bruce suggested. The actual code used to set focus
to the next control can be one of many things and what Bruce
posted is a valid method. However, he left out 2 very important
details.
The first is that for it to work as expected, you must set the
form's KeyPreview property to true. The second is that if you
don't replace the VK_ENTER value with something else, the
system bell will *always* sound if the control doesn't accept
returns.
Just as with the Application wide method, you need to define
the event in the header but this time it goes in the TMyForm
class instead of the MainForm and again, you assign the event
in the forms constructor:
class TMyForm : public TForm
{
__published:
void __fastcall FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift);
private:
public:
__fastcall TMyForm(TComponent* Owner);
};
//-------------------------------------------------------------
__fastcall TMyForm::TMyForm(TComponent* Owner)
: TForm(Owner)
{
KeyPreview = true;
OnKeyDown = FormKeyDown;
}
//-------------------------------------------------------------
void __fastcall TMyForm::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
{
if( Key == VK_ENTER )
{
// use the same techniques as above to determine if
// you want to tab to the next control but now you
// can use the form's ActiveControl propert instead
if( you want to change focus )
{
Key = 0;
Perform( WM_NEXTDLGCTL, 0, 0 );
}
}
}
//-------------------------------------------------------------
One type of control that can accept returns that I didn't
include a sample of testing for is a TMemo:
TMemo* pMemo = dynamic_cast<TMemo*>( ActiveControl );
if( pMemo )
{
if( ! pMemo->WantReturns )
{
Key = 0;
Perform( WM_NEXTDLGCTL, 0, 0 );
}
}
Quote
[...] but i really don't understand what you mean with source
of window
I needed to know if you were creating the window or if it was
some thing like the dialog that opens when you use the Windows
SHBrowseForFolder API.
The reason is that the source of the window dictates what you
have to do to accomplish what you want. Since you are creating
the form yourself, you have easy access to the controls and
their events. If not, it would be quite complicated.
Quote
Many of the concepts are confuse to me many times
To be clear, most people think of a form or dialog as a window
but Windows thinks of every single object as a window.
~ JD
 

Re:Setting focus on next active control...

Hello!
I founde some problem to implement the option that use de KeyPreview e
OnKeyDown from MyForm...
In this form i have 2 buttons... And when i hit the enter key, the form
is closed like if i press the button OK (that in MyForm is put as Custon
and with code of return mrNone)...
The code tha i used for FormKeyDown is:
if( Key == 13 )
{
if (ActiveControl == TISPButtonOK)
{
ModalResult = mrOk;
}
else
{
Key = 0;
Perform( WM_NEXTDLGCTL, 0, 0 );
}
}
If i have some mistake plese tell me... I try to use the code of both of
you (JD and Bruce)..
But anyway, if i put a breakpoint in first line, i figure that this
procedure is never valled when i press the key enter...
What is going on?
There´s some setting that put this type of behavior to the window?
How can i fix it?
 

Re:Setting focus on next active control...

Hello again!!
I discover that if i disabled the button OK, the code work fine...
But i need this button...
What i have to do?
Eduardo Jauch.
 

Re:Setting focus on next active control...

Thank's Bruce!
Work's fine...
But i dont understand this line:
if (ActiveControl == OKButton)
OKButton is what exactly? The name of the button OK?
Eduardo Jauch.
 

Re:Setting focus on next active control...

Eduardo Jauch wrote:
Quote
Hello again!!

I discover that if i disabled the button OK, the code work fine...
But i need this button...
What i have to do?

Eduardo Jauch.
Excuse me... I deleted the buttons and make news and this time they work
ok...
But i'm still don't understang why i have to do this... Becayse for me,
the buttons are same...
Only that in first time, i put the buttons mbOK and mbCancel and after i
changed the results from mrOk and mrCancel to mrNone...
Was some more action that i have to do? Like some other property that i
have to change?
Thanks!
Eduardo Jauch.
 

Re:Setting focus on next active control...

Eduardo Jauch < XXXX@XXXXX.COM >wrote:
Quote
[...] i put the buttons mbOK and mbCancel and after i
changed the results from mrOk and mrCancel to mrNone...
Bruce didn't explain that part and I didn't include it because
you clearly showed that you were using MyForm->ShowModal but
you were not checking the returned result. If you need to
check if the user clicked OK or Cancel, then you would do
something like:
if( MyForm->ShowModal() == mrOK )
{
// the user clicked the OK button
}
else
{
// the user clicked the Cancel button
}
but for that to work, you have to set the button's ModalResult
property to mrOK and mrCancel or use the method in my other
post.
~ JD
 

Re:Setting focus on next active control...

Eduardo Jauch < XXXX@XXXXX.COM >wrote:
Quote
[...] The code tha i used for FormKeyDown is:

if( Key == 13 )
{
if (ActiveControl == TISPButtonOK)
{
ModalResult = mrOk;
}
else
{
Key = 0;
Perform( WM_NEXTDLGCTL, 0, 0 );
}
}
You are programming using C++. You should get into the habit
of using C++ code instead of old style C. In this case you
should be using the virtual key code ( VK_xx ) instead of the
actual integer value.
You also failed to check if the user pressed enter while the
Cancel button had focus.
~ JD
 

Re:Setting focus on next active control...

Eduardo Jauch < XXXX@XXXXX.COM >wrote:
Quote
I discover that if i disabled the button OK, the code work
fine...
There are a bunch of different combinations of things that you
can do. You just have to know how things work to decide what's
best for you.
It still hasn't been established that you need the modal
result but since your OnKeyDown event is setting the result,
I'll assume that you want to use it.
If that's correct, I would suggest that set the button's
ModalResult's to mrOK and mrCancel and make sure that the
Default property for each button is set to false. Then use the
following:
void __fastcall TMyForm::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
{
if( Key == VK_ENTER )
{
if( ActiveControl != TISPButtonOK && ActiveControl != TISPButtonCancel )
{
Key = 0;
Perform( WM_NEXTDLGCTL, 0, 0 );
}
}
}
Now to get the desired resilts, you need to set each control's
TabStop property. If you don't want the control in the TabOrder,
set TabStop to false.
Next, set each control's (that has TabStop = true) TabOrder.
Start with the control that you want to have focus when the
form first opens and set it's TabOrder to 1 and set the next
to 2 and so on.
Finish by setting the form's ActiveControl property to the
control that you want to have focus when the form first opens.
You can do this in the IDE or by setting it in the form's
constructor (same place I showed where to set the KeyPreview
property):
ActiveControl = Edit1;
One final note: I'm not sure what will happen if the user
closes the form by clicking the system close button or by
using the system menu. If you find that it causes a problem
for you, you will have to either remove the form's biSystem
buttons or change your code.
If you have to change your code, set the button results to
mrNone and add OnClick events for both buttons, add an OnClose
event for the form and use the OnKeyDown sample where you set
the ModalResult if the button(s) was focused. You would also
need to add a global bool flag:
// --- in the MyForm header
private:
bool ResultFlag;
//-------------------------------------------------------------
__fastcall TMyForm::TMyForm(TComponent* Owner)
: TForm(Owner)
{
ResultFlag = false;
KeyPreview = true;
OnKeyDown = FormKeyDown;
}
//-------------------------------------------------------------
void __fastcall TMyForm::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
{
if( Key == VK_ENTER )
{
if( ActiveControl == TISPButtonOK )
{
ModalResult = mrOk;
}
else if( ActiveControl == TISPButtonCancel )
{
ModalResult = mrCancel;
}
else
{
Key = 0;
Perform( WM_NEXTDLGCTL, 0, 0 );
}
}
}
//-------------------------------------------------------------
void __fastcall TMyForm::OKBtnClick(TObject *Sender)
{
ModalResult = mrOk;
}
//-------------------------------------------------------------
void __fastcall TMyForm::CancelBtnClick(TObject *Sender)
{
ModalResult = mrCancel;
}
//-------------------------------------------------------------
void __fastcall TTIDEAtumFormulas::FormClose(TObject *Sender, TCloseAction &Action)
{
if( !ResultFlag ) ModalResult = mrCancel;
}
//-------------------------------------------------------------
~ JD
 

Re:Setting focus on next active control...

JD wrote:
Quote
Eduardo Jauch < XXXX@XXXXX.COM >wrote:

You are programming using C++. You should get into the habit
of using C++ code instead of old style C. In this case you
should be using the virtual key code ( VK_xx ) instead of the
actual integer value.

You also failed to check if the user pressed enter while the
Cancel button had focus.

~ JD

Sorry...
Old habits...
I think i need buy a good book of c++ to study more... I never upgrade
completely from my old Turbo C do Borland C++...
In this case i receve an error, because the compiler not recognize the
VK_... I swap the VK_ for 13 only to make the code work... In what
header file is located the VK_xx statments?
Thanks for the help!
I appreciated very much!
 

Re:Setting focus on next active control...

JD wrote:
Quote

It still hasn't been established that you need the modal
result but since your OnKeyDown event is setting the result,
I'll assume that you want to use it.

Please, forgive me... was an paste error... I really test the result,
because i depend the return value to do some operations with the data in
the form...
Quote
If that's correct, I would suggest that set the button's
ModalResult's to mrOK and mrCancel and make sure that the
Default property for each button is set to false.
I think that was de Default property that was true and cause to close my
form when i hit the enter key!
Now work fine!
Quote

One final note: I'm not sure what will happen if the user
closes the form by clicking the system close button or by
using the system menu. If you find that it causes a problem
for you, you will have to either remove the form's biSystem
buttons or change your code.
Really causes... Thanks! i disabled it now!
Thanks for the help!!!!
 

Re:Setting focus on next active control...

Hi, Eduardo
Quote
Work's fine...
But i dont understand this line:

if (ActiveControl == OKButton)

OKButton is what exactly? The name of the button OK?
My intention was to allow a default button (usually an OK button, but
not always) to close and accept the form. Normally that is what
happens when you press Enter in a dialog. OKButton is the pointer to
your default button, eg. in the form's decalaration:
TButton* OKButton;
When the user presses enter when the OKButton is active, the form
closes.
FWIW, I've used this technique in the past for users who weren't
familiar with Windows. It seemed natural that enter moves to the next
field. But once they got used to the Windows interface (and using the
tab key) this trick started to seem awkward and unexpected. So I've
stopped using it (YMMV).
Regards,
Bruce