开发者

How to sort views in an ItemsControl in Prism / MEF?

开发者 https://www.devze.com 2023-01-26 18:26 出处:网络
I use prism v4 and MEF to load my modules. My modules contain a handful of views (MVVM) which are loaded in a ItemsControl/NavigationRegion automatically by MEF.

I use prism v4 and MEF to load my modules. My modules contain a handful of views (MVVM) which are loaded in a ItemsControl/NavigationRegion automatically by MEF.

This works nicely, all items show up in the ItemControl. But I don't like the order in which they show. One module might contain several of the items, so changing the module load order is not enough by itself.

How can I sort the different views in the ItemsControl? Is there any way to sort them by some property?

I use prism V4, MEF and exploration due to a开发者_如何学Gottributes like in the StockTraderRI example.


This is actually baked into Prism4. Just apply the ViewSortHintAttribute to your views:

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

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

The default sort comparer on the regions will pick up this attribute and sort the views accordingly. You can put any string into the attribute but I tend to use medium sized numbers that allow me to easily put a new view in between existing ones.


Oh dang, this was way easier than I expected:

You can tell the region manager how to sort the views in a specific region. You just need to provide a compare function to the region.

This example sorts by a very stupid value, the function name:

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 needs to be known to the region manager before you can set the SortComparison. So far the only workaround I found to achieve this was to defer to set 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 one should use some more useful information than the class name for the sorting order, but this should be easy to solve (I'll just add an interface to all views which might be added to this region which provide a value to sort by).


I'm pretty sure you are looking for the CollectionViewSource. Bea provides some information on how to make use of it in the link.

From an MVVM stance this is how I use the ICollectionView within my ViewModel. The _scriptService.Scripts property is an ObservableCollection<T> getting wrapped in an ICollectionView which is returned to the View. The _view.Filter is being used to filter out items within the ICollection, thus changing the View. Similar to typing 'acc' and seeing all items that begin with 'acc' in your list.

    public class ScriptRepositoryViewModel : AViewModel
    {
        private readonly IUnityContainer _container;
        private readonly IScriptService _scriptService;
        private readonly IEventAggregator _eventAggregator;

        private ICollectionView _view;

        public ScriptRepositoryViewModel(IUnityContainer container, IScriptService scriptService, IEventAggregator eventAggregator)
        {
            _container = container;
            _scriptService = scriptService;
            _eventAggregator = eventAggregator;
        }

        public ICollectionView Scripts
        {
            get 
            {
                if (_view == null)
                {
                    _view = CollectionViewSource.GetDefaultView(_scriptService.Scripts);
                    _view.Filter = Filter;
                }

                return _view;
            }
        }
     }

Below is the code which takes care of the filtering, and is coming in via a DelegateCommand within Prism, this resides in the same ViewModel.

#region SearchCommand
public DelegateCommand<object> SearchCommand { get; private set; }

private String _search = String.Empty;
private void Search(object commandArg)
{
    _search = commandArg as String;
    _view.Refresh();
}

public bool Filter(object arg)
{
    bool usingPrefix;

    IScript script = arg as IScript;
    if (script.FileType == ConvertPrefixToFileType(_search, out usingPrefix))
    {
        if (_search.Length == 2)
            return true;
        else
            return CheckProperties(script, usingPrefix);
    }
    else
    {
        if (usingPrefix)
            return false;
        else
            return CheckProperties(script, usingPrefix);
    }
}

With the base functionality in place and making use of the ICollectionView you can apply your sorting as follows....

_view.SortDescriptions.Add(new SortDescription("PropertyName", direction));

More information on the sorting behavior can be found here, as there are some performance thoughts to keep in mind.


You could use either metadata or properties. It depends on whether you have control over the interface or not...


Views are displayed in the order they are added:

RegionManager.RegisterViewWithRegion("ListRegion", typeof(ListView));
RegionManager.RegisterViewWithRegion("ListRegion", typeof(ListView2));
RegionManager.RegisterViewWithRegion("ListRegion", typeof(ListView3));

will look like:

----region--| | view3 | | view2 | | view |


0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号