Step 5 - Custom Properties (C++)

From RAD Studio
Jump to: navigation, search

Go Up to Creating a FireMonkey Component (C++)


The component needs an additional property for the dialog's action verb, like "Save", to appear on the Do button. That verb is also combined with "Don't" on the Don't button. (More complex internationalization is beyond the scope of this topic.)

It would be simpler if you could rely on the presence of the button, and delegate to its Text to hold the verb. But the buttons are not there until the style is applied, and if the style is reapplied, the existing buttons are discarded. So the component needs to hold onto this string itself. Declare and implement a DoVerb property:

class PACKAGE TDialogButtonPanel : public TPanel
{
private:
	String FDoVerb;
	TAlignLayout FAlign;
	void __fastcall SetDoVerb(String AString);
	String __fastcall GetDoVerb();
protected:
	TControl* __fastcall TDialogButtonPanel::GetStyleObject();
	void __fastcall ApplyStyle();
public:
	__fastcall TDialogButtonPanel(TComponent* Owner);
__published:
	__property Align = { default = TAlignLayout::Bottom };
	__property String DoVerb ={ read=GetDoVerb, write=SetDoVerb };
};
//---------------------------------------------------------------------------
const String DoButtonName = "DoButton";
const String DontButtonName = "DontButton";
const String CancelButtonName = "CancelButton";
// ---------------------------------------------------------------------------
__fastcall TDialogButtonPanel::TDialogButtonPanel(TComponent* Owner)
	: TPanel(Owner) {
	Height = 46;
	Width = 300;
	Align = TAlignLayout::Bottom;
	DoVerb = "Verb";
}

// ---------------------------------------------------------------------------
void __fastcall TDialogButtonPanel::ApplyStyle() {
	TStyledControl::ApplyStyle();
	SetDoVerb(FDoVerb);
}

// ---------------------------------------------------------------------------
String __fastcall TDialogButtonPanel::GetDoVerb(){
	TFmxObject* Base;
	TTextControl* TextBase;
	Base = FindStyleResource(DoButtonName);
	if ((TextBase = dynamic_cast<TTextControl*>(Base)) != NULL) {
		FDoVerb = TextBase->Text;
	}
	return FDoVerb;
}

// ---------------------------------------------------------------------------
void __fastcall TDialogButtonPanel::SetDoVerb(String AString) {
	TFmxObject* Base;
	TTextControl* TextBase;
	Base = FindStyleResource(DoButtonName);
	FDoVerb = AString;
	if ((TextBase = dynamic_cast<TTextControl*>(Base)) != NULL) {
		TextBase->Text = AString;
	}
	Base = FindStyleResource(DontButtonName);
	if ((TextBase = dynamic_cast<TTextControl*>(Base)) != NULL) {
		TextBase->Text = "Don't " + AString;
	}
}

The StyleName for each of the three buttons is declared as a constant, along with the "negative OK" modal result code for the Don't button. The FDoVerb field is added to the object to contain the verb string.

At run time, the access sequence goes like this:

  1. When the application is executed, all its forms are created, including the dialog box form containing the button panel. The panel is instantiated by its object line when loading from the .fmx. The constructor writes the default verb to the property, calling SetDoVerb. The field is set, and no buttons are found.
  2. Assigning the property values from the .fmx, SetDoVerb is called again with the string saved by the designer (for example, "Save"), changing the field.
  3. When the dialog box is opened, it is painted, and one of the preliminary steps of painting a control is to apply its style if necessary, which includes the first time the control is painted. This ends up calling ApplyStyle, which is overridden here to reapply the verb string from the field. It redundantly reassigns the field, but now that the buttons are present, it also sets the verb in the Do button, and combines "Don't" and the verb in the Don't button.
  4. Rendering the buttons does not go through GetDoVerb, because the buttons that comprise the style are rendered directly, and they already have the appropriate text, assigned by the setter method. The getter method is used primarily by the Form Designer and any run-time code that gets the panel's properties. For completeness, the method will attempt to update the field from the Do button if it exists; but unless the button is manipulated directly (bypassing encapsulation), its text and the field should already be synchronized.

Previous

See Also