Duplicate calls to SmartPart.OnActivated

Jul 18, 2010 at 6:48 PM
Edited Jul 18, 2010 at 7:19 PM

I was experiencing some strange behaviour where the OnActivated method of one of my Views (which is a SmartPart) was getting called twice when I showed the view in the workspace, I've managed to track this down and work around it but I don't fully understand why it happens or if the current framework logic is correct.

The application is going to be an enterprise application which runs on device boot and stays running permanently, consequently I took the decision to preload all views and sacrifice some startup speed in return for better performance in the app. There is a top level form which is the Shell, this contains a single DeckWorkspace occupying the entire screen, in the constructor of this form I load all of the views (SmartParts) into the RootWorkItem SmartParts collection using AddNew(). I then show these in the workspace as required.

Obviously the first time I show a SmartPart in the workspace it doesn't exist in the workspace SmartParts collection, consequently in Workspace.OnShow() this adds the SmartPart to the SmartParts collection, calls the OnActivated method of the SmartPart and then goes through the Workspace.Activate() method which in turn calls Workspace.OnActivate(). It is in Workspace.OnActivate() that the second call occurs to SmartPart.OnActivated(), this happened because the Visible property of the SmartPart was already set to true. This is the bit I'm not sure of the frameworks logic for, should showing a smartpart in a workspace for the first time when the smartpart is already visible result in the OnActivated() method of the smartpart being called twice?

I realise showing an already visible SmartPart seems a bit silly and this is where I'm confused as the SmartPart wasn't actually visible, it turns out that the instance created by RootWorkItem.SmartParts.Addnew() actually already has the Visible property set to true which I was not expecting, looking through the code I'm sure this isn't the fault of the framework but I'm also not sure why this happens either, is the initial value of the Visible property for a UserControl always true? Any insight on this would be helpful, for now I've worked around the issue by explicitly setting the SmartPart visible property to false in the constructor of the SmartPart but this feels like a bit of a hack.

EDIT: Not exactly related to this but I just spotted it in the code so thought I'd include it, from reading the code it would appear the Workspace SmartPartClosing event will get raised twice when calling Workspace.Close(Smartpart), the Workspace.Close() method directly calls Workspace.RaiseSmartPartClosing() and then goes on to call Workspace.OnClose() which also calls Workspace.RaiseSmartPartClosing().

Coordinator
Jul 20, 2010 at 6:11 PM

The behavior is definitely wrong - you should get OnActivated only once.  The interesting thing here is that if you're using a TabWorkspace, you get OnActivated only once, but the DeskWorkspace gives it twice.  It appears that there's a bug in the DeckWorkspace that I need to track down.

Coordinator
Jul 20, 2010 at 6:25 PM

Changeset 49037 fixes this issue.

Jul 20, 2010 at 7:15 PM
Edited Jul 20, 2010 at 7:15 PM
Thanks, I can confirm that I now only get a single call to OnActivated without having to explicitly toggle the initial Visible state of the SmartPart to false after creation. Have you had a chance to check the bit I edited in at the end on possible duplicate SmartPartClosing events when using Workspace.Close(), this isn't one I've actually got as far as testing to confirm yet but from reading the code it looks like it will definately happen.
Coordinator
Jul 20, 2010 at 11:13 PM

Yes, OnClosing was incorrectly getting called twice.  I've also fixed that.

Jul 21, 2010 at 10:47 AM
Edited Jul 21, 2010 at 11:01 AM
Perhaps i'm getting something wrong, but now OnActivated gets called only when showing the SmartPart, Hide it and then show it again, for example see WorkspaceDisposal Sample
Coordinator
Jul 21, 2010 at 2:33 PM

No, you were probably doing it right - I've updated the test applications and base code to further test these events.  I think it's right now.

Jul 21, 2010 at 4:18 PM

I'm not in a position to write some code to test this at the moment but want to post whilst it's in my mind, I'll try and test it properly later.

Looking at the latest changeset I'm still not sure the firing of OnActivated is correct, in Workspace.OnActivate() there are unconditional calls to SmartPart.OnActivated() and also Workspace.RaiseSmartPartActivated(), howver if the Smartpart Visible property is false then Workspace.OnActivate() will set this to true which will then raise the SmartPart.VisibleChanged event, this will be sunk in Workspace.smartPart_VisibleChanged() which will then call Workspace.RaiseSmartPartActivated() and SmartPart.OnActivated() again. So from looking at the code if the Visible property of the SmartPart is false and then is displayed using Workspace.Show() it looks like you'll end up with two calls to SmartPart.OnActivated() and two calls to Workspace.RaiseSmartPartActivated().

The code path for Workspace.OnHide() is similar but in this case there shouldn't be a duplicate call to SmartPart.OnDeactivated() as Workspace.OnDeactivate() only calls this if it doesn't need to change the Visible property of the SmartPart, so if Visible is changed the SmartPart.OnDeactivated() will be called by Workspace.smartPart_VisibleChanged() otherwise it will be called from Workspace.OnDeactivate(). It does however look like the Workspace.SmartPartDeactivated event will be raised twice as this event is raised by Workspace.RaiseSmartPartDeactivated() which is called from Workspace.smartPart_VisibleChanged() but is also unconditionally called from Workspace.OnDeactivate().

Coordinator
Jul 21, 2010 at 4:29 PM

It's certainly possible.  THe tests I've created for the UI stuff don't have full coverage for all of the possible permutations of starting with visible/non-visible SmartParts, and unfortunately writing UI tests for the CF is as close to impossible as you can get.  If I have time over the next few days to look into this I will, but for now I've got other work to do, so it's likely to be next week before I can get back and look at it.

Aug 1, 2010 at 5:58 PM

I've finally had a chance to checkout the latest revision and give this a go and I'm still seeing issues.

Using a DeckWorkspace the first time I show a newly constructed SmartPart I get a single call to SmartPart.OnActivated() as expected, but then if I show another SmartPart and then show the original SmartPart again the OnActivated method of the original SmartPart gets called twice.

This appears to be happening as follows, when constructing the SmartPart the Visible property of the underlying UserControl is set to true (this to me seems a bit illogical but appears to be the default for a new UserControl), the first time you show the SmartPart the OnActivated method gets called directly from Workspace.OnActivate(), when the second SmartPart is shown the DeckWorkspace.OnShow method sets the Visible property of the first SmartPart to false. When the first SmartPart is shown a subsequent time the Workspace.OnActivate() method sets the Visible property to true, this raises the SmartPart.VisibleChanged event which is sunk in Workspace.smartPart_VisibleChanged() which calls SmartPart.OnActivated(), but then back in Workspace.OnActivate() a direct call is made to SmartPart.OnActivated() after changing the Visible property, this results in two calls to OnActivated().

Coordinator
Aug 2, 2010 at 6:28 PM

My guess is that we're going to have to forcibly set Visible to false on creation and before we handle all of our IoC work to get this thing to behave the way we want.  If I have time, I'll try to look at it, but the next couple weeks are going to be busy for me.