Prism for Silverlight: how to maintain views in a specific order within a region

I create a “navigation bar” (which is actually an ItemControl) for SL and uses regions so that each module can add its own link to the bar.

The problem is that the loading of modules is inconsistent and, therefore, the order of connections in the panel can change in accordance with the order of loading of modules.

Limiting the order of the modules is out of the question.

Another possible option is to streamline the collection of views of the area bound to the ItemControl, the problem is that the ViewCollection is very limited, so it’s quite difficult to organize it.

I missed the option, do you have an idea?

Thanks Ariel

+3
source share
6 answers

In Prism4, you simply apply the ViewSortHintAttribute to your views:

[ViewSortHint("100")] class FirstView : UserControl { } [ViewSortHint("200")] class SecondView : UserControl { } 

By default, a regional sorting sorter selects this attribute and sorts the views accordingly. You can put any string in an attribute, but I tend to use medium-sized numbers that allow me to easily add a new view between existing ones.

+7
source

Referring to Sam's answer, you first need to create your companion. The following method also allows viewing which do not have the desired placement. To attach this companion to the region that needs to be sorted, you can use the method superimposed in the prism manual:

 public partial class MainView : UserControl { public MainView( ) { InitializeComponent( ); ObservableObject<IRegion> observableRegion = RegionManager.GetObservableRegion( ContentHost ); observableRegion.PropertyChanged += ( sender, args ) => { IRegion region = ( (ObservableObject<IRegion>)sender ).Value; region.SortComparison = CompareViews; }; } private static int CompareViews( object x, object y ) { IPositionView positionX = x as IPositionView; IPositionView positionY = y as IPositionView; if ( positionX != null && positionY != null ) { //Position is a freely choosable integer return Comparer<int>.Default.Compare( positionX.Position, positionY.Position ); } else if ( positionX != null ) { //x is a PositionView, so we favour it here return -1; } else if ( positionY != null ) { //y is a PositionView, so we favour it here return 1; } else { //both are no PositionViews, so we use string comparison here return String.Compare( x.ToString( ), y.ToString( ) ); } } } 
+3
source

At least in the V4 prism, you can tell the region manager how to sort the views in a particular region. You just need to provide a comparison function for the region.

This example is sorted by a very silly value, the name of the function:

 private static int CompareViews(object x, object y) { return String.Compare(x.ToString(), y.ToString()); } this._regionManager.Regions["MyRegion"].SortComparison = CompareViews; 

Of course, the region must be known to the region manager before you can install SortComparison. So far, the only workaround I have found for this is to defer the installation of the comparison function using the dispatcher:

 private readonly IRegionManager _regionManager; [ImportingConstructor] public ShellViewModel(IRegionManager regionManager) { this._regionManager = regionManager; Dispatcher dp = Dispatcher.CurrentDispatcher; dp.BeginInvoke(DispatcherPriority.ApplicationIdle, new ThreadStart(delegate { if (this._regionManager.Regions.ContainsRegionWithName("MyRegion")) this._regionManager.Regions["MyRegion"].SortComparison = CompareViews; })); } 

Of course, you should use more useful information than the class name for the sort order, but this should be easy to solve.

+2
source

This is not built into the Prism domain, but it is easy to implement.

Damian Schenkelman published an extension method that he created to add a region to the index, which seems to work very well. http://blogs.southworks.net/dschenkelman/2009/03/14/how-to-add-a-view-to-a-region-in-a-particular-index-with-prism-v2/

Hope this helps.

+1
source

I found that Sam's solution worked, but found that he was sorting when all the views were added to the scope, thus sorting the views twice.

Despite the fact that this is still a valid solution, after reading this post in the Prism discussion, I thought about how to implement this only when the region was loaded, but before all the views were added yet.

1 - Subscribe to CollectionChanged of Regions

I put this in the ViewModel shell code, which is associated with a view that contains the area I want to sort. Whenever importing IRegionManager was allowed, I subscribe to the CollectionChanged event in its collection of regions:

 this._regionManager.Regions.CollectionChanged += new NotifyCollectionChangedEventHandler(Regions_CollectionChanged); 

2 - Change the region's SortComparison to delegate deletion

Then the Regions_CollectionChanged delegate will be executed whenever the Collection of Regions is updated and SortComparison changes my desired area:

 void Regions_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Add) { foreach (var o in e.NewItems) { IRegion region = o as IRegion; if (region != null && region.Name == RegionNames.NavigationRegion) { region.SortComparison = CompareNavigatorViews; } } } } 

3 - Define a CompareNavigatorViews delegate

In my case, I just sort the views by the assembly header where they are contained, you can implement your own comparison method here. Remember that the objects you get here are Views, not ViewModels.

 private static int CompareNavigatorViews(object x, object y) { if (x == null) if (y == null) return 0; else return -1; else if (y == null) return 1; else { AssemblyInfo xAssemblyInfo = new AssemblyInfo(Assembly.GetAssembly(x.GetType())); AssemblyInfo yAssemblyInfo = new AssemblyInfo(Assembly.GetAssembly(y.GetType())); return String.Compare(xAssemblyInfo.Title, yAssemblyInfo.Title); } } 

Just in case someone asks, the AssemblyInfo class is the utility class that I created. To get the name of the assembly, you can use this function:

 string GetAssemblyTitle(Assembly assembly) { object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), false); if (attributes.Length == 1) { return (attributes[0] as AssemblyTitleAttribute).Title; } else { // Return the assembly name if there is no title return this.GetType().Assembly.GetName().Name; } } 

Hope this helps someone!

+1
source

Well, as a lack of answers. I did not find a solution with Prism.

Instead, I used MEF to solve this problem.

I will write a blog post and update this place.

0
source

All Articles