RachelとDTigのおかげで私はついにそれを作った。 WrapPanel
のような要素を調整できるパネルはありませんが、カスタマイズ可能なのはHorizontalContentAlignment
です。そして明らかに - HorizontalContentAlignment = HorizontalAlignment.Stretch
は私がここで必要だったものです。
Rachelアドバイスに続いて、私はthisを試しましたが、HorizontalAlignment.Stretch
の値をサポートしていませんでした。
だから設けられた素子幅動作をサポートし、追加互いに等しい:Stretch
配向を有するこの基本的Tig's溶液を添加
/// <summary>
/// <see cref="Panel"/> like <see cref="WrapPanel"/> which supports <see cref="HorizontalContentAlignment"/> property.
/// </summary>
public class AlignableWrapPanel : Panel {
/// <summary>
/// <see cref="HorizontalAlignment"/> property definition.
/// </summary>
public static readonly DependencyProperty HorizontalContentAlignmentProperty =
DependencyProperty.Register(
"HorizontalContentAlignment",
typeof(HorizontalAlignment),
typeof(AlignableWrapPanel),
new FrameworkPropertyMetadata(HorizontalAlignment.Left, FrameworkPropertyMetadataOptions.AffectsArrange)
);
/// <summary>
/// Gets or sets the horizontal alignment of the control's content.
/// </summary>
[BindableAttribute(true)]
public HorizontalAlignment HorizontalContentAlignment {
get { return (HorizontalAlignment)GetValue(HorizontalContentAlignmentProperty); }
set { SetValue(HorizontalContentAlignmentProperty, value); }
}
/// <summary>
/// Measures the size in layout required for child elements and determines a size for the <see cref="AlignableWrapPanel"/>.
/// </summary>
/// <param name="constraint">The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.</param>
/// <returns>The size that this element determines it needs during layout, based on its calculations of child element sizes.</returns>
protected override Size MeasureOverride(Size constraint) {
var curLineSize = new Size();
var panelSize = new Size();
var children = base.InternalChildren;
for (var i = 0; i < children.Count; i++) {
var child = children[i] as UIElement;
// Flow passes its own constraint to children
child.Measure(constraint);
var sz = child.DesiredSize;
if (curLineSize.Width + sz.Width > constraint.Width) { //need to switch to another line
panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);
panelSize.Height += curLineSize.Height;
curLineSize = sz;
if (sz.Width > constraint.Width) { // if the element is wider then the constraint - give it a separate line
panelSize.Width = Math.Max(sz.Width, panelSize.Width);
panelSize.Height += sz.Height;
curLineSize = new Size();
}
}
else { //continue to accumulate a line
curLineSize.Width += sz.Width;
curLineSize.Height = Math.Max(sz.Height, curLineSize.Height);
}
}
// the last line size, if any need to be added
panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);
panelSize.Height += curLineSize.Height;
return panelSize;
}
/// <summary>
/// Positions child elements and determines a size for a <see cref="AlignableWrapPanel"/>.
/// </summary>
/// <param name="arrangeBounds">The final area within the parent that this element should use to arrange itself and its children.</param>
/// <returns>The actual size used.</returns>
protected override Size ArrangeOverride(Size arrangeBounds) {
var firstInLine = 0;
var curLineSize = new Size();
var accumulatedHeight = 0.0;
var children = InternalChildren;
for (var i = 0; i < children.Count; i++) {
var sz = children[i].DesiredSize;
if (curLineSize.Width + sz.Width > arrangeBounds.Width) { //need to switch to another line
ArrangeLine(accumulatedHeight, curLineSize, arrangeBounds.Width, firstInLine, i);
accumulatedHeight += curLineSize.Height;
curLineSize = sz;
if (sz.Width > arrangeBounds.Width) { //the element is wider then the constraint - give it a separate line
ArrangeLine(accumulatedHeight, sz, arrangeBounds.Width, i, ++i);
accumulatedHeight += sz.Height;
curLineSize = new Size();
}
firstInLine = i;
}
else { //continue to accumulate a line
curLineSize.Width += sz.Width;
curLineSize.Height = Math.Max(sz.Height, curLineSize.Height);
}
}
if (firstInLine < children.Count)
ArrangeLine(accumulatedHeight, curLineSize, arrangeBounds.Width, firstInLine, children.Count);
return arrangeBounds;
}
/// <summary>
/// Arranges elements within a line.
/// </summary>
/// <param name="y">Line vertical coordinate.</param>
/// <param name="lineSize">Size of the items line.</param>
/// <param name="boundsWidth">Width of the panel bounds.</param>
/// <param name="start">Index of the first child which belongs to the line.</param>
/// <param name="end">Index of the last child which belongs to the line.</param>
private void ArrangeLine(double y, Size lineSize, double boundsWidth, int start, int end) {
var children = InternalChildren;
var x = 0.0;
var stretchOffset = 0.0;
if (HorizontalContentAlignment == HorizontalAlignment.Center) x = (boundsWidth - lineSize.Width)/2;
else if (HorizontalContentAlignment == HorizontalAlignment.Right) x = (boundsWidth - lineSize.Width);
else if (HorizontalAlignment == HorizontalAlignment.Stretch) {
var childWidth = children[start].DesiredSize.Width; // warning, this works only when all children have equal widths
int n = (int)boundsWidth/(int)childWidth;
if (children.Count > n) {
var takenWidth = n * childWidth;
var spaceWidth = boundsWidth - takenWidth;
stretchOffset = spaceWidth/(n - 1);
}
}
for (var i = start; i < end; i++) {
var child = children[i];
child.Arrange(new Rect(x, y, child.DesiredSize.Width, lineSize.Height));
x += child.DesiredSize.Width + stretchOffset;
}
}
}
。ここで
は、このためのテストXAMLです:
<Window
x:Class="ListViewItemSpacing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:CustomControls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ListViewItemSpacing"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="525"
Height="250"
mc:Ignorable="d">
<Grid>
<ListView Background="#222">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<c:AlignableWrapPanel Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}" HorizontalContentAlignment="Stretch" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Items>
<Rectangle
Width="100"
Height="100"
Fill="#27f" />
<Rectangle
Width="100"
Height="100"
Fill="#27f" />
<Rectangle
Width="100"
Height="100"
Fill="#27f" />
<Rectangle
Width="100"
Height="100"
Fill="#27f" />
<Rectangle
Width="100"
Height="100"
Fill="#27f" />
<Rectangle
Width="100"
Height="100"
Fill="#27f" />
</ListView.Items>
</ListView>
</Grid>
</Window>
これは完璧ではないが、それだけで完全に特定の仕事をしていません。子要素のサイズが異なる場合、正しく動作しません。この場合、は、start
からend
までのインデックスを持つ子の合計として計算する必要があります。最後の行にも異なる条件を指定する必要があります。
おかげで再び、種類見知らぬ人:)
私は今年初めwrapPanelについて同様のWPF質問を見てきました覚えています。その質問にも賞金がありました。それは愚か者になることができますが、残念ながら今私はそれを見つけることができません – ASh
あなたはコメントで、アイテムの '幅 'を知っているので、あなたはコンバータを考えることができます。利用可能な幅と項目の幅を渡してから、コンバーターに 'AvailableWidth/ItemWidth = numOfItems; AvailableWidth%ItemWidth = remainingSpace;残りのスペース/ numOfItems = extraSpacePerItem; 'あなたがそれを必要とすることを割り当てます。おそらく2で割り、その量を各項目の左/右の余白に置きます。 – Rachel
あるいは、[このカスタムパネル](http://stackoverflow.com/a/7747002/302677)があなたにとってうまくいくでしょうか?これは、コンテンツを整列する機能を持っていることを除いて、WrapPanelのように動作するように構築されています。 – Rachel