DelayedRegionCreationBehavior creates regions too early
description
According to the Prism documentation, DelayedRegionCreationBehavior delays creation of regions until the control that will host the IRegion is added to the VisualTree.
However, in practice the region is often created long before the control is added to the tree. This means the region adapter is invoked before the control is part of a window, even, possibly if the control will never be added to the visual tree.
The "early" delayed region creation occurs when RegionManager.UpdateRegions() is called, because the behavior monitors the RegionManager.UpdatingRegions event, and uses this as one of its triggers for View creation.
Typically, code like this will cause too early creation:
var view = new ViewWithRegionsInIt();
regionManager.Regions["ParentRegion"].Add( view );
Whereas this code will create the region when the view is actually added to the tree:
var rgn = regionManager.Regions["ParentRegion"];
var view = new ViewWithRegionsInIt();
rgn.Add( view );
The difference in behavior occurs because indexing the Regions collection in the IRegionManager causes RegionManager.UpdateRegions() to be called, and thus create any regions in views that haven't yet been created, whether they are in the visual tree or not.
It's not documented why the DelayedRegionCreationBehavior behaves like this, but I'm guessing it's a hack to allow Regions to be created for non FrameworkElement objects.
Unfortunately, there's no way to extend it to modify this behavior (only CreateRegion is virtual), so the only way I could find to fix the Prism behavior was to patch the Prism code. My fix for this issue as as follows:
In DelayedRegionCreationBehavior.cs:
public void OnUpdatingRegions(object sender, EventArgs e)
{
if( TargetElement != null &&
TargetElement is FrameworkElement &&
((FrameworkElement)TargetElement).Parent != null &&
Window.GetWindow( TargetElement ) == null )
// Looks like we can trust that the Loaded event will work for this element, so wait for that
return;
this.TryCreateRegion();
}