继承人的例子:
<Window x:Class="ListViewItemSpacing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:c="clr-namespace:ListViewItemSpacing.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ListViewItemSpacing"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListView FlowDirection="LeftToRight" Background="#222">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Items>
<Rectangle Fill="#27f" Width="100" Height="100"/>
<Rectangle Fill="#27f" Width="100" Height="100"/>
<Rectangle Fill="#27f" Width="100" Height="100"/>
<Rectangle Fill="#27f" Width="100" Height="100"/>
<Rectangle Fill="#27f" Width="100" Height="100"/>
<Rectangle Fill="#27f" Width="100" Height="100"/>
</ListView.Items>
</ListView>
</Grid>
</Window>
此XAML
生成6个蓝色方块,第一行为4,下一行为2。经过4个第一个蓝色方块后,右侧有一些空间。
换句话说 - 它看起来像左对齐的文字。
我希望它看起来像对齐文本,所以我希望调整方块之间的水平间距以左右对齐ListView
,空间均匀分布在元素之间,而不是右侧。
尽管看起来很简单,但我不知道该怎么做。我不知道从哪里开始。例如 - 我可以看到默认添加一些间距。默认情况下很丑,因为水平间距远大于垂直间距,但它不够大,无法将物品对齐左右两侧。但间距来自哪里?如何手动更改?我不想把这些元素搞得一团糟。真实世界app中的元素来自不同的模块,这些模块应该分开。
最明显的想法是处理SizeChanged
的{{1}}事件并手动调整水平间距,但是间距是什么?我的意思是如何在代码后面访问项目间距?
澄清:所需的渲染应该仍然包含6个相同的蓝色方块,但前4个对齐左右控制边缘,其余2个对齐左边和前面的行方块。
默认外观如下:
ListView
期望的样子是这样的:
----------------
|[] [] [] [] |
|[] [] |
----------------
答案 0 :(得分:1)
感谢Rachel和DTig我终于做到了。似乎没有任何面板可以对齐像.slide-transition {
transition: all .4s cubic-bezier(.25,.1,.25,1);
}
.slide-enter {
transform: translate3d(25%, 0, 0);
opacity: 0;
}
.slide-leave {
transform: translate3d(-25%, 0, 0);
opacity: 0;
}
这样的元素,但可以使用可自定义的WrapPanel
。显然 - HorizontalContentAlignment
就是我在这里所需要的。
根据Rachel建议我尝试了this,但它并不支持HorizontalContentAlignment = HorizontalAlignment.Stretch
值。
所以我添加了支持,其中提供的元素宽度彼此相等:
HorizontalAlignment.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:
Stretch
它并不完美,但它完美地完成了特定的工作。如果子元素的大小不同,它将无法正常工作 - 在这种情况下,<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>
应计算为具有从takenWidth
到start
的索引的子项的总和。您还应该为最后一行指定不同的条件。
再次感谢,有点陌生人:)
答案 1 :(得分:0)
假设我们不知道列数。 windows xaml和代码隐藏的完整代码(修复类名以匹配您自己的类名)。试试这个:
<强>的Xaml:强>
<Window x:Class="SO39640127.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:so39640127="clr-namespace:SO39640127"
mc:Ignorable="d"
Title="MainWindow"
Height="{Binding LayoutHeight, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="{Binding LayoutWidth,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
d:DataContext="{d:DesignInstance so39640127:MainWindow}">
<Grid>
<ListView Background="#222" VerticalContentAlignment="Top">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid HorizontalAlignment="Center" VerticalAlignment="Top"
Columns="{Binding LayoutColumns, UpdateSourceTrigger=PropertyChanged}"
Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"></UniformGrid>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Items>
<Rectangle Fill="#27f" Width="{Binding ItemWidth}" Height="{Binding ItemHeight}" Margin="{Binding VerticalSpacing}"/>
<Rectangle Fill="#27f" Width="{Binding ItemWidth}" Height="{Binding ItemHeight}" Margin="{Binding VerticalSpacing}"/>
<Rectangle Fill="#27f" Width="{Binding ItemWidth}" Height="{Binding ItemHeight}" Margin="{Binding VerticalSpacing}"/>
<Rectangle Fill="#27f" Width="{Binding ItemWidth}" Height="{Binding ItemHeight}" Margin="{Binding VerticalSpacing}"/>
<Rectangle Fill="#27f" Width="{Binding ItemWidth}" Height="{Binding ItemHeight}" Margin="{Binding VerticalSpacing}"/>
<Rectangle Fill="#27f" Width="{Binding ItemWidth}" Height="{Binding ItemHeight}" Margin="{Binding VerticalSpacing}"/>
</ListView.Items>
</ListView>
</Grid>
</Window>
<强>代码隐藏:强>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private double itemHeight;
private double itemWidth;
private int layoutColumns;
private int layoutHeight;
private int layoutWidth;
private Thickness verticalSpacing;
public MainWindow()
{
InitializeComponent();
DataContext = this;
InitializeDesign();
}
public double ItemWidth
{
get { return itemWidth; }
set
{
if (value.Equals(itemWidth))
return;
itemWidth = value;
OnPropertyChanged();
}
}
public double ItemHeight
{
get { return itemHeight; }
set
{
if (value.Equals(itemHeight))
return;
itemHeight = value;
OnPropertyChanged();
}
}
public int LayoutColumns
{
get { return layoutColumns; }
set
{
if (value == layoutColumns)
return;
layoutColumns = value;
OnPropertyChanged();
}
}
public int LayoutWidth
{
get { return layoutWidth; }
set
{
if (value == layoutWidth)
return;
layoutWidth = value;
OnPropertyChanged();
UpdateCalculations();
}
}
public int LayoutHeight
{
get { return layoutHeight; }
set
{
if (value == layoutHeight)
return;
layoutHeight = value;
OnPropertyChanged();
UpdateCalculations();
}
}
public Thickness VerticalSpacing
{
get { return verticalSpacing; }
set
{
if (value.Equals(verticalSpacing))
return;
verticalSpacing = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void UpdateCalculations()
{
//Calculate the # of elements that fit on the list view
var totalItems = (int) (LayoutWidth/ItemWidth);
LayoutColumns = totalItems;
}
private void InitializeDesign()
{
LayoutWidth = 525;
LayoutHeight = 350;
ItemWidth = 100;
ItemHeight = 100;
VerticalSpacing = new Thickness(3);
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
我通过物品中的边距实现垂直间距。您可以尝试调整大小,以便根据窗口的尺寸查看列的正确调整方式。另外,我正在使用一点MVVM来进行绑定以进行演示。你当然应该相应地调整它。
希望这有帮助!