Conductor.OnActivate() shoudl not be required to call base.OnActivate()
Yaevh opened this issue · comments
Description
I've just stumbled on the following scenario:
I have a conductor, MyConductor
. In its constructor, I immediately assign an ActiveItem
. I expect that when MyConductor
is activated, its ActiveItem
will also be activated - and most of the times it works exactly that way.
However, if I override MyConductor.OnActivate()
, the children are never activated. That's because children activation happens in Conductor.OnActivated()
, and that method is overridden by MyConductor
. So I have remember to call base.OnActivate()
manually, or my ActiveItem
won't get activated properly - which is an anti-pattern, see https://en.wikipedia.org/wiki/Call_super.
The same is true for OnDeactivate()
and OnClose()
: overriding these methods on MyConductor
without remembering to manually call the base implementations results in ActiveItem
failing to deactivate/close properly.
To Reproduce
var myConductor = new MyConductor();
ScreenExtensions.TryActivate(myConductor);
public class MyConductor : Conductor<IScreen>
{
public MyConductor()
{
ActivateItem(new MyChild());
}
protected override void OnActivate()
{
//base.OnActivate(); <- I have to remember to call base here, or ActiveItem won't actually get activated
}
}
public class MyChild : Screen
{
protected override void OnActivate()
{
// never gets called!
}
}
Version Info
- Stylet version: 1.3.6
- Runtime version: 7.0.5
Additional Info
The solution could be either:
- Instead of overriding
OnActivate()
/OnDeactivate()
/OnClose()
methods,Conductor
should rather listen for its ownActivated/Deactivated/Closed
events and change state ofActiveItem
from there Conductor
should provide its own implementation ofActivate()
,Deactivate()
andClose()
and callScreen.TryActivate(ActiveItem)
etc. from there
I'm willing to implement either one of the solutions, but I want to know your opinion first :)
Eh, needing to call the overridden method is common enough in .NET: you should always do it as a matter of habit. It also gives you control as to if and when to invoke the base method's behaviour, which people might be relying on.
At this point, any change here would be a breaking change though, I'm afraid.
I don't think the first solution would actually be a breaking change. Public API remains the same, and even when the users have indeed overridden Conductor.OnActivate()
and called base.OnActivate()
, their code would still compile and work as intended, but the base
call would simply be a no-op, as there will be nothing to do in base.OnActivate()
.
The only publicly visible change is that from now on, ActiveItem.Activate()
would always be activated after its parent Conductor.OnActivate()
has returned; while until now, the users could potentially select the order of activation by deciding whether to call base.OnActivate()
either before or after they did their work in Conductor.OnActivate()
.
I've created PR #379 with the first solution, but I'm not trying to force the issue. Please feel free to close the PR if you think it's too risky.
The breaking change is that people who don't call the base method or who rely on the sequencing of calling the base method, have behaviour they potentially rely on changed.
It's common in wpf that you call the base method in your override - look at any OnXCC method on Window or UserControl. I don't think Stylet is taking an unusual approach here.
Also, I think there's a thread dispatch when the events are called?