Prism для Silverlight: как поддерживать представления в определенном порядке внутри региона

Я создаю своего рода «панель навигации» (которая на самом деле является ItemControl) для SL и использую регионы, чтобы позволить каждому модулю добавлять свою ссылку на панель.

Проблема в том, что загрузка модулей непостоянна, поэтому порядок ссылок в панели может меняться в соответствии с порядком загрузки модулей.

Об ограничении порядка модулей не может быть и речи.

Другим возможным вариантом является порядок коллекции представлений региона, которая привязана к ItemControl, проблема в том, что коллекция ViewCollection очень ограничена, поэтому упорядочить ее довольно сложно.

Я пропустил вариант, у вас есть идея?

Спасибо, Ариэль.


person ArielBH    schedule 11.01.2010    source источник


Ответы (6)


В Prism4 вы просто применяете ViewSortHintAttribute к своим представлениям:

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

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

Компаратор сортировки по умолчанию для регионов подберет этот атрибут и соответствующим образом отсортирует представления. Вы можете поместить любую строку в атрибут, но я обычно использую числа среднего размера, которые позволяют мне легко поместить новое представление между существующими.

person Jesper Larsen-Ledet    schedule 25.12.2010
comment
Важное замечание: использование чисел здесь ненадежно. поскольку реализация сортировки по регионам упорядочивает строки. Использование таких чисел дало хорошие результаты в WPF, но в Silverlight этого не произошло, пока я не использовал a и b, а затем это сработало. - person ArielBH; 09.11.2011
comment
Использование чисел будет работать, если все они имеют одинаковую длину. 100 и 050 будут заказаны правильно, а 100 и 50 - нет. - person Alex; 28.03.2012

Ссылаясь на ответ Сэма, вам сначала нужно создать свой компаратор. Следующий также может отображать представления, которые не имеют специального желания быть размещенными. Чтобы прикрепить этот компаратор к области, которая должна быть отсортирована, вы можете использовать способ, описанный в руководстве по призме:

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( ) );
        }
    }
}
person PVitt    schedule 15.12.2010

По крайней мере, в prism V4 там можно указать региональному менеджеру, как сортировать виды в конкретном регионе. Вам просто нужно предоставить функцию сравнения с регионом.

В этом примере выполняется сортировка по очень глупому значению, имени функции:

private static int CompareViews(object x, object y)
{
  return String.Compare(x.ToString(), y.ToString());
}

this._regionManager.Regions["MyRegion"].SortComparison = CompareViews;

Конечно, регион должен быть известен менеджеру региона, прежде чем вы сможете установить SortComparison. До сих пор единственным обходным путем, который я нашел для достижения этой цели, было отложить установку функции сравнения с помощью 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;
  }));
}

Конечно, вы должны использовать более полезную информацию, чем имя класса для порядка сортировки, но это должно быть легко решить.

person Sam    schedule 26.11.2010

Это не встроено в регионы Prism, однако его легко реализовать.

Дамиан Шенкельман опубликовал созданный им метод расширения для добавления региона в индекс, который, похоже, работает довольно хорошо. http://blogs.southworks.net/dschenkelman/2009/03/14/how-to-add-a-view-to-a-region-in-a-particular-index-with-prism-v2/

Надеюсь это поможет.

person Anderson Imes    schedule 13.01.2010
comment
Нет, это не решение. Предположим, вы добавили представление A в ModuleA с индексом 0 и представление B в ModuleB с индексом 1. Если случайно ModuleB загрузится первым, он попытается добавить его к индексу 1 и потерпит неудачу. - person ArielBH; 05.05.2010
comment
@ArielBH: пример легко изменить, чтобы использовать строковые индексы (это то, что я делаю лично). Надеюсь это поможет. - person Anderson Imes; 05.05.2010
comment
@ArielBH: Думаю, да. Вы должны дать мне немного времени, чтобы извлечь нужные кусочки из хлама. - person Anderson Imes; 06.05.2010
comment
@ Алекс, извини, я не понимаю тебя. - person Anderson Imes; 26.03.2012
comment
@AndersonImes, ArielBH спросил, следует ли вам поделиться своим измененным кодом, и вы сказали, что сделаете это, но я нигде не могу его найти; в этих комментариях или в вашем ответе нет ссылок на какой-либо измененный код. - person Alex; 27.03.2012
comment
@ Алекс: Ах. Я понимаю. Извинения. Я могу попытаться откопать это (это было некоторое время назад). Не обещаю. - person Anderson Imes; 27.03.2012

Я обнаружил, что решение Сэма работает, но обнаружил, что оно выполняет сортировку, когда все представления были добавлены в область, таким образом, сортируя представления дважды.

Хотя это все еще допустимое решение, чтение этого сообщения в обсуждении Prism заставило меня задуматься о способе реализуя это только тогда, когда регион был загружен, но до того, как были добавлены какие-либо представления.

1 – Подпишитесь на коллекцию CollectionChanged of Regions

Я поместил это в код Shell ViewModel, который связан с представлением, содержащим область, которую я хочу отсортировать. Всякий раз, когда импорт IRegionManager разрешается, я подписываюсь на событие CollectionChanged его коллекции Regions:

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

2 — Изменить SortComparison региона в делегате события

Затем делегат Regions_CollectionChanged будет выполняться всякий раз, когда обновляется коллекция Regions, и будет изменять SortComparison нужного мне региона:

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. Определите делегат CompareNavigatorViews

В моем случае я просто сортирую представления по названию сборки, в которой они содержатся, здесь вы можете реализовать свой собственный метод сравнения. Помните, что объекты, которые вы здесь получите, — это представления, а не модели представления.

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);
        }
}

На всякий случай, если кто-то спросит, класс AssemblyInfo — это служебный класс, который я создал. Чтобы получить название сборки, вы можете использовать эту функцию:

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;
    }
}

Надеюсь, это поможет кому-то!

person CGodo    schedule 12.03.2013

Ну и отсутствие подсчета ответов. Я не нашел решения с Prism.

Вместо этого я использовал MEF для решения этой проблемы.

Я напишу об этом сообщение в блоге и обновлю этот заполнитель.

person ArielBH    schedule 14.01.2010
comment
Вы можете свободно обновить этот заполнитель сейчас. У тебя было больше двух лет. :-П - person Alex; 23.03.2012