I've checked out many threads of a similar title but they haven't helped.
The following compiles and installs to the component palette, but when I try to add the component to a panel, I get the error message mentioned in the thread title.
Could anyone please explain why?
__fastcall TEditBox::TEditBox(TComponent* Owner) : TGroupBox(Owner)
{
ToolBar=new TToolBar(this);
ToolBar->Parent=this;
TToolButton *Btn=new TToolButton(ToolBar);
Btn->Parent=ToolBar;
}
If I omit the Btn->Parent=ToolBar line, everything's OK, so presumably that's the problem line?
Assigning a ToolButton's Parent requires the ToolBar to have an allocated HWND, which requires it to have a Parent with an allocated HWND, and so on. But your EditBox does not have a Parent (or a Name) assigned yet when its constructor is called, so the ToolBar cannot allocate an HWND yet, hence the error.
If you want your Toolbar to have a default button at runtime, you need to move the creation of the button to the EditBox's virtual Loaded() method (or even the SetParent() method), eg:
__fastcall TEditBox::TEditBox(TComponent* Owner)
: TGroupBox(Owner)
{
ToolBar=new TToolBar(this);
ToolBar->Parent=this;
}
void __fastcall TEditBox::Loaded()
{
TGroupBox::Loaded();
TToolButton *Btn=new TToolButton(ToolBar);
Btn->Parent=ToolBar;
}
Related
In the OnCreate event, I have code which adds menu items to a std::vector (for the purposes of this question, it could be buttons, or any other components we create dynamically with the new operator):
// Iterrate through styles and hints and construct menu
for (int i=0; i<styleNamesAndHints->Count; i++) {
TMenuItem* miTheme = new TMenuItem(miRoot);
miTheme->Caption = styleNamesAndHints->Strings[i].SubString(0, styleNamesAndHints->Strings[i].Pos(styleNamesAndHints_Delimeter)-1);
miTheme->AutoCheck = false;
miTheme->AutoHotkeys = maManual;
miTheme->RadioItem = false;
miTheme->GroupIndex = MENU_GROUP_INDEX_STYLES;
miTheme->OnClick = &miStyles_Click;
miTheme->OnDrawItem = &miStyles_DrawItem;
miTheme->OnMeasureItem = &miStyles_MeasureItem;
miTheme->ImageIndex = IL_MENU_A_THEME;
miTheme->Tag = miStyleArray.size();
miStyleArray.push_back(miTheme);
miRoot->Add(miTheme);
}
Then in the OnDestroy event, we clean up the vector miStyleArray:
// Clean up miStyleArray vector, delete each element
while(miStyleArray.empty()==false) {
delete miStyleArray.back();
miStyleArray.pop_back();
}
This code works... I have no errors, everything works perfectly....
Now, someone told me that code like this could lead to errors and that, I quote, I should not use operator delete on objects that have parent, parent of the object is responsible for destroying and clean up..
As a matter of fact, the person that told me this is a moderator at a C++Builder forum, and warned me that I could be banned next time for such code! Here is the post link.
Who is right, who is wrong??
It is perfectly safe to manually delete a component instance that has an Owner or Parent assigned. The component's destructor will remove the instance from its Owner/Parent's internal list so the instance is not freed a 2nd time.
On the other hand, a component instance with an Owner assigned does not need to be delete'd manually, it will be freed when its Owner is freed, so your OnDestroy code is redundant and can be removed.
On a side note: you should NOT use the Form's OnCreate and OnDestroy events in C++Builder. They are a Delphi idiom that can lead to undefined behavior in C++ if you are not careful. OnCreate can fire before your Form's constructor, and OnDestroy can fire after your Form's destructor. As such, you should use the Form's actual constructor and destructor instead.
This is related to Assigning events to a VCL control created dynamically at runtime.
I used the above listed/reference post to solve a problem that I had with some code. Thank you for providing that example. I found it very useful, and implemented the "alternative" way that it provided as I was unable to make the first way work.
I am using C++Builder 10.x from Embarcadero. I just updated to C++Builder 10.3. This new update is now throwing a warning:
[bcc32c Warning] LogitToMemo.cpp(196): implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension
The line it is throwing on is:
Method.Code = &LogLevelComboBoxOnChange;
I am not sure how to "fix" this.
The code is in support of a logging function to a memo field, where the page for the logging memo has a TComboBox to select the logging level/verbosity.
The TComboBox is external to the logging function, as it is on the user's form. I want the TComboBox::OnChange event to call my LogLevelComboBoxOnChange function, which adjusts the logging level based on the TComboBox item/entry selected.
Supporting code around this includes.
Function declaration - TComboBox::OnChange Event Function
void __fastcall LogLevelComboBoxOnChange(void *pThis, TObject *Sender);
Function Declaration - Logging start up where the TMemo field to log to & the TComboBox are provided
int LogStartWithComboBox(TMemo *LogIt_MemoField, TComboBox *AppLogLevelComboBox, int iThreshold, AnsiString &asFieldTitles);
This is the function that assigns the OnChange function to the TComboBox object on the user's logging form.
int LogStartWithComboBox(TMemo *LogIt_MemoField, TComboBox *AppLogLevelComboBox, int iThreshold, AnsiString &asFieldTitles)
{
static TMethod Method;
//
// Set-Up CombBox and OnChange Event
// - Save ComboBox pointer
// - Assign List of Log Levels
// - Assign/Set-Up OnChange Function
//
LogLevelComboBox = AppLogLevelComboBox;
AppLogLevelComboBox->Items->Text =
"Off\n"
"All Messages\n"
"Verbose\n"
"Trace\n"
"Informational\n"
"Warning\n"
"Error\n"
"Severe\n"
"Fatal";
AppLogLevelComboBox->ItemIndex = iThreshold + 1;
//
// Set-Up - On Change Function for "external" Log Level Combo-Box
//
Method.Data = NULL; // passed to the pThis parameter, can be whatever you want
//
// The Following line generates the warning
// [bcc32c Warning] LogitToMemo.cpp(196): implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension
//
Method.Code = &LogLevelComboBoxOnChange;
LogLevelComboBox->OnChange = reinterpret_cast<TNotifyEvent&>(Method);
return 0;
}
You are using C++Builder's CLang-based C++ compiler. The "implicit conversion" warning you are seeing is a CLang issue that has cropped up during the past few years in multiple toolchains that use CLang, not just in C++Builder. This issue does not affect C++Builder's "classic" (non-CLang) compiler.
&LogLevelComboBoxOnChange creates a pointer-to-function, which CLang does not like to implicitly convert to void* (which is what the TMethod::Code field is declared as) unless Clang is put into a Microsoft compatibility mode.
Possible solutions are to either:
type-cast the pointer explicitly:
(UPDATE: apparently CLang doesn't like static_casting a pointer-to-function to void*, either!)
Method.Code = static_cast<void*>(&LogLevelComboBoxOnChange);
Method.Code = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(&LogLevelComboBoxOnChange));
enable the -Wmicrosoft-cast flag in the CLang compiler settings.
disable the warning in your code by using #pragma statements around the assignment to Method.Code:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmicrosoft-cast"
Method.Code = &LogLevelComboBoxOnChange;
#pragma clang diagnostic pop
use a union (though, I'm sure some people will argue that this probably violates Strict Aliasing rules, but if it works...):
union {
void (__fastcall *func)(void*, TObject*);
void *ptr;
} u;
u.func = &LogLevelComboBoxOnChange;
Method.Code = u.ptr;
I know how to create frame in the design time and place it in the panel in the runtime in Delphi. As for C++ Builder, It looked difficult as I am not familiar with C++ scripts. Please advise how to do the right way?
Thanks in advance
The solution is the exact same as in Delphi, you just need to use C++ syntax instead.
Something like this should work:
/*
Assuming your frame is located in a unit called Frame1, and it's
called TMyFrameType, this is what you should add your Form unit
cpp file.
*/
#include "Frame1.h"
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
// This assumes you have a panel in this form called "ThePanelWhereIWantIt".
// You could move the MyFrameInstance to the class definition, if you need to
// access it somewhere after in your form code, but this is trivial.
TMyFrameType *MyFrameInstance;
MyFrameInstance = new TMyFrameType(ThePanelWhereIWantIt);
MyFrameInstance->Parent = ThePanelWhereIWantIt;
MyFrameInstance->Align = alClient;
}
//---------------------------------------------------------------------------
I have the following snippet in my UI Builder code:
table.addShortcutListener(new ShortcutListener("Select all", null, KeyCode.A, ModifierKey.CTRL) {
#Override
public void handleAction(Object sender, Object target) {
AbstractSelect t = (AbstractSelect) target;
if (t.isMultiSelect()) {
t.setValue(t.getItemIds());
}
}
});
return table;
This allows to press Ctrl+A to select all items in a table. This usually works the first time I load a view until I make one of the tables invisible (setVisible(false)). After making the tables visible again, it no longer works (not even on reloading the page) and I get the following console output whenever I press Ctrl+A:
WARNING: Ignoring action for disabled connector c.b.a.web.ui.builder.table.TranslatedHeaderTable
Nov 03, 2014 11:15:00 AM com.vaadin.event.ConnectorActionManager handleAction
What is wrong with my code? How would I achieve my goal?
I'd suggest this modification, works fine for me (i suppose comp is the Vaadin Table)
comp.addShortcutListener(new ShortcutListener("Select all", null, KeyCode.A, ModifierKey.CTRL) {
private static final long serialVersionUID = 1L;
#Override
public void handleAction(Object sender, Object target) {
if (comp.isMultiSelect()) {
comp.setValue(comp.getItemIds());
}
}
});
The problem might be, while testing locally, the failure of serialization of some component, so a static reference to comp (you'll need to make it final) and a defaut UID should do the trick.
tested multiple times and the error never occured.
Cheers.
EDIT
I understood that the problem occured when making invisible and then visible the table.
It came to my mind just now that you could have tried CTRL+A on an invisible Table: if this is the case so it's correct, when a Component is made invisible every listener is put in "Standby" until you make it visible again. So for me:
setVisible(false);
setVisible(true);
"CTRL+A"
works, while
setVisible(false);
"CTRL+A"
gives me
nov 03, 2014 2:19:34 PM com.vaadin.event.ConnectorActionManager handleAction
WARNING: Ignoring action for disabled connector com.vaadin.ui.Table
It's meant to be this way, nothing wrong in your code, you have to change your functionality and do not have a "CTRL+A" on an invisible Table (which seems a bad thing to do imho). On the other hand you could overwrite the setVisible method but I discourage it.
Cheers.
It seems there are some problems with the Table implementation of the Action.Notifier interface. In this Vaadin Forum Post, the Vaadin Devs suggest to add the ShortcutListener not to the Table itself but to a Panel that the Table is enclosed within.
My new implementation:
private void addCtrlAHandler(final AbstractSelect table) {
Panel panelEnclosingTable = table.findAncestor(Panel.class);
Preconditions.checkArgument(panelEnclosingTable != null, "Table is not enclosed in a panel; cannot add shortcut handlers");
panelEnclosingTable.addShortcutListener(new ShortcutListener("Select all", null, KeyCode.A, ModifierKey.CTRL) {
#Override
public void handleAction(Object sender, Object target) {
if (table.isMultiSelect()) {
table.setValue(table.getItemIds());
}
}
});
}
With this workaround, I get the expected behavior.
Currently, I am trying to understand Delphi's VCL, specifically, the notification mechanism (which is a great mechanism in my point of view).
When I was studying on this subject, I remembered of the TLabeledEdit, of course I have been using it for long time, but I never had chance to stop and study it's code.
As I understand so far:
When a TComponent is destroyed:
It will notify all its children's components, to include csDestroying in its state.
FreeNotifiers part. I can't understand.
Will iterate the components list and:
Remove each item from the components list
Actually destroy each component instance.
When a child component is being destroyed, it restarts the same process for all of its children components. So, as far as I can tell, this is a chain effect.
What I can't understand is the FreeNotification, what could I possibly do with it?
Let's think about the TLabeledEdit in first place. The relevant part of the notification, in TLabeledEdit's code is an override on the Notification function, with the following code:
if (AComponent = FEditLabel) and (Operation = opRemove) then
FEditLabel := nil;
What could have happened if FreeNotification was not used?
In General, what benefits would I have because of this mechanism and what am I not seeing that might eventually make its existence necessary?
What the FreeNotification mechanism does is notifies registered components that this component is being freed. They then use their Notification method to ensure that they don't hold any references to it (which is what your example is doing) so that they don't end up with dangling references to an invalid object.
The point of FreeNotification is to allow you to clean up references to form children in cases where these components close and free themselves. In this example I use a Modal form, the FreeNotification sets the reference to NULL which allows me to detect if the form is still open or already exists if the user creates another one
void __fastcall TForm1::OpenForm(TObject *Sender)
{
frmGetPrint = new TfrmGetPrint(Application);
frmGetPrint->FreeNotification( this);
frmGetPrint->Show();
}
frmGetPrint must free itself when closed:
void __fastcall TfrmSetPrint::FormClose(TObject *Sender, TCloseAction &Action)
{
Action = caFree;
}
You need to create a function in the parent form referred to by this in the call to FreeNotification( this), in this case Form1
void __fastcall TForm1::Notification(Classes::TComponent* AComponent,
Classes::TOperation Operation)
{
if( AComponent == frmGetPrint && Operation == opRemove)
frmGetPrint = NULL;
}