I am trying to capture WM_KEYDOWN to process some keyboard events at the Form level. I know I can use KeyPreview=true and use the OnKeyDown event of TForm. However, I'd like to do it in the WndProc() to simplify event processing in a single function. The problem is, it doesn't fire in the WndProc(). Is there a particular reason why it doesn't? Aside from using BEGIN_MESSAGE_MAP to handle WM_KEYDOWN, is there another way?
void __fastcall TForm1::WndProc(TMessage &fMessage)
{
switch (fMessage.Msg)
{
default: break;
case WM_KEYDOWN: {
TWMKeyDown KDMsg = reinterpret_cast<TWMKeyDown&>(fMessage);
TShiftState KDSs = KeyDataToShiftState(KDMsg.KeyData);
if (KDMsg.CharCode == 'C' && KDSs.operator ==(TShiftState() << ssCtrl))
{
// Process CTRL+C key
fMessage.Result = 1;
return;
}
}
break;
}
TForm::WndProc(fMessage);
}
Keyboard (and mouse) messages are posted to the window that has input focus. If a TForm has a child control that has the focus, the messages will go directly to the child's WndProc, not the Form's WndProc.
That is where KeyPreview comes into play. When true, it tells child controls to forward their keyboard messages to their parent TForm for processing.
Otherwise, you can alternatively use the TApplication(Events)::OnMessage event to handle input messages before they are dispatched to their target windows.
Related
I have a number of TStringGrids inside TTab's and have been using
if (Key==VK_RETURN) for years (its an old app) as the trigger to execute code pertaining the the cell entry.
I would now like the option to delete the Cell contents, by writing a empty string to it, so tried using
if (Key==VK_DELETE)
yet the Delete Button does not trigger the OnKeyPress event at all in my case.
I was hoping to capture the event via the following code pertaining to the specific active Stringrid1
void __fastcall TPagesDlgLoadEditorFixed::StringGrid1KeyPress(TObject
*Sender, char &Key)
{
int Grid = 1;
EntriesOnStringGrid(Key, Grid);
}
and with the KeyPress then passed on code that is common to all the StringGrids, as follows:
void TPagesDlgLoadEditorFixed::EntriesOnStringGrid(char &Key, int Grid)
{
if (Key==VK_RETURN)
{
//code works fine
}
if (Key==VK_DELETE)
{
//code has no effect
}
}
However whilst in debug mode, one can see that the StringGrid1KeyPress event is not triggered at all by the "Delete" button.
Any advise would be greatly appreciated.
It's generally better to use OnKeyUp and OnKeyDown when checking for virtual key codes instead of OnKeyPress.
So using that should fix your problem and then it will detect VK_DELETE when it's pressed or let go.
I am using C++Builder XE4 32bit VCL platform. I am writing for Windows OS.
I have a MainForm with a lot of components on it. When I press a keyboard arrow key and the Form's OnShortCut event is triggered, how do I determine which component has the program focus?
I have different actions which must be taken based on which component has the focus.
void __fastcall TMainForm::FormShortCut(TWMKey &Msg, bool &Handled)
{
//determine which component has the focus.
}
Use the global Screen->ActiveControl property:
Indicates which control currently has input focus on the screen.
Read ActiveControl to learn which windowed control object in the active form currently receives the input from the keyboard.
void __fastcall TMainForm::FormShortCut(TWMKey &Msg, bool &Handled)
{
TWinControl *ctrl = Screen->ActiveControl;
if (ctrl == Control1)
{
// do something...
}
else if (ctrl == Control2)
{
// do something else...
}
// and so on...
}
Or, you can use the Form's own ActiveControl property:
Specifies the control that has focus on the form.
Use ActiveControl to get or set the control that has focus on the form. Only one control can have focus at a given time in an application.
If the form does not have focus, ActiveControl is the control on the form that will receive focus when the form receives focus.
void __fastcall TMainForm::FormShortCut(TWMKey &Msg, bool &Handled)
{
TWinControl *ctrl = this->ActiveControl;
if (ctrl == Control1)
{
// do something...
}
else if (ctrl == Control2)
{
// do something else...
}
// and so on...
}
My application is using two forms. When I click a panel in main form, the Form2 should showing up. There're a few pixels distance between the Main Form and Form2.
Now what I need is when I move the Main Form to anywhere, then Form2 moves where ever Main Form goes. I mean I need Form2 to be lock on Main Form.
Have the MainForm override the virtual WndProc() method to reposition Form2 as needed relative to the MainForm's current position whenever a WM_WINDOWPOSCHANGING or WM_WINDOWPOSCHANGED message is received.
class TMainForm : public TForm
{
...
protected:
void __fastcall WndProc(TMessage &Message);
...
};
...
#include "Form2.h"
...
void __fastcall TMainForm::WndProc(TMessage &Message)
{
TForm::WndProc(Message);
switch (Message.Msg)
{
case WM_WINDOWPOSCHANGING:
case WM_WINDOWPOSCHANGED:
{
if ((Form2) && (Form2->Visible))
{
Form2->Left = ...; // such as this->Left
Form2->Top = ...; // such as this->Top + this->Height
}
break;
}
}
}
in my sample project (c++ vs10) i placed an ActiveX control in an Dialog (used as PropertyPage). The Dialog is connected to a CPropertyPage derived class called CTestPage. If i open a PropertySheet with DoModal, where the CTestPage is added, the ActiveX control appears, but i cant handle it by keyboard. If i click the ActiveX control (e.g. Calendar Control 8.0) the control receives the click and works fine. But no keyboard stroke is handled by the control. I tested ten different ActiveX controls on my developer machine -> always the same behavior.
If i place the ActiveX control in a normal Dialog everything works fine.
Any hints what i do wrong?
Thanks a lot
heribert
PS. Yes, AfxEnableControlContainer is called in InitInstance.
A PropertyPage sends WM_GETDLGCODE to every control to check out, which keyboard input can be handled by the control.
All the ActiveX controls i have tested results with 0 as answer on the request! Cool, so no one will receive any keyboard input.
The ActiveX Control i will use is written by me in c#. The ActiveX layer is needed to offer the complex c# control to c++.
But the underlying complex c# control returns with a 0 on a WM_GETDLGCODE message, too.
So i subclassed the c# control like the following code and now it works fine!!
internal class SubclassedComplexControl : ComplexControl
{
[SecurityPermission(SecurityAction.LinkDemand,
Flags = SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref System.Windows.Forms.Message m)
{
const int WM_GETDLGCODE = 0x0087;
const int DLGC_WANTARROWS = 0x0001;
const int DLGC_WANTALLKEYS = 0x0004;
const int DLGC_WANTCHARS = 0x0080;
const int VK_ESCAPE = 0x1B;
const int VK_RETURN = 0x0D;
if (m.Msg == WM_GETDLGCODE)
{
if (m.WParam.ToInt32() == VK_RETURN || m.WParam.ToInt32() == VK_ESCAPE)
m.Result = (IntPtr) DLGC_WANTALLKEYS;
else
m.Result = (IntPtr)(DLGC_WANTARROWS | DLGC_WANTCHARS);
return;
}
base.WndProc(ref m);
}
}
I am dealing with KeywordFilterField which is populated with the list of the countries. Now the problem i am facing is that when i click the particular country it should move to the next screen as I have written the pushScreen code in the trackwheelClick event after checking that _keywordFilterField is focusable or not, but this is not the case. The Menu opens when trackwheelClick event is fired, at the center of the screen, rather than moving onto the next screen.Can anybody have the idea why Menu dialog opens on the trackwheelclick event instead of going to the next screen.
See what i have done on trackwheel click event:
protected boolean trackwheelClick(int status, int time) {
if (_keywordFilterField.isFocus()) {
int index = _keywordFilterField.getSelectedIndex();
ReadableList readableList = _keywordFilterField.getResultList();
Object selectedCountry = readableList.getAt(index);
String countryName=selectedCountry.toString();
urlutf8Encoder=new URLUTF8Encoder();
String newCountry=urlutf8Encoder.encode(countryName);
pushToSearchResult(newCountry,countryName);//To Next SCreen
return true;
}
return false;
}
But it looks like:
The trackwheelClick event returns a boolean that indicates whether the event is consumed. Once an event is consumed, it stops propagating to other UI elements. If you return true, the menu will stop appearing.