Sort ItemsControl Prism v2.2 Region without a bound collection?

Topics: Prism v2 - WPF 4
Nov 4, 2010 at 6:02 PM

Is there a way to sort the views that get added to a ItemsControl region? The views being added are registered with the container and added to the region in each unique module.

Some pseudo code...

Shell:

<Window>
<ItemsControl Prism:RegionManager.Region="ItemsRegion"/>
</Window>

Modules: This is the initialization code in the modules.

    protected override void RegisterViewsAndServices()
   
{
       
CommonContainerLifetimeManager.Register<IView, ItemView1>();
       
Container.RegisterType<IViewModel, ItemViewModel1>("ItemViewModel1");
   
}

   
public override void AdditionalInitialization()
   
{
       
var itemView1 = Container.Resolve<ItemView1>();
       
RegionManager.Regions["ItemsRegion"].Add(itemView1);
   
}

With this approach it is showing the added views in the shell's itemscontrol in the order the modules are loaded. Based on the role of the logged on user different modules are loaded. Is there a way, without having to add a collection inbetween, to sort the itemscontrol.items on a property of the view's viewmodel for example? Is there a way to force the modules to be loaded in a certain order? I am currently using a module catalog.

Thanks

Andy

Nov 4, 2010 at 9:28 PM

Hi Andy,

If you need to load the ModuleA before others, you could indicate in your other Modules that they depends on ModuleA. For more information on this topic, you could take a look at the following documentation section:

If you are using a ModuleCatalog, it might look like the following:

<Modularity:ModuleInfo ModuleName="ModuleB" ModuleType="ModuleB.ModuleB, ModulesB, Version=1.0.0.0">
<
Modularity:ModuleInfo.DependsOn>
<
sys:String>ModuleA</sys:String>
</
Modularity:ModuleInfo.DependsOn>
</
Modularity:ModuleInfo>
<
Modularity:ModuleInfo ModuleName="ModuleC" ModuleType="ModuleC.ModuleC, ModulesC, Version=1.0.0.0">
<
Modularity:ModuleInfo.DependsOn>
<
sys:String>ModuleB</sys:String>
</
Modularity:ModuleInfo.DependsOn>
</
Modularity:ModuleInfo>

Hope this helps.

Fernando Antivero
http://blogs.southworks.net/fantivero

Nov 8, 2010 at 5:55 AM

I have just been battling with the same issue myself in that I was trying to define an order to modules that were loaded into a WPF TabControl, however I am using a DirectoryModuleCatalog whereby all modules are simply loaded at startup from a directory without any explicit configuration.

The solution proposed whereby you define dependencies between modules seems to be hijacking the dependency feature for a completely different purpose. i.e. Module B may indeed be dependent on Module A to be 'loaded' first, but that doesn't necessarily mean it should be displayed in that order.

The solution I have just come up with was to add a new RegionAdapter and Host Aware Region Behavior to wire up my own binding to a CollectionViewSource that is in turn bound to the Regions.Views collection.

In my bootstrapper, I've added the following method:

protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
	RegionAdapterMappings mappings = base.ConfigureRegionAdapterMappings();
	mappings.RegisterMapping(typeof(TabControl), this.Container.Resolve<TabControlRegionAdapter>());
	return mappings;
}

The TabControlRegionAdapter then looks like this:

public class TabControlRegionAdapter : RegionAdapterBase<TabControl>
{
	public TabControlRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory) {}
	
	protected override void Adapt(IRegion region, TabControl regionTarget) {}
	
	protected override void AttachBehaviors(IRegion region, TabControl regionTarget)
	{
		region.Behaviors.Add(
			TabControlSortedItemsBehavior.BehaviorKey, 
			new TabControlSortedItemsBehavior() { HostControl = regionTarget });
		base.AttachBehaviors(region, regionTarget);
	}

	protected override IRegion CreateRegion()
	{
		return new Region();
	}
}

And the TabControlSortedItemsBehavior looks like this:

public class TabControlSortedItemsBehavior : RegionBehavior, IHostAwareRegionBehavior
{
	public static readonly string BehaviorKey = "TabControlSortedItemsBehavior";
	private TabControl hostControl;

	public DependencyObject HostControl
	{
		get
		{
			return this.hostControl;
		}

		set
		{
			this.hostControl = value as TabControl;
		}
	}

	protected override void OnAttach()
	{
		bool itemsSourceIsSet = this.hostControl.ItemsSource != null;
#if !SILVERLIGHT
		itemsSourceIsSet = itemsSourceIsSet || (BindingOperations.GetBinding(this.HostControl, ItemsControl.ItemsSourceProperty) != null);
#endif
		if (!itemsSourceIsSet)
		{
			CollectionViewSource cvs = new CollectionViewSource();
			cvs.Source = this.Region.Views;
			cvs.SortDescriptions.Add(new SortDescription("Order", ListSortDirection.Ascending));
			this.hostControl.ItemsSource = cvs.View;
		}
	}
}

I then have my views implement an IOrderableView interface that contains a single int property called Order that defines an arbitrary integer value that I can set to define the order I want my view to appear in. This is a very simplistic implementation that doesn't handle re-sorting or anything, but its a starting point that could easily be built upon to expose the CollectionViewSource and provide those features.