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;
}
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.
I'm hoping someone can offer some guidance.
It is about making use of the Aurelia Observers in the best possible manner.
The example is a made-up example to illustrate the perceived problem.
Let's say I have three number properties: hours, mins, secs aka H, M, S and I wish to build an object that monitors H, M, S and each time any of these three changes, it produces a property T which is string that looks like a duration eg. "hh:mm:ss" using the H, M, S properties with appropriate zero padding etc.
Conversely, the property T is bound to in the user interface and therefore if the user modifies T (eg. via an input field), I need to decompose the pieces of T into their H, M, S components and push the individual values back into the H, M, S properties.
It should not be lost upon anyone that this behaviour is remarkably similar to the Aurelia value converter behaviour in terms of "toView" and "fromView" behaviour, except that we are dealing with 3 properties on 1 side, and 1 property on the other side.
Implementation-wise, I can use the BindingEngine or ObserverLocator to create observers on the H, M, S and T properties, and subscribe to those change notifications. Easy peasy for that bit.
My question is ultimately this:
When H changes, that will trigger a recompute of T, therefore I will write a new value to T.
Changing T will trigger a change notification for T, whose handler will then decompose the value of T and write new values back to H, M, S.
Writing back to H, M, S triggers another change notification to produce a new T, and so forth.
It seems self-evident that it is - at the very least - DESIRABLE that when my "converter" object writes to the H, M, S, T properties then it should prepare itself in some way to ignore the change notification that it expects will be forthcoming as a consequence.
However, [the ease of] saying it and doing it are two different things.
My question is -- is it really necessary at all? If it is desirable, how do I go about doing it in the easiest manner possible. The impediments to doing it are as follows:
There must be a real change to a value to produce a change notification, so you need to know in advance whether you are "expecting" to receive one.
The much more difficult issue is that the change notifications are issued via the Aurelia "synthetic" micro-task queue, so you are very much unaware when you will receive that micro-task call.
So, is there a good solution to this problem? Does anyone actually worry about this, or do they simply rely on Point 1 producing a self-limiting outcome? That is, there might be a couple of cycles of change notifications but the process will eventually acquiesce?
The simplest way to implement an N-way binding (in this case 4-way) is by using change handlers combined with "ignore" flags to prevent infinite recursion.
The concept below is quite similar to how some of these problems are solved internally in the Aurelia framework - it is robust, performant, and about as "natural" as it gets.
#autoinject()
export class MyViewModel {
#bindable({ changeHandler: "hmsChanged" })
public h: string;
#bindable({ changeHandler: "hmsChanged" })
public m: string;
#bindable({ changeHandler: "hmsChanged" })
public s: string;
#bindable()
public time: string;
private ignoreHMSChanged: boolean;
private ignoreTimeChanged: boolean;
constructor(private tq: TaskQueue) { }
public hmsChanged(): void {
if (this.ignoreHMSChanged) return;
this.ignoreTimeChanged = true;
this.time = `${this.h}:${this.m}:${this.s}`;
this.tq.queueMicroTask(() => this.ignoreTimeChanged = false);
}
public timeChanged(): void {
if (this.ignoreTimeChanged) return;
this.ignoreHMSChanged = true;
const hmsParts = this.time.split(":");
this.h = hmsParts[0];
this.m = hmsParts[1];
this.s = hmsParts[2];
this.tq.queueMicroTask(() => this.ignoreHMSChanged = false);
}
}
If you need a generic solution you can reuse across multiple ViewModels where you have different kinds of N-way bindings, you could do so with a BindingBehavior (and possibly even the combination of 2 ValueConverters).
But the logic that needs to be implemented in those would be conceptually similar to my example above.
In fact, if there were a premade solution to this problem in the framework (say, for example, a setting of the #bindable() decorator) then the internal logic of that solution would also be similar. You'll always need those flags as well as to delay their resetting via a micro task.
The only way to avoid using a micro task would be to change some internals of the framework such that a context can be passed along which can tell the dispatcher of change notifications "this change came from a change handler, so don't trigger that change handler again"
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;
}
I was able to follow the instruction on adding data, that part was easy and understandable. But when I tried to follow instructions for editing data, I'm completely lost.
I am following the todo sample, which works quite well, but when I tried to add to my own project using the same principle, nothing works.
in my controller, I have the following:
function listenForPropertyChanged() {
// Listen for property change of ANY entity so we can (optionally) save
var token = dataservice.addPropertyChangeHandler(propertyChanged);
// Arrange to remove the handler when the controller is destroyed
// which won't happen in this app but would in a multi-page app
$scope.$on("$destroy", function () {
dataservice.removePropertyChangeHandler(token);
});
function propertyChanged(changeArgs) {
// propertyChanged triggers save attempt UNLESS the property is the 'Id'
// because THEN the change is actually the post-save Id-fixup
// rather than user data entry so there is actually nothing to save.
if (changeArgs.args.propertyName !== 'Id') { save(); }
}
}
The problem is that any time I change a control on the view, the propertyChanged callback function never gets called.
Here's the code from the service:
function addPropertyChangeHandler(handler) {
// Actually adds any 'entityChanged' event handler
// call handler when an entity property of any entity changes
return manager.entityChanged.subscribe(function (changeArgs) {
var action = changeArgs.entityAction;
if (action === breeze.EntityAction.PropertyChange) {
handler(changeArgs);
}
});
}
If I put a break point on the line:
var action = changeArgs.entityAction;
In my project, it never reaches there; in the todo sample, it does! It completely skips the whole thing and just loads the view afterwards. So none of my callback functions work at all; so really, nothing is subscribed.
Because of this, when I try to save changes, the manager.hasChanges() is always false and nothing happens in the database.
I've been trying for at least 3 days getting this to work, and I'm completely dumbfounded by how complicated this whole issue has been for me.
Note: I'm using JohnPapa's HotTowel template. I tried to follow the Todo editing functionality to a Tee.. and nothing is working the way I'd like it to.
Help would be appreciated.
The whole time I thought the problem was in the javascript client side end of things. Turned out that editing doesn't work when you created projected DTOs.
So in my server side, I created a query:
public IQueryable<PersonDTO> getPerson(){
return (from _person in ContextProvider.Context.Queries
select new PersonDTO
{
Id = _person.Id,
FirstName = _person.FirstName,
LastName = _person.LastName
}).AsQueryable();
}
Which just projected a DTO to send off to the client. This did work with my app in fetching data and populating things. So this is NOT wrong. Using this, I was able to add items and fetch items, but there's no information that allowed the entitymanager to know about the item. When I created an item, the entitymanager has a "createEntity" which allowed me to tell the entitymanager which item to use.. in my case:
manager.createEntity(person, initializeValues);
Maybe if there was a "manager.getEntity" maybe that would help?
Anyways, I changed the above query to get it straight from the source:
public IQueryable<Person> getPeople(){
return ContextProvider.Context.People;
}
Note ContextProvider is:
readonly EFContextProvider<PeopleEntities> ContextProvider =
new EFContextProvider<PeopleEntities>();
So the subscribe method in the javascript checks out the info that's retrieved straight from the contextual object.. interesting. Just wish I didn't spend 4 days on this.
I am new to Android development with Xamarin.Android and I would like to understand how to have the next issue fixed.
Sometimes after restoring my Android application from background I was facing the next error:
Unable to find the default constructor on type MainMenuFragment. The MainMenuFragment is used by the application NavigationDrawerActivity to allow users to switch between different Fragments inside the app.
In order to solve it, I added a default constructor to the MainMenuFragment as described inside the next links:
Xamarin Limitations - 2.1. Missing constructors
Added a default constructor, should fix the issue.
public class MainMenuFragment : DialogFragment
{
readonly NavigationDrawerActivity navigationDrawer;
#region Constructors
public MainMenuFragment () {} // Default constructor...
public MainMenuFragment (NavigationDrawerActivity navigationDrawer, IMenuType launchMenu = null)
{
if (navigationDrawer == null)
throw new ArgumentNullException ("navigationDrawer");
this.navigationDrawer = navigationDrawer;
...
Fragment UpdateTopFragmentForCurrentMenu (Fragment newMenuRootFragment = null)
{
Fragment currentMenuRootFragment = navigationDrawer.CurrentFragment; // issued line.
But now sometime in the future, the MainMenuFragment gets initialized using its default constructor and at the first time it tries to access its navigationDrawer it throws a System.NullReferenceException:
System.NullReferenceException: Object reference not set to an instance of an object
at MainMenuFragment.UpdateTopFragmentForCurrentMenu (Android.App.Fragment) <0x00018>
at MainMenuFragment.OpenMenu (IMenuType,bool) <0x0006b>
at MainMenuFragment.OnCreate (Android.OS.Bundle) <0x00053>
at Android.App.Fragment.n_OnCreate_Landroid_os_Bundle_ (intptr,intptr,intptr) <0x0005b>
at (wrapper dynamic-method) object.3919a6ec-60c1-49fd-b101-86191363dc45 (intptr,intptr,intptr) <0x00043>
How can I have a default constructor implemented without facing this null reference exception?
You're programming like a C# developer, thats what the problem is :) I faced these same hurdles learning monodroid.
Take a look at the examples out there, in java, you'll see almost all the time they initialize using a static method like object.NewInstance() which returns object. This is how they initialize their views/receivers/fragments. At that point they populate the Arguments property and store that in the fragment. You need to remove all your constructors EXCEPT the empty ones and use arguments to pass your data around. If you try to do this using constructors and regular oo concepts you'll be in for a world of hurt. Arguments.putExtra and all those methods are there. It makes things a little verbose but once you get the hang of it you'll start creating some helper methods etc.
Once you get that sorted, you'll need to figure out if you need to recreate your fragments everytime the activity is resumed and if not, mark them as RetainInstance = true as well as get them onto a fragmentmanager which will help you retain all your state.
If you haven't built on android before it's weird and certainly not what I expected. But it's reeaaallly cool, much more awesome than I expected too. And same with Xamarin.
Great similar question: Best practice for instantiating a new Android Fragment