Collapse a region in PRISM 4

Topics: Prism v4 - WPF 4
Jun 25, 2013 at 4:29 PM
HI! Im trying to collapse a region if the region contains no views. I tried to make a custom region behaviour like this one below. But my problem is that i cant get the host control of my region to change the Visiblity property of it. How can i achive this ? or is there any better way to do this?
    [Export(typeof(CollapseRegionBehavior))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class CollapseRegionBehavior : RegionBehavior, IHostAwareRegionBehavior
    {

        public DependencyObject HostControl { get; set; }

        protected override void OnAttach()
        {
            this.Region.ActiveViews.CollectionChanged += ActiveViews_CollectionChanged;
        }

        void ActiveViews_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            var viewCount = this.Region.Views.Count();

            if (viewCount > 0)
            {
                //Show the region
            }
            else
            {
                //hide the region
            }
        }
    }
Developer
Jun 25, 2013 at 6:42 PM
Hi

If you wish to obtain the host control of the region inside your RegionBehavior you can make it implement the IHostAwareRegionBehavior interface, which defines a HostControl property. Said property should be automatically populated with the host control of the region when the behavior is attached to it.

However, using a RegionBehavior to implement this functionality might be an overkill, specially if you need this behavior in only one specific region of your application. Another possible approach is to implement the logic to show / hide the region in the code behind of the view where it's defined. This can be implemented in the same way: subscribing to the CollectionChanged event of the Views collection and then modifying the host control properties. In this case, you will have access to the host control and the logic to change the view will be contained in the view itself.

On the other hand, if you need to implement this functionality in other regions of your application or you don't want to add code in your views' code behind, using a RegionBehavior might be a more suitable approach.

I hope you find it useful,

Damian Cherubini
http://blogs.southworks.net/dcherubini
Jun 25, 2013 at 7:24 PM
Thanks for the reply DCherubini!
Yes i implemented the IHostAwareRegionBehavior and got the HostControl property as in my code post. But the property is always null.
It doesn't set the property automatically. I´m mapping my region with a ItemsControl maybe that is the reason that it doesn´t set the property ?.

Yes i´m trying to get this behavior to many regions i´m trying to make it collapse with animations etc.... Is there any other ways to get the region parent ? Or i´m i doing it wrong ?
Developer
Jun 25, 2013 at 8:53 PM
Hi,

That is strange. Usually the HostControl property is be set by the corresponding RegionAdapter when the region is created, unless the region has no host control.

It would be useful if you could post the code of your region behavior along with how you are adding the behavior to the region, so that we can find why the HostControl property is not being set.

Regards,

Damian Cherubini
http://blogs.southworks.net/dcherubini
Jun 25, 2013 at 9:37 PM
Edited Jun 25, 2013 at 9:46 PM
Thanks for the help here is the codes.

My behavior.

[Export(typeof(CollapseRegionBehavior))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class CollapseRegionBehavior : RegionBehavior, IHostAwareRegionBehavior
{

    public DependencyObject HostControl { get; set; }

    protected override void OnAttach()
    {
        this.Region.ActiveViews.CollectionChanged += ActiveViews_CollectionChanged;
    }

    void ActiveViews_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        var viewCount = this.Region.Views.Count();
        var control = this.HostControl as FrameworkElement;

        if (viewCount > 0)
        {
            //Show the region
            control.Visibility = Visibility.Visible;
        }
        else
        {
            //hide the region
            control.Visibility = Visibility.Collapsed;
        }
    }
}
Here is the module i load then i add the behavior.
[ModuleExport(typeof(MenuModule), InitializationMode = InitializationMode.WhenAvailable)]
[Module(ModuleName = "MenuModule")]
public class MenuModule : IModule
{
    private readonly IRegionManager _regionManager;

    [ImportingConstructor]
    public MenuModule(IRegionManager regionManager)
    {
        _regionManager = regionManager;
    }

    public void Initialize()
    {
        var region = _regionManager.Regions[RegionNames.SideMenuRegion];
        region.Behaviors.Add("CollapseRegionBehavior", new CollapseRegionBehavior());
    }
}
My bootstrapper

public class Bootstrapper : MefBootstrapper
{
protected override DependencyObject CreateShell()
{
    return this.Container.GetExportedValue<Shell>();
}

protected override void InitializeShell()
{
    base.InitializeShell();

    Application.Current.MainWindow = (Shell)this.Shell;
    Application.Current.MainWindow.Show();
}

protected override void ConfigureAggregateCatalog()
{
    base.ConfigureAggregateCatalog();

    //Add this main assembly to the catalog
    this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));

    //Add Base modules for the app that is referenses in the project.
    this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(MenuModule.MenuModule).Assembly));
    this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(AuthenticationModule.AuthenticationModule).Assembly));
    this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(AutoPopulateExportedViewsBehavior).Assembly));
}

protected override void ConfigureContainer()
{
    base.ConfigureContainer();
}

protected override IModuleCatalog CreateModuleCatalog()
{
    return base.CreateModuleCatalog();
}

protected override Microsoft.Practices.Prism.Regions.IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
{
    var factory = base.ConfigureDefaultRegionBehaviors();

    factory.AddIfMissing("AutoPopulateExportedViewsBehavior", typeof(AutoPopulateExportedViewsBehavior));
    return factory;
}
}
Developer
Jun 26, 2013 at 7:35 PM
Hi,

The problem seems to be that the behavior is added to the Region after the Region was created:
When a region is created by the corresponding RegionAdapter it will obtain the list of default behaviors and add them to the region. If one of those behaviors implement the IHostAwareRegionBehavior interface, the RegionAdapter will set its HostControl property by itself since the adapter is the only component that is aware of the control. Hence, any IHostAwareRegionBehavior that is added after the creation of the region will not have its HostControl property set.

A possible workaround for this could be to obtain the HostControl from a default behavior (like for example the SyncRegionContextWithHostBehavior) and set it manually to your CollapseRegionBehavior:
public void Initialize()
{
    var region = _regionManager.Regions[RegionNames.SideMenuRegion];
    var syncBehavior = region.Behaviors[SyncRegionContextWithHostBehavior.BehaviorKey] as IHostAwareRegionBehavior;
    var collapseBehavior = new CollapseRegionBehavior();
    collapseBehavior.HostControl = syncBehavior.HostControl;
    region.Behaviors.Add("CollapseRegionBehavior", collapseBehavior);
}
I hope this helps,

Damian Cherubini
http://blogs.southworks.net/dcherubini
Jun 26, 2013 at 9:44 PM
Hi thanks for the help. now it works like a charm. thanks so much.