I have a custom control which needs to do some things when loading at run time, based on a published property. However, I am running into a problem where, whenever I check the published property, it has not been set yet, and is always the default value.
I first attempted checking the property in the constructor, of the control, but quickly found they were not loaded yet. I know that when the control is shown on the screen the properties are set correctly, thus its not an issue with the properties not being loaded at all.
I next attempted overriding the Loaded Method but am still having the same problem, so I don't think this is exactly what I am looking for.
void __fastcall TFmSearchBar::Loaded()
{
TEdit::Loaded(); //call base class loaded
if( MyProperty )
{
//do stuff
}
}
At what point are these published properties actually getting set?
What method can/should I hook into in order to execute some logic in my control based on these properties, as soon as the properties are set correctly?
If I check the property in the constructor of the control, the property is always the default value even if I have specified otherwise in the designer.
Correct, because its design-time values have not been assigned yet.
At what point are these published properties actually getting set?
When the Owner (Form, Frame, or DataModule) is being constructed. It loads its own DFM resource and parses it, constructing stored child components and reading their property values.
For example, say you have the following DFM:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
...
object Edit1: TEdit
Left = 136
Top = 64
Width = 121
Height = 21
TabOrder = 0
end
object Button1: TButton
Left = 263
Top = 62
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 1
end
end
The DFM streaming process roughly translates to the following equivalent code (I'm leaving a lot of internal details out for simplicity):
__fastcall TCustomForm::TCustomForm(TComponent *Owner)
: TScrollingWinControl(Owner)
{
this->FFormState << fsCreating;
try
{
// locate, load, and parse the "Form1" DFM resource ...
this->FComponentState << csLoading;
this->Parent = ...;
this->Name = L"Form1":
this->FComponentState << csReading;
this->Left = 0;
this->Top = 0;
this->Caption = L"Form1";
...
TEdit *e = new TEdit(this);
try
{
e->FComponentState << csLoading;
e->Parent = this;
e->Name = L"Edit1"; // <-- sets the derived Form's 'Edit1' member to this object
e->FComponentState << csReading;
e->Left = 136;
e->Top = 64;
e->Width = 121;
e->Height = 21;
e->TabOrder = 0;
e->FComponentState >> csReading;
}
catch (...)
{
delete e;
throw;
}
TButton *b = new TButton(this);
try
{
b->FComponentState << csLoading;
b->Parent = this;
b->Name = L"Button1"; // <-- sets the derived Form's 'Button1' member to this object
b->FComponentState << csReading;
b->Left = 263;
b->Top = 62;
b->Width = 75;
b->Height = 25;
b->Caption = L"Button1";
b->TabOrder = 1;
b->FComponentState >> csReading;
}
catch (...)
{
delete b;
throw;
}
this->FComponentState >> csReading;
...
e->Loaded();
b->Loaded();
this->Loaded();
}
__finally
{
this->FFormState >> fsCreating;
}
}
So, as you can see, a component's property values are not available yet when its constructor is called.
What method can/should I hook into in order to execute some logic in my control based on these properties, as soon as the properties are set correctly?
That depends on what the properties need to do. If they need to perform operations immediately, you can do that directly in their property setters. But if they need to wait until other properties have been loaded first (if one property is dependent on the value of another property), then override the virtual Loaded() method instead, which is automatically called after DFM streaming is finished. Property setters can check the flags of the ComponentState property to know whether or not the component is currently running in the Form Designer at design-time, whether or not a DFM is currently being streamed, etc and then act accordingly as needed.
I attempted overriding the Loaded Method but am still having the same problem
Which is what exactly? You did not explain what your actual problem is. Please edit your question to provide those details.
so I don't think this is exactly what I am looking for.
It most likely is, you probably are just not using it correctly.
Related
I've been developing an automated system that requires me to generate the LayerInfo objects of a given Landscape. So far I've been able to generate the required LayerInfo object's, but I wasn't able to assign them, or to be more precise, when I try to assign the necessary layers in the ULandscapeInfo property they appear in the editor but are not assigned, unless I save them and restart the engine, they also appear weird in the editor, as you can see in the image. I think it's a similar problem to the one in this thread. The code bellow it's responsible for assignig the LayerInfo:
// This method creates a
// new layer info object
UWorld * currentWorld = GetWorld();
ULevel * level = currentWorld->GetLevel(0); // TODO : just for debuging
ULandscapeLayerInfoObject* layerInfo = Landscape->CreateLayerInfo(*(layerName), level);
bool isDirty = layerInfo->MarkPackageDirty();
// Get a reference for the landscape Info
ULandscapeInfo* landscapeInfo = Landscape->GetLandscapeInfo();
landscapeInfo->CreateLayerEditorSettingsFor(layerInfo);
FLandscapeInfoLayerSettings* layerSettings = &landscapeInfo->Layers[1];
landscapeInfo->Layers[0] = FLandscapeInfoLayerSettings(layerInfo, Landscape);
UEditorAssetLibrary::SaveAsset(assetName, false);
// Assign the respective values
if (!layerSettings->LayerInfoObj)
{
layerSettings->Owner = Landscape;
layerSettings->LayerInfoObj = layerInfo;
layerSettings->bValid = true;
}
Editor Screenshot
I tried this code to get the first visible row in a scrolling Table inside a BorderLayout.CENTER, but it didn't work, seems the points returned do not reflect the visible cells, unless I am missing a sort of calculation,
thank you for your insights,
#Override
protected void onScrollY(int scrollY) {
super.onScrollY(scrollY); //To change body of generated methods, choose Tools | Templates.
Component c=getComponentAt(50, scrollY);
if (c instanceof Table){
System.err.println("table "+getWidth()+" "+getHeight()+" s "+scrollY);
return;
}
Button b=(Button) c;
System.err.println("c: "+b.getText());
}
getComponentAt(x,y) takes absolute (screen) coordinates. The scrollY value is a relative coordinate in that container.
So what you want is something like:
Component c = getComponentAt(getAbsoluteX()+50, getAbsoluteY() + scrollY)
Also worth nothing that getComponentAt(x,y) will only return components that are focusable or have been set to grab pointer events. If you just want to find the first paintable immediate child of this container, and you're using a BoxLayout.Y_AXIS layout, then you might be better to just iterate through the children until you find one where y is at least the scrollY.
e.g.
Component c = null;
for (Component child : this) {
if (child.getY() + child.getHeight() > scrollY) {
c = child;
break;
}
}
....
I would like to dynamically create a series of Frame components, then save the pointers into a vector.
I am able to dynamically create TEdit components with no problem, but if I replace TEdit with the name of the frame component (that contains an edit box) the program will error. The first frame will be created, but errors when creating the second one, stating "External exception EEFFACE"
Here is the relevant code. Note that if I replace TFrame2 with TEdit, it works.
class TForm1 : public TForm
{
...
public: // User declarations
std::vector<TFrame2*> fields;
...
};
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TFrame2* temp = new TFrame2 (Layout1);
temp->Parent = Layout1;
temp->Align = TAlignLayout::Top;
fields.push_back(temp);
count++;
}
This is what it looks like after one click.
Error message after 2 clicks.
This is how I want it to look after two clicks.
This is what it looks like when I replace TFrame2 with TEdit, after 3 clicks.
-
EDIT
If I try to hard-code the creation of two Frames, I get the same error on the first click.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TFrame2* temp = new TFrame2 (Layout1);
temp->Parent = Layout1;
temp->Align = TAlignLayout::Top;
fields.push_back(temp);
count++;
TFrame2* temp1 = new TFrame2 (Layout1);
temp1->Parent = Layout1;
temp1->Align = TAlignLayout::Top;
fields.push_back(temp1);
count++;
}
-
EDIT 2
In this post
Can FireMonkey frames be created dynamically?
I see a comment that states
I should note here that it seems the frame objects need to be assigned a unique Name property manually right after creation, at least when using C++, or the next frame object created of the same type will try to take the same name as the first one.
To fix this issue, I needed to set the name of the Frame at runtime. Adding the following code in the Button1 click method fixes the issue.
temp->Name = std::strcat("TFrame2", std::to_string(count).c_str());
This names every new Frame "TFrame2#" where # is the number frame that has been created.
VS2010 with an MDI document layout using tabs along the top to switch between documents. Each document is a "live" view into a database, where the persistent data per document is a group of configuration settings.
We would like to allow the user to rearrange the tabs (this functionality is built in), but need to persist this new order. Right now it appears the document z-order is not affected by moving the tabs around. when closing the app, the documents close in the order they were opened so this is not helpful in determining the final tab order on close.
We are using the EnableMDITabbedGroups(TRUE, mdiTabParams) with m_bEnableTabSwap = TRUE which is the default.
Thanks! Ended up with the following solution in the MainFrame::OnClose() method.
Note that this code example uses two custom classes of 1) CSpectraAnalysisUtilityView which inherits from CView and 2) CReviewDataFolder which is our object that we needed to update the recent Tab Order.
This code solution also implements the GetMDITabGroups in case there are multiple group windows open.
void CMainFrame::OnClose()
{
iReviewDataFolderOrder = 1;
const CObList& tabGroups =m_wndClientArea.GetMDITabGroups();
if (0 < tabGroups.GetCount())
{
POSITION pos = tabGroups.GetHeadPosition();
CMFCTabCtrl* pCrtTabCtrl;
while(pos != NULL)
{
pCrtTabCtrl=DYNAMIC_DOWNCAST(CMFCTabCtrl, tabGroups.GetNext(pos));
int count = pCrtTabCtrl->GetTabsNum();
for(int i = 0; i < count; i++)
{
CWnd* pWnd = pCrtTabCtrl->GetTabWndNoWrapper(i);
CMDIChildWnd *pChild = ((CMDIChildWnd*)(pWnd));
if (pChild)
{
CView *pView = pChild->GetActiveView();
if (pView)
{
if (pView->IsKindOf(RUNTIME_CLASS(CSpectraAnalysisUtilityView)))
{
CSpectraAnalysisUtilityView* specUtilView;
specUtilView = (CSpectraAnalysisUtilityView*)pView;
CReviewDataFolder* pDataFolder = specUtilView->GetSpecReviewDataFolder();
if(pDataFolder)
{
pDataFolder->SetRecentOrder(iReviewDataFolderOrder);
iReviewDataFolderOrder++;
}
}
}
}
}
}
}
CMDIFrameWnd::OnClose();
}
Upon destruction of the outer main frame (OnDestroy) you can access the the CMFCTabCtrl members and can loop over each tab and determine the current sequence stored in the tab. GetTabWnd will allow you to access each tab by its index.
To access the tab control use CMDIClientAreaWnd::GetMDITab.
I have searched far and wide and thought different options for quite a while, and am now absolutely stumped. I created a simple class that makes 16 buttons and assigns IDs to them in the constructor. I would like each of the buttons to have an event triggered when clicked.
Class in the header:
class step16
{
///signals and buttons
private:
wxButton* sequencer [16];
long* ids = new long[16];
public:
step16(wxFrame* frame);
~step16();
};
Declaration of the functions in the source file:
///constructor for 16 step sample sequencer class
step16::step16(wxFrame* frame)
{
///clear all signals on initialization and create buttons
for(int i = 0; i < 16; i++){
ids [i] = wxNewId();
sequencer[i] = new wxButton(frame,ids[i],wxString::Format(_("")),
wxPoint(i*30 , 0,wxSize(30,20) );
}
}
///destructor for the 16 step sequencer class
step16::~step16(){delete[]signals;}
The only way I know how to add click events to buttons in wxWidgets is using the Connect() method in the initialization part of the Main wxFrame, but connecting them in that part of the program would not bring the desired results. Mainly because I need a new set of 16 buttons with unique IDs and events in every instance of the step16 class. How would I go about adding unique click events to each of these buttons?
You can use Bind to bind a handler in any class that is derived from wxEventHandler (i.e. just about any standard wxWidgets class, including wxFrame).
Pass the ID of the button to the Bind() call so your event handler knows which button has been pressed.
For example your step16 constructor could look like this:
///constructor for 16 step sample sequencer class
step16::step16(wxFrame* frame)
{
///clear all signals on initialization and create buttons
for(int i = 0; i < 16; i++)
{
ids [i] = wxNewId();
sequencer[i] = new wxButton(frame,ids[i],wxString::Format(_("")),
wxPoint(i*30,0), wxSize(30,20));
/// Add it to something so I can test this works!
frame->GetSizer()->Add(sequencer[i]);
/// Bind the clicked event for this button to a handler
/// in the Main Frame.
sequencer[i]->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
&MainFrame::OnPress,
(MainFrame*)frame);
}
}
In this example, I have created the event handler in the MainFrame class, a pointer to an instance of which is passed to ctor for step16.
You can distinguish between the button presses using event.GetId() which will be the value set by the line:
ids [i] = wxNewId();
The MainFrame::OnPress method could look like this:
void MainFrame::OnPress(wxCommandEvent& event)
{
long firstID = *theStep16->GetIDs();
switch(event.GetId() - firstID)
{
case 0:
std::cout << "First button" << std::endl;
break;
case 1:
std::cout << "Second button" << std::endl;
break;
default:
std::cout << "One of the other buttons with ID "
<< event.GetId() << std::endl;
}
}