WPF ListBox WrapPanel обрезает длинные группы - PullRequest
3 голосов
/ 16 сентября 2008

Я создал ListBox для отображения элементов в группах, где группы переносятся справа налево, когда они больше не могут поместиться в пределах высоты панели ListBox. Таким образом, группы будут выглядеть примерно так в списке, где высота каждой группы произвольна (например, группа 1 в два раза больше группы 2):

[ 1 ][ 3 ][ 5 ]
[   ][ 4 ][ 6 ]
[ 2 ][   ]

Следующий XAML работает правильно, поскольку он выполняет перенос и позволяет горизонтальной полосе прокрутки появляться, когда элементы выходят с правой стороны ListBox.

<ListBox> 
  <ListBox.ItemsPanel> 
    <ItemsPanelTemplate> 
      <StackPanel Orientation="Vertical"/> 
    </ItemsPanelTemplate> 
  </ListBox.ItemsPanel> 

  <ListBox.GroupStyle> 
    <ItemsPanelTemplate> 
      <WrapPanel Orientation="Vertical" 
                 Height="{Binding Path=ActualHeight, 
                          RelativeSource={RelativeSource 
                            FindAncestor, 
                            AncestorLevel=1, 
                            AncestorType={x:Type ScrollContentPresenter}}}"/> 
    </ItemsPanelTemplate> 
  </ListBox.GroupStyle> 
</ListBox>

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

Есть ли способ включить полосу прокрутки или другой способ обойти эту проблему, которую я не вижу?

Ответы [ 4 ]

2 голосов
/ 17 сентября 2008

Вот слегка измененный код - все заслуги, данные Abe Heidebrecht, который ранее разместил его, - который позволяет как горизонтальную, так и вертикальную прокрутку. Единственное изменение заключается в том, что возвращаемое значение MeasureOverride должно быть base.MeasureOverride (новый размер (ret.width, h)).

// Original code : Abe Heidebrecht
public class SmartWrapPanel : WrapPanel
{
  /// <summary>
  /// Identifies the DesiredHeight dependency property
  /// </summary>
  public static readonly DependencyProperty DesiredHeightProperty = DependencyProperty.Register(
    "DesiredHeight",
    typeof(double),
    typeof(SmartWrapPanel),
    new FrameworkPropertyMetadata(Double.NaN, 
            FrameworkPropertyMetadataOptions.AffectsArrange |
            FrameworkPropertyMetadataOptions.AffectsMeasure));

  /// <summary>
  /// Gets or sets the height to attempt to be.  If any child is taller than this, will use the child's height.
  /// </summary>
  public double DesiredHeight
  {
    get { return (double)GetValue(DesiredHeightProperty); }
    set { SetValue(DesiredHeightProperty, value); }
  }

  protected override Size MeasureOverride(Size constraint)
  {
    Size ret = base.MeasureOverride(constraint);
    double h = ret.Height;

    if (!Double.IsNaN(DesiredHeight))
    {
      h = DesiredHeight;
      foreach (UIElement child in Children)
      {
        if (child.DesiredSize.Height > h)
          h = child.DesiredSize.Height;
      }
    }

    return base.MeasureOverride(new Size(ret.Width, h));
  }

  protected override System.Windows.Size ArrangeOverride(Size finalSize)
  {
    double h = finalSize.Height;

    if (!Double.IsNaN(DesiredHeight))
    {
      h = DesiredHeight;
      foreach (UIElement child in Children)
      {
        if (child.DesiredSize.Height > h)
          h = child.DesiredSize.Height;
      }
    }

    return base.ArrangeOverride(new Size(finalSize.Width, h));
  }
}
2 голосов
/ 16 сентября 2008

Устанавливая свойство Height в WrapPanel в высоту ScrollContentPresenter, оно никогда не будет прокручиваться по вертикали. Однако, если вы удалите эту привязку, она никогда не будет перенесена, поскольку на этапе компоновки она имеет бесконечную высоту для макета.

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

Вот пример панели для этого:

public class SmartWrapPanel : WrapPanel
{
    /// <summary>
    /// Identifies the DesiredHeight dependency property
    /// </summary>
    public static readonly DependencyProperty DesiredHeightProperty = DependencyProperty.Register(
        "DesiredHeight",
        typeof(double),
        typeof(SmartWrapPanel),
        new FrameworkPropertyMetadata(Double.NaN, 
            FrameworkPropertyMetadataOptions.AffectsArrange |
            FrameworkPropertyMetadataOptions.AffectsMeasure));

    /// <summary>
    /// Gets or sets the height to attempt to be.  If any child is taller than this, will use the child's height.
    /// </summary>
    public double DesiredHeight
    {
        get { return (double)GetValue(DesiredHeightProperty); }
        set { SetValue(DesiredHeightProperty, value); }
    }

    protected override Size MeasureOverride(Size constraint)
    {
        Size ret = base.MeasureOverride(constraint);
        double h = ret.Height;

        if (!Double.IsNaN(DesiredHeight))
        {
            h = DesiredHeight;
            foreach (UIElement child in Children)
            {
                if (child.DesiredSize.Height > h)
                    h = child.DesiredSize.Height;
            }
        }

        return new Size(ret.Width, h);
    }

    protected override System.Windows.Size ArrangeOverride(Size finalSize)
    {
        double h = finalSize.Height;

        if (!Double.IsNaN(DesiredHeight))
        {
            h = DesiredHeight;
            foreach (UIElement child in Children)
            {
                if (child.DesiredSize.Height > h)
                    h = child.DesiredSize.Height;
            }
        }

        return base.ArrangeOverride(new Size(finalSize.Width, h));
    }
}
0 голосов
/ 16 сентября 2008

Спасибо, что ответили, Дэвид.

Если привязка равна removed, перенос не выполняется. WrapPanel помещает каждую группу в один вертикальный столбец.

Связывание предназначено для того, чтобы заставить WrapPanel фактически обернуться. Если привязка не установлена, WrapPanel предполагает, что высота бесконечна и никогда не переносится.

Привязка к MinHeight приводит к пустому списку. Я могу видеть, как свойство VerticalAlignment может показаться решением, но само выравнивание предотвращает возникновение переноса. Когда связывание и выравнивание используются вместе, выравнивание не влияет на проблему.

0 голосов
/ 16 сентября 2008

Я думаю, что вы правы, что это связано с привязкой. Что происходит при удалении привязки? С привязкой вы пытаетесь заполнить хотя бы всю высоту списка? Если это так, рассмотрите возможность привязки к MinHeight или попробуйте использовать свойство VerticalAlignment.

...