Custome StackPanel Prism RegionAdapter для поддержки заказов

У меня есть следующая реализация RegionAdapter для StackPanel, но мне нужен строгий порядок элементов, которые я связываю с регионом, может кто-нибудь помочь?

Я хочу, чтобы представления, которые регистрируют себя в регионе, могли иметь возможность контролировать их положение, может быть, какой-то индекс

    protected override void Adapt(IRegion region, StackPanel regionTarget)
    {
        region.Views.CollectionChanged += (sender, e) =>
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (FrameworkElement element in e.NewItems)
                    {
                        regionTarget.Children.Add(element);
                    }

                    break;

                case NotifyCollectionChangedAction.Remove:
                    foreach (UIElement elementLoopVariable in e.OldItems)
                    {
                        var element = elementLoopVariable;
                        if (regionTarget.Children.Contains(element))
                        {
                            regionTarget.Children.Remove(element);
                        }
                    }

                    break;
            }
        };
    }

3 ответа

Решение

Способ решения этой проблемы во многом зависит от того, относится ли сортировка к (а) типу представления или (б) к экземпляру представления. Первый будет иметь место, если вы хотите указать это, например, представления типа ViewA должно быть выше представления типа ViewB, Последнее имеет место, если вы хотите указать, как сортируются несколько конкретных экземпляров одного и того же типа представления.

A. сортировать по типу

Опция заключается в реализации пользовательского атрибута, что-то вроде OrderIndexAttribute, который предоставляет целочисленное свойство:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public OrderIndexAttribute : Attribute
{
    public int Index { get; }

    public OrderIndexAttribute(int index)
    {
        Index = index;
    }
}

Пометьте свой класс представления этим атрибутом:

[OrderIndex(2)]
public ViewA : UserControl
{...}

Получите атрибут типа при добавлении представления в регион:

case NotifyCollectionChangedAction.Add:
    foreach (FrameworkElement element in e.NewItems)
    {
        // Get index for view
        var viewType = element.GetType();
        var viewIndex= viewType.GetCustomAttribute<OrderIndexAttribute>().Index;
        // This method needs to iterate through the views in the region and determine
        // where a view with the specified index needs to be inserted
        var insertionIndex = GetInsertionIndex(viewIndex);
        regionTarget.Children.Insert(insertionIndex, element);
    }
    break;

Б. Сортировать пример мудрым

Сделайте так, чтобы ваши представления реализовали интерфейс:

public interface ISortedView 
{
   int Index { get; }
}

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

case NotifyCollectionChangedAction.Add:
    foreach (FrameworkElement element in e.NewItems)
    {
        // Get index for view
        var sortedView = element as ISortedView;
        if (sortedView != null)
        {
            var viewIndex = sortedView.Index;
            // This method needs to iterate through the views in the region and determine
            // where a view with the specified index needs to be inserted
            var insertionIndex = GetInsertionIndex(viewIndex);
            regionTarget.Children.Insert(insertionIndex, sortedView);
        }
        else
        { // Add at the end of the StackPanel or reject adding the view to the region }
    }

Ответ от Марка и Р. Эванса помог мне создать мой собственный, более универсальный RegionAdapter со следующими улучшениями:

  • использует ViewSortHint для совместимости с Prism 6
  • Совместимость с Prism 7 / .Net 5
  • Вспомогательный класс для использования в нескольких адаптерах
  • меньше кода

Метод адаптации:

          protected override void Adapt(IRegion region, StackPanel regionTarget)
    {
        region.Views.CollectionChanged += (s, e) =>
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
                foreach (FrameworkElement item in e.NewItems)
                {
                    regionTarget.Children.Insert(regionTarget.Children.GetInsertionIndex(item), item);
                }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
                foreach (UIElement item in e.OldItems)
                {
                    if (regionTarget.Children.Contains(item))
                    {
                        regionTarget.Children.Remove(item);
                    }
                }
        };
    }

Помощник / расширение:

      internal static int GetInsertionIndex(this IList items, in object newItem)
    {
        // Return the list index of the viewIndex
        foreach (object item in items)
        {
            var currentIndex = item.GetType().GetCustomAttribute<ViewSortHintAttribute>()?.Hint ?? "0";
            var intendedIndex = newItem.GetType().GetCustomAttribute<ViewSortHintAttribute>()?.Hint ?? "0";

            if (currentIndex.CompareTo(intendedIndex) >= 0)
                return items.IndexOf(item);
        }

        // if no greater index is found, insert the item at the end
        return items.Count;
    }

Я обнаружил, что случай Марка "Сортировка по типу A." очень помог в моей ситуации. Мне нужно было отсортировать представления по региону с помощью атрибута OrderIndexAttribute и по-прежнему иметь возможность добавлять представление, если оно фактически не имело атрибута OrderIndexAttribute.

Как вы увидите ниже, я сделал это, отслеживая индексы представлений в списке. Индекс вставки представления без атрибута по умолчанию равен нулю, так что он сортируется в начало (или верх) StackPanel.

Этот исходный пост довольно старый, но, возможно, кто-то наткнется на него, как и я, и сочтет мой вклад полезным. Предложения по рефакторингу приветствуются.:-)

public class StackPanelRegionAdapter : RegionAdapterBase<StackPanel>
{
    private readonly List<int> _indexList;

    public StackPanelRegionAdapter(IRegionBehaviorFactory behaviorFactory)
        : base(behaviorFactory)
    {
        _indexList = new List<int>();
    }

    protected override void Adapt(IRegion region, StackPanel regionTarget)
    {
        
        region.Views.CollectionChanged += (sender, e) =>
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (FrameworkElement element in e.NewItems)
                    {
                        var viewType = element.GetType();

                        // Get the custom attributes for this view, if any
                        var customAttributes = viewType.GetCustomAttributes(false);

                        var viewIndex = 0;  // Default the viewIndex to zero

                        // Determine if the view has the OrderIndexAttribute.
                        // If it does have the OrderIndexAttribute, get its sort index.
                        if (HasThisAttribute(customAttributes))
                        {
                            viewIndex= viewType.GetCustomAttribute<OrderIndexAttribute>().Index;
                        }
                        
                        // Get the insertion index
                        var insertionIndex = GetInsertionIndex(viewIndex);

                        regionTarget.Children.Insert(insertionIndex, element);
                    }
                    break;

                case NotifyCollectionChangedAction.Remove:
                    foreach (UIElement elementLoopVariable in e.OldItems)
                    {
                        var element = elementLoopVariable;
                        if (regionTarget.Children.Contains(element))
                        {
                            regionTarget.Children.Remove(element);
                        }
                    }
                    break;
            }
        };
    }

    private static bool HasThisAttribute(IReadOnlyList<object> customAttributes)
    {
        // Determine if the view has the OrderIndexAttribute
        if (customAttributes.Count == 0) return false;
        for (var i = 0; i < customAttributes.Count; i++)
        {
            var name = customAttributes[i].GetType().Name;
            if (name == "OrderIndexAttribute") return true;
        }

        return false;
    }

    private int GetInsertionIndex(in int viewIndex)
    {
        // Add the viewIndex to the index list if not already there
        if (_indexList.Contains(viewIndex) == false)
        {
            _indexList.Add(viewIndex);
            _indexList.Sort();
        }

        // Return the list index of the viewIndex
        for (var i = 0; i < _indexList.Count; i++)
        {
            if (_indexList[i].Equals(viewIndex))
            {
                return i;
            }
        }

        return 0;
    }

    protected override IRegion CreateRegion()
    {
        return new AllActiveRegion();
    }
}
Другие вопросы по тегам