I have a TWinControl that needs to catch WM_POWERBROADCAST messages, but they never seem to reach it despite adding the message handler to the control's VCL_MESSAGE_MAP. I've also tried a custom WndProc() and that also never receives these messages. Other messages are working fine.
I can catch the message successfully in the main form, but it's never passed to my controls.
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(WM_PAINT, TMessage, WMPaint); // Works
VCL_MESSAGE_HANDLER(WM_ERASEBKGND, TMessage, WMEraseBackground); // Works
VCL_MESSAGE_HANDLER(WM_POWERBROADCAST, TMessage, WMPower); // Doesn't work!
END_MESSAGE_MAP(inherited);
WM_POWERBROADCAST is sent only to top-level windows, never to child windows. So, you have a few choices:
have your WinControl intercept the message that is sent to the hidden TApplication window by using the TApplication.HookMainWindow() method. Be sure to remove the hook when your WinControl is destroyed.
__fastcall TMyControl::TMyControl(TComponent *Owner)
: TWinControl(Owner)
{
Application->HookMainWindow(&AppHook);
}
__fastcall TMyControl::~TMyControl()
{
Application->UnhookMainWindow(&AppHook);
}
bool __fastcall TMyControl::AppHook(TMessage &Message)
{
if (Message.Msg == WM_POWERBROADCAST)
{
// ...
}
return false;
}
intercept the message that is sent to the TForm window, either by applying a MESSAGE_MAP to the Form class, or by overriding the Form's virtual WndProc() method, and then have the From forward the message to your WinControl.
BEGIN_MESSAGE_MAP
...
VCL_MESSAGE_HANDLER(WM_POWERBROADCAST, TMessage, WMPowerBroadcast);
END_MESSAGE_MAP(inherited);
...
void __fastcall TForm1::WMPowerBroadcast(TMessage &Message)
{
inherited::Dispatch(&Message);
MyControl->Perform(Message.Msg, Message.WParam, Message.LParam);
}
Or:
void __fastcall TForm1::WndProc(TMessage &Message)
{
inherited::WndProc(Message);
if (Message.Msg == WM_POWERBROADCAST)
MyControl->Perform(Message.Msg, Message.WParam, Message.LParam);
}
have your WinControl create its own hidden top-level window by using the RTL's AllocateHWnd() function.
private:
HWND FPowerWnd;
void __fastcall PowerWndProc(TMessage &Message);
...
__fastcall TMyControl::TMyControl(TComponent *Owner)
: TWinControl(Owner)
{
FPowerWnd = AllocateHWnd(&PowerWndProc);
}
__fastcall TMyControl::~TMyControl()
{
DeallocateHWnd(FPowerWnd);
}
void __fastcall TMyControl::PowerWndProc(TMessage &Message)
{
if (Message.Msg == WM_POWERBROADCAST)
{
// ...
}
else
{
Message.Result = ::DefWindowProc(FPowerWnd, Message.Msg, Message.WParam, Message.LParam);
}
}
Related
TComboBox in TStringGrid does not work when used from the keyboard. It does not update the Cells value.
I expected it to work from the keyboard when it works when I use the mouse. How should I change the code to make it work from the keyboard? Or is it a dead case?
Thanks Mika
void __fastcall TForm1::ChangeStringGridComboBox(TObject* Sender)
{
TComboBox* combobox = dynamic_cast<TComboBox*>(Sender);
if (combobox && combobox->ItemIndex > -1) {
StringGrid1->Cells[StringGrid1->Col][StringGrid1->Row] =
combobox->Items->Strings[combobox->ItemIndex];
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::StringGrid1CreateCustomEditor(
TObject* Sender, TColumn* const Column, TStyledControl*&Control)
{
TComboBox* combobox = new TComboBox(this);
if (Column == Column1) {
Control = combobox;
combobox->Items->Assign(Memo1->Lines);
combobox->ItemIndex = combobox->Items->IndexOf(
StringGrid1->Cells[StringGrid1->Col][StringGrid1->Row]);
if (combobox->ItemIndex > -1) {
StringGrid1->Cells[StringGrid1->Col][StringGrid1->Row] =
combobox->Items->Strings[combobox->ItemIndex];
}
combobox->OnChange = &ChangeStringGridComboBox;
}
}
From Firemonkey: How do I use a TComboBox in a TStringGrid to make it work from the keyboard?:
To use a TComboBox in a TStringGrid and make it work from the keyboard, you can follow these steps:
Place a TComboBox component on your form and set its Parent property to the TStringGrid.
Set the TComboBox's Visible property to False.
Handle the TStringGrid's OnSelectCell event. In the event handler, check if the current column of the selected cell is the column that the TComboBox is associated with. If it is, set the TComboBox's Left and Top properties to the coordinates of the selected cell, and set its Visible property to True.
Handle the TStringGrid's OnKeyPress event. In the event handler, check if the key pressed is the down arrow key. If it is, and the TComboBox is visible, set the focus to the TComboBox.
Handle the TComboBox's OnExit event. In the event handler, set the TComboBox's Visible property to False, and set the focus back to the TStringGrid.
In the TComboBox's OnSelect event, you can get the selected value and assign it to the selected cell of the TStringGrid.
You can also create a custom component that inherits from TStringGrid and add the TComboBox and the necessary events to it.
Since you are creating the TComboBox as a custom InplaceEditor, most of that should already be handled for you by the TStringGrid. All you are really missing is the Key Press handler, eg:
// Unlike VCL, FMX does not expose access to TStringGrid's
// active Editor, so keep track of it manually...
protected:
void __fastcall Notification(TComponent* AComponent, TOperation Operation) override;
private:
TComboBox *myComboBox;
...
void __fastcall TForm1::StringGrid1CreateCustomEditor(
TObject* Sender, TColumn* const Column, TStyledControl* &Control)
{
if (Column == Column1) {
myComboBox = new TComboBox(this);
myComboBox->Items->Assign(Memo1->Lines);
myComboBox->ItemIndex = myComboBox->Items->IndexOf(
StringGrid1->Cells[StringGrid1->Col][StringGrid1->Row]);
myComboBox->OnChange = &ChangeStringGridComboBox;
Control = myComboBox;
}
}
void __fastcall TForm1::ChangeStringGridComboBox(TObject* Sender)
{
if (myComboBox->ItemIndex > -1) {
StringGrid1->Cells[StringGrid1->Col][StringGrid1->Row] =
myComboBox->Items->Strings[myComboBox->ItemIndex];
}
}
void __fastcall TForm1::StringGrid1KeyPress(
TObject* Sender, Word &Key, WideChar &KeyChar, TShiftState Shift)
{
if ((Key == vkDown) && StringGrid1->EditorMode)
{
Key = 0;
myComboBox->SetFocus();
}
}
void __fastcall TForm1::Notification(TComponent* AComponent, TOperation Operation)
{
TForm::Notification(AComponent, Operation);
if ((Operation == TOperation::opRemove) && (AComponent == myComboBox)) {
myComboBox = nullptr;
}
}
With the following changes, TComboBox works fine:
ChangeStringGridComboBox
{
...
temp = myComboBox->Items->Strings[myComboBox->ItemIndex];
}
StringGridEditingDone
{
if (Column1 == StringGrid1->ColumnByIndex(ACol) && temp.Length()) {
StringGrid1->Cells[StringGrid1->Col][StringGrid1->Row] = temp;
temp = "";}
}
I am using C++Builder from Embarcadero Technology. The built in OnClick event handler does not identify if the mouse click is the left or right button. Is there a function I can call to manually fill the values for TMouseButton. Below is the OnClick event handler?
void __fastcall TForm::ListBox1Click(TObject *Sender)
{
TMouseButton Button;
Button = ???
}
As others have mentioned, you can use the OnMouseDown event to remember the current mouse button state for use in OnClick, eg.
private:
bool LButtonDown;
bool RButtonDown;
...
void __fastcall TForm1::ListBox1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
switch (Button) {
case mbLeft:
LButtonDown = true;
break;
case mbRight:
RButtonDown = true;
break;
}
}
void __fastcall TForm1::ListBox1MouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
switch (Button) {
case mbLeft:
LButtonDown = false;
break;
case mbRight:
RButtonDown = false;
break;
}
}
void __fastcall TForm::ListBox1Click(TObject *Sender)
{
if (LButtonDown) ...
if (RButtonDown) ...
}
If you don't want to do that way, you can use the Win32 API GetKeyState() or GetAsyncKeyState() function to query the current state of the mouse's left and right buttons, using the VK_LBUTTON and VK_RBUTTON virtual key codes, eg:
void __fastcall TForm::ListBox1Click(TObject *Sender)
{
if (GetKeyState(VK_LBUTTON)) ...
if (GetKeyState(VK_RBUTTON)) ...
}
The correct event to use for details of mouse click events is OnMouseDown (also OnMouseUp and OnMouseMove).
Override the event and then implement MouseDown event like this
void __fastcall TMyListView::MouseDown(System::Uitypes::TMouseButton Button, System::Classes::TShiftState Shift, int X, int Y)
{
if (Button == mbLeft){
}
if (Button == mbRight){
}
}
See also Vcl.Controls.TControl.OnMouseDown in Embarcadero's documentation.
I want to implement a collection or list using TOwnedCollection / TCollectionItem. I need a persistent list (to load and create from a FileStream) of classes with polymorphism.
Here is (part of) my code so far, but I didn't succeed to create the derived class TGenerator instead of its parent TPowerComponent and add it to the Collection.
//-------------------------------------------------------------------------------------
class TPCCollection : public TOwnedCollection
{
typedef TOwnedCollection inherited;
private:
TPowerComponent* __fastcall GetPowerComponent(int Index);
void __fastcall SetPowerComponent(int Index, TPowerComponent *Value);
public:
__fastcall TPCCollection(TPersistent *Owner);
HIDESBASE TPowerComponent* __fastcall Add(void);
HIDESBASE TPowerComponent* __fastcall Insert(int Index);
__property TPowerComponent* PCCollection[int Index] = {read=GetPowerComponent, write=SetPowerComponent};
};
//-------------------------------------------------------------------------------------
class TPowerComponent : public TCollectionItem
{
typedef TCollectionItem inherited;
public :
int X, Y, Rotation;
PowSymbType HisType;
__fastcall TPowerComponent(TCollection *Collection, PowSymbType AType );
void __fastcall Assign(TPersistent *Source);
virtual void __fastcall Paint(TCanvas * Canvas);
};
//-------------------------------------------------------------------------------------
class TGenerator : public TPowerComponent
{
typedef TPowerComponent inherited;
public :
double PG, Qgmin, Qgmax, Vsch;
__fastcall TGenerator(TCollection *Collection, PowSymbType AType );
void __fastcall Assign(TPersistent *Source);
virtual void __fastcall Paint(TCanvas * Canvas);
};
//-------------------------------------------------------------------------------------
// implementation
//-------------------------------------------------------------------------------------
// TPCCOllection
//-------------------------------------------------------------------------------------
__fastcall TPCCollection::TPCCollection(TPersistent *Owner)
: TOwnedCollection(Owner, __classid(TPowerComponent))
{
}
//-------------------------------------------------------------------------------------
TPowerComponent* __fastcall TPCCollection::Add()
{
return static_cast<TPowerComponent>(inherited::Add());
}
//-------------------------------------------------------------------------------------
TPowerComponent* __fastcall TPCCollection::Insert(int Index)
{
return static_cast<TPowerComponent>(inherited::Insert(Index));
}
//-------------------------------------------------------------------------------------
TPowerComponent* __fastcall TPCCollection::GetPowerComponent(int Index)
{
return static_cast<TPowerComponent>(inherited::GetItem(Index));
}
//-------------------------------------------------------------------------------------
void __fastcall TPCCollection::SetPowerComponent(int Index, TPowerComponent *Value)
{
inherited::SetItem(Index, Value);
}
//-------------------------------------------------------------------------------------
// TPowerComponent
//-------------------------------------------------------------------------------------
__fastcall TPowerComponent::TPowerComponent(TCollection *Collection, PowSymbType AType )
: TCollectionItem(Collection)
{
HisType=AType;
Rotation=0;
}
//-------------------------------------------------------------------------------------
void __fastcall TPowerComponent::Assign(TPersistent *Source)
{
TPowerComponent *Src = dynamic_cast<TPowerComponent>(Source);
if( Src )
{
// copy members from Src...
}
else inherited::Assign(Source);
}
//-------------------------------------------------------------------------------------
// se dessine
void __fastcall TPowerComponent::Paint(TCanvas * Canvas)
{
...
}
//-------------------------------------------------------------------------------------
// TGenerator
//-------------------------------------------------------------------------------------
__fastcall TGenerator::TGenerator(TCollection *Collection, PowSymbType AType )
:TPowerComponent( Collection, AType )
{
PG=0; Qgmin=0; Qgmax=0; Vsch=1.0; Con=-1;
}
//-------------------------------------------------------------------------------------
void __fastcall TGenerator::Assign(TPersistent *Source)
{
TGenerator *Src = dynamic_cast<TGenerator>(Source);
if( Src )
{
// copy members from Src...
}
else inherited::Assign(Source);
}
//-------------------------------------------------------------------------------------
// Usage
TPCCollection * NetWork = new TPCCollection(this);
// Usage to Access all the collection
for( int i=0; i< NetWork->Count; i++)
{
((TPowerComponent*)(NetWork->Items[i]))->Paint(Canvas);
}
To add a TGenerator and not a TPowerComponent, I use:
TGenerator * Gen=new TGenerator( NetWork, Generator);
The creation of the TCollectionItem child automatically add itself to the TCollection
The problem here is that we can't separate the process of item creation from adding it to the collection.
When I need another list that can have some of the items of the first collection list, for example, SelectedComponents can have one or some of the Items of the NetWork Collection, without recreating them.
This can be done with
std::list<TPowerComponent*> SelectedComponents;
but I can't write/read them using FileStream / persistent list. I need to put them in a TCollection but without recreating them.
How?
The RTL's native DFM streaming for TCollection objects only partially supports polymorphic TCollectionItem classes.
You can add polymorphic TCollectionItem objects to a TCollection in code (at runtime, as well as at design-time with the aid of a custom editor), as long as they all derive from a common base class that is passed to the TCollection constructor. And such a collection can even be saved as-is to a DFM.
However, when loading back a DFM, native streaming will force all collection items read from the DFM to use whatever TCollectionItem class type you pass to the TCollection constructor. So, polymorphic classes can't be loaded natively.
The only way to override that behavior is to disable native streaming for the collection (make your TCollection property be non-published, or at least mark it as stored=false), and then stream the collection items manually.
Have your main component (or whatever TPersistent class owns the collection) override the virtual DefineProperties() method to call TFiler.DefineProperty() to register custom reading/writing methods for streaming the collection items. To support polymorphic classes, you will have to write each item's ClassName to the DFM before writing its property values, then read the name back so you know which class to instantiate before then reading property values.
I need to create a style hook for TEdit in C++ Builder XE7, in order to override the style color management, as the following Delphi example. Could somebody post a complete example in C++ Builder (hook unit, registration and example form)? Thanks!
A translation of the Delphi example would look something like this:
class TEditStyleHookColor : public TEditStyleHook
{
typedef TEditStyleHook inherited;
private:
void UpdateColors();
protected:
virtual void __fastcall WndProc(TMessage &Message);
public:
__fastcall TEditStyleHookColor(TWinControl *AControl);
};
#include <Vcl.Styles.hpp>
class TWinControlH : public TWinControl {};
__fastcall TEditStyleHookColor::TEditStyleHookColor(TWinControl *AControl)
: TEditStyleHook(AControl)
{
//call the UpdateColors method to use the custom colors
UpdateColors();
};
//Here you set the colors of the style hook
void TEditStyleHookColor::UpdateColors()
{
if (Control->Enabled)
{
Brush->Color = (static_cast<TWinControlH*>(Control)->Color; //use the Control color
FontColor = static_cast<TWinControlH*>(Control)->Font->Color;//use the Control font color
}
else
{
//if the control is disabled use the colors of the style
TCustomStyleServices *LStyle = StyleServices();
Brush->Color = LStyle->GetStyleColor(scEditDisabled);
FontColor = LStyle->GetStyleFontColor(sfEditBoxTextDisabled);
}
}
//Handle the messages of the control
void __fastcall TEditStyleHookColor::WndProc(TMessage &Message)
{
switch (Message.Msg)
{
case CN_CTLCOLORMSGBOX:
case CN_CTLCOLORSCROLLBAR:
case CN_CTLCOLORSTATIC:
{
//Get the colors
UpdateColors();
SetTextColor(reinterpret_cast<HDC>(Message.WParam), ColorToRGB(FontColor));
SetBkColor(reinterpret_cast<HDC>(Message.WParam), ColorToRGB(Brush->Color));
Message.Result = reinterpret_cast<LRESULT>(Brush->Handle);
Handled = true;
break;
}
case CM_ENABLEDCHANGED:
{
//Get the colors
UpdateColors();
Handled = false;
break;
}
default:
inherited::WndProc(Message);
break;
}
}
...
TStyleManager::Engine->RegisterStyleHook(__classid(TEdit), __classid(TEditStyleHookColor));
TStyleManager::Engine->RegisterStyleHook(__classid(TMaskEdit), __classid(TEditStyleHookColor));
TStyleManager::Engine->RegisterStyleHook(__classid(TLabeledEdit), __classid(TEditStyleHookColor));
TStyleManager::Engine->RegisterStyleHook(__classid(TButtonedEdit), __classid(TEditStyleHookColor));
Version: C++Builder 10.1
Compiler: bcc32c
While using a library written in Delphi, an exception has occurred in TRttiType.GetMethods function.
I tried separately test the function, it causes the same exception.
Below is the code used in the test.
What's the problem in a class of C++Builder.
Unit1.cpp:
#include <memory>
#include "Unit2.hpp"
class PACKAGE TCpp : public TDelphi // new class derived from delphi class
{
__published:
void __fastcall Proc2(void);
};
void __fastcall TCpp::Proc2(void)
{
Proc();
}
inline int __fastcall GetMethodCount(TClass AClass)
{
const _STD unique_ptr<TRttiContext> LContext(_STD make_unique<TRttiContext>());
return LContext->GetType(AClass)->GetMethods().Length; // GetMethods raise exception
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
ShowMessage(GetMethodCount(__classid(TDelphi))); // safe
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
ShowMessage(GetMethodCount(__classid(TCpp))); // exception
}
Unit2.pas:
unit Unit2;
interface
uses
Winapi.Windows;
type
TDelphi = class
published
procedure Proc;
end;
implementation
{ TDelphi }
procedure TDelphi.Proc;
begin
Sleep(0);
end;
end.