Переработка WPF-контейнеров - PullRequest
1 голос
/ 22 марта 2010

Я хочу реализовать пользовательский Canvas, который перерабатывает контейнеры при использовании в качестве ItemsPanel. Итак, я получил от VirtualizingPanel и переопределил ArrangeOverride и MeasureOverride. Я делаю генерацию в MeasureOverride так:

var children = base.InternalChildren;

var itemsControl = ItemsControl.GetItemsOwner(this);
var itemsCount = itemsControl.Items.Count;

IItemContainerGenerator generator = itemsControl.ItemContainerGenerator;

var startPos = generator.GeneratorPositionFromIndex(0);

using (generator.StartAt(startPos, GeneratorDirection.Forward, true))
{
    for (int i = 0; i < itemsCount; i++)
    {
        bool isNewlyRealized;

        var child = generator.GenerateNext(out isNewlyRealized) as UIElement;

        if (isNewlyRealized)
        {
            base.AddInternalChild(child);
            generator.PrepareItemContainer(child);
        }

        child.Measure(constraint);
    }
}

Чего я не знаю, так это как сделать переработку. Я пробовал что-то вроде следующего:

protected override void OnItemsChanged(object sender, ItemsChangedEventArgs e)
{
    switch (e.Action)
    {
        case NotifyCollectionChangedAction.Remove:
        case NotifyCollectionChangedAction.Replace:
        case NotifyCollectionChangedAction.Move:
            IRecyclingItemContainerGenerator generator = ItemsControl.GetItemsOwner(this).ItemContainerGenerator;
            generator.Recycle(e.Position, e.ItemUICount);
            RemoveInternalChildRange(e.Position.Index, e.ItemUICount);
            break;
    }
}

Но это не работает. Есть идеи как это сделать?

1 Ответ

3 голосов
/ 18 мая 2010

Смотрите здесь: http://blogs.claritycon.com/blogs/lee_roth/default.aspx

Я делаю утилизацию следующим образом:

В OnItemsChanged я звоню только RemoveInternalChildRange:

protected override void OnItemsChanged(object sender, ItemsChangedEventArgs args)
    {
      switch (args.Action)
      {
        case NotifyCollectionChangedAction.Remove:
        case NotifyCollectionChangedAction.Replace:
          RemoveInternalChildRange(args.Position.Index, args.ItemUICount);
          break;
        case NotifyCollectionChangedAction.Move:
          RemoveInternalChildRange(args.OldPosition.Index, args.ItemUICount);
          break;
      }
    }

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

Здесь мы добавляем новые элементы:

GeneratorPosition start = ItemContainerGenerator.GeneratorPositionFromIndex(iFirstItemIndex);
      int iChildIndex = (start.Offset == 0) ? start.Index : start.Index + 1;
      using (ItemContainerGenerator.StartAt(start, GeneratorDirection.Forward, true))
      {
        for (int i = iFirstItemIndex; i <= iLastItemIndex; i++, iChildIndex++)
        {
          bool bNew;
          UIElement element = (UIElement)ItemContainerGenerator.GenerateNext(out bNew);
          //If we get a new instance
          if (bNew)
          {
            if (iChildIndex >= Children.Count) AddInternalChild(element);
            else InsertInternalChild(iChildIndex, element);
            ItemContainerGenerator.PrepareItemContainer(element);
          }
          //If we get a recycled element
          else if (!InternalChildren.Contains(element))
          {
            InsertInternalChild(iChildIndex, element);
            ItemContainerGenerator.PrepareItemContainer(element);
          }
          element.Measure(...);
        }
      }

После добавления элементов мы удаляем старые элементы:

for (int i = Children.Count - 1; i >= 0; i--)
      {
        GeneratorPosition childGeneratorPosition = new GeneratorPosition(i, 0);
        int iIndex = ItemContainerGenerator.IndexFromGeneratorPosition(childGeneratorPosition);
        if (iIndex < iFirstItemIndex || iIndex > iLastItemIndex)
        {
          //remove() calls ItemContainerGenerator.remove() OR recycle(). Both works.
          remove(childGeneratorPosition, 1);
          RemoveInternalChildRange(i, 1);
        }
      }

Я надеюсь, что смогу вам помочь.

...