C# WPF列表框布局:多列
我有一个包含复选框的列表框(WPF)。我在配置屏幕中使用的是。下面的示意图示例: 现在我想添加一个“test5”复选框。我的垂直空间有限,因此我希望它显示在水平方向,如下所示: 是否可以修改列表框布局,使复选框按如下方式排列?C# WPF列表框布局:多列,c#,wpf,xaml,C#,Wpf,Xaml,我有一个包含复选框的列表框(WPF)。我在配置屏幕中使用的是。下面的示意图示例: 现在我想添加一个“test5”复选框。我的垂直空间有限,因此我希望它显示在水平方向,如下所示: 是否可以修改列表框布局,使复选框按如下方式排列? <ListBox Name="CategoryListBox" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemsSource="{Binding Path=
<ListBox Name="CategoryListBox"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Path=RefValues,
UpdateSourceTrigger=PropertyChanged}"
SelectionMode="Multiple">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal"
MinWidth="150" MaxWidth="150"
Margin="0,5, 0, 5" >
<CheckBox
Name="checkedListBoxItem"
IsChecked="{Binding
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBoxItem} },
Path=IsSelected, Mode=TwoWay}" />
<ContentPresenter
Content="{Binding
RelativeSource={RelativeSource TemplatedParent},
Path=Content}"
Margin="5,0, 0, 0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
或者像这样简单:
<Grid>
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBoxItem>listbox item 1</ListBoxItem>
<ListBoxItem>listbox item 2</ListBoxItem>
<ListBoxItem>listbox item 3</ListBoxItem>
<ListBoxItem>listbox item 4</ListBoxItem>
<ListBoxItem>listbox item 5</ListBoxItem>
</ListBox>
</Grid>
列表框项目1
列表框项目2
列表框项目3
列表框项目4
列表框项目5
我遇到了类似的问题,eibhrum的回答给了我一些想法。我使用了下面的代码,我认为这也是您需要的。我使用了UniformGrid而不是WrapPanel
<ListBox HorizontalAlignment="Stretch"
ItemsSource="{Binding Timers}"
>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<!-- UNIFORM GRID HERE -->
<UniformGrid Columns="3" IsItemsHost="True"
HorizontalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border>
<StackPanel Orientation="Vertical" >
<TextBlock Text="{Binding Label}" TextWrapping="Wrap"/>
<Separator Margin="5,0,10,0"/>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
我知道这是一篇比较老的文章,但在我试图解决同样的问题时,我偶然发现了一个相当简单的方法: 只需添加绑定数据源(或根据需要添加项)
具有多列和ListBoxItem方向的ListBox是垂直的。 列表框具有固定高度和自动宽度。添加ListBoxItem时,ListBox将自动增加宽度
<ListBox Height="81" VerticalAlignment="Top" HorizontalAlignment="Left" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBoxItem Content="1"/>
<ListBoxItem Content="2"/>
<ListBoxItem Content="3"/>
<ListBoxItem Content="4"/>
<ListBoxItem Content="5"/>
<ListBoxItem Content="6"/>
<ListBoxItem Content="7"/>
<ListBoxItem Content="8"/>
<ListBoxItem Content="9"/>
<ListBoxItem Content="10"/>
</ListBox>
具有多列和ListBoxItem方向的ListBox是水平的。列表框具有固定宽度和自动高度。添加ListBoxItem时,ListBox将自动增加高度
<ListBox Width="81" VerticalAlignment="Top" HorizontalAlignment="Left" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBoxItem Content="1"/>
<ListBoxItem Content="2"/>
<ListBoxItem Content="3"/>
<ListBoxItem Content="4"/>
<ListBoxItem Content="5"/>
<ListBoxItem Content="6"/>
<ListBoxItem Content="7"/>
<ListBoxItem Content="8"/>
<ListBoxItem Content="9"/>
<ListBoxItem Content="10"/>
</ListBox>
如果需要在多个区域(在我的例子中是多个窗口)之间流动行,可以使用自定义面板实现 用法示例:
<Grid>
<Grid.Resources>
<ItemsPanelTemplate x:Key="ItemsPanelTemplate">
<local:SharedLayoutStackPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
<local:SharedLayoutCoordinator x:Key="slc" ItemsSource="{Binding Path=MyItems}" />
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Margin="10,10,5,10" BorderBrush="Black" BorderThickness="1">
<ListBox
local:SharedLayoutCoordinator.Region="{Binding Source={StaticResource slc}, Path=[0]}"
ItemsPanel="{StaticResource ResourceKey=ItemsPanelTemplate}" />
</Border>
<Border Grid.Column="1" Margin="5,10" BorderBrush="Black" BorderThickness="1">
<ListBox
local:SharedLayoutCoordinator.Region="{Binding Source={StaticResource slc}, Path=[1]}"
ItemsPanel="{StaticResource ResourceKey=ItemsPanelTemplate}" />
</Border>
<Border Grid.Column="2" Margin="5,10,10,10" BorderBrush="Black" BorderThickness="1">
<ListBox
local:SharedLayoutCoordinator.Region="{Binding Source={StaticResource slc}, Path=[2]}"
ItemsPanel="{StaticResource ResourceKey=ItemsPanelTemplate}" />
</Border>
</Grid>
结果:
完整的演示实现是完整的,但关键点如下
SharedLayoutCoordinator.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace MultiRegionListBox
{
internal class SharedLayoutCoordinator : DependencyObject
{
private List<SharedLayoutRegion> Regions = new List<SharedLayoutRegion>();
public SharedLayoutRegion this[int index]
{
get
{
var slr = new SharedLayoutRegion(this, index);
for (int i = 0; i < Regions.Count; i++)
{
if (Regions[i].Index > index)
{
Regions.Insert(i, slr);
return slr;
}
}
Regions.Add(slr);
return slr;
}
}
public object ItemsSource
{
get { return (object)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(object), typeof(SharedLayoutCoordinator), new PropertyMetadata(null));
public static SharedLayoutRegion GetRegion(DependencyObject obj)
{
return (SharedLayoutRegion)obj.GetValue(RegionProperty);
}
public static void SetRegion(DependencyObject obj, SharedLayoutRegion value)
{
obj.SetValue(RegionProperty, value);
}
public static readonly DependencyProperty RegionProperty =
DependencyProperty.RegisterAttached("Region", typeof(SharedLayoutRegion),
typeof(SharedLayoutCoordinator), new PropertyMetadata(null, Region_Changed));
private static void Region_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var itemsControl = (ItemsControl)d;
var newController = (SharedLayoutRegion)e.NewValue;
if (newController == null)
{
return;
}
itemsControl.SetBinding(ItemsControl.ItemsSourceProperty, new Binding(nameof(ItemsSource)) { Source = newController.Coordinator });
}
public static SharedLayoutRegion GetParentSharedLayoutController(DependencyObject obj)
{
while (obj != null)
{
if (obj is ItemsControl ic)
{
var slc = GetRegion(ic);
if (slc != null)
{
return slc;
}
}
obj = VisualTreeHelper.GetParent(obj);
}
return null;
}
public IEnumerable<SharedLayoutRegion> GetPreceedingRegions(SharedLayoutRegion region)
{
return Regions.Where(r => r.Index < region.Index);
}
internal SharedLayoutRegion GetNextRegion(SharedLayoutRegion region)
{
var idx = Regions.IndexOf(region);
if (idx + 1 < Regions.Count)
{
return Regions[idx + 1];
}
return null;
}
internal SharedLayoutRegion GetPreviousRegion(SharedLayoutRegion region)
{
var idx = Regions.IndexOf(region);
if (idx > 0)
{
return Regions[idx - 1];
}
return null;
}
}
internal class SharedLayoutRegion
{
private Action InvalidateMeasureCallback;
public SharedLayoutRegion(SharedLayoutCoordinator coord, int index)
{
this.Coordinator = coord;
this.Index = index;
}
public SharedLayoutCoordinator Coordinator { get; }
public int Index { get; }
public SharedLayoutStackPanel Panel { get; set; }
public bool IsMeasureValid
=> !(Panel == null || !Panel.IsMeasureValid || Panel.IsMeasureMeaningless);
internal bool CanMeasure(Action invalidateMeasure)
{
if (Coordinator.GetPreceedingRegions(this).All(pr => pr.IsMeasureValid))
{
return true;
}
this.InvalidateMeasureCallback = invalidateMeasure;
return false;
}
public int StartOfRegion => Coordinator.GetPreviousRegion(this)?.EndOfRegion ?? 0;
public int CountInRegion { get; set; }
public int EndOfRegion => CountInRegion + StartOfRegion;
public bool HasNextRegion => Coordinator.GetNextRegion(this) != null;
internal void OnMeasure()
{
var nextRegion = Coordinator.GetNextRegion(this);
if (nextRegion != null && nextRegion.InvalidateMeasureCallback != null)
{
nextRegion.InvalidateMeasureCallback();
nextRegion.InvalidateMeasureCallback = null;
}
}
}
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.Threading.Tasks;
使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Data;
使用System.Windows.Media;
名称空间多区域列表框
{
内部类SharedLayoutCoordinator:DependencyObject
{
私有列表区域=新列表();
public SharedLayoutRegion此[int索引]
{
得到
{
var slr=新的SharedLayoutRegion(此,索引);
对于(int i=0;i索引)
{
区域。插入(i,单反);
返回单反;
}
}
区域。添加(单反);
返回单反;
}
}
公共对象项资源
{
获取{return(object)GetValue(ItemsSourceProperty);}
set{SetValue(ItemsSourceProperty,value);}
}
公共静态只读依赖项Property ItemsSourceProperty=
DependencyProperty.Register(“ItemsSource”、typeof(object)、typeof(SharedLayoutCoordinator)、new PropertyMetadata(null));
公共静态SharedLayoutRegion GetRegion(DependencyObject obj)
{
返回(SharedLayoutRegion)对象GetValue(RegionProperty);
}
公共静态void SetRegion(DependencyObject对象,SharedLayoutRegion值)
{
对象设置值(RegionProperty,value);
}
公共静态只读DependencyProperty RegionProperty=
DependencyProperty.RegisterAttached(“地区”),类型(SharedLayoutRegion),
typeof(SharedLayoutCoordinator),新的PropertyMetadata(null,Region_Changed));
私有静态无效区域\u已更改(DependencyObject d、DependencyPropertyChangedEventArgs e)
{
var itemsControl=(itemsControl)d;
var newController=(SharedLayoutRegion)e.NewValue;
if(newController==null)
{
返回;
}
itemsControl.SetBinding(itemsControl.ItemsSourceProperty,新绑定(nameof(ItemsSource)){Source=newController.Coordinator});
}
公共静态SharedLayoutRegion GetParentSharedLayoutController(DependencyObject obj)
{
while(obj!=null)
{
if(obj是项目控制ic)
{
var slc=GetRegion(ic);
如果(slc!=null)
{
返回slc;
}
}
obj=VisualTreeHelper.GetParent(obj);
}
返回null;
}
公共IEnumerable GetPrecedingRegion(共享布局区域)
{
返回区域。其中(r=>r.Index0)
{
返回区[idx-1];
}
返回null;
}
}
内部类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace MultiRegionListBox
{
internal class SharedLayoutCoordinator : DependencyObject
{
private List<SharedLayoutRegion> Regions = new List<SharedLayoutRegion>();
public SharedLayoutRegion this[int index]
{
get
{
var slr = new SharedLayoutRegion(this, index);
for (int i = 0; i < Regions.Count; i++)
{
if (Regions[i].Index > index)
{
Regions.Insert(i, slr);
return slr;
}
}
Regions.Add(slr);
return slr;
}
}
public object ItemsSource
{
get { return (object)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(object), typeof(SharedLayoutCoordinator), new PropertyMetadata(null));
public static SharedLayoutRegion GetRegion(DependencyObject obj)
{
return (SharedLayoutRegion)obj.GetValue(RegionProperty);
}
public static void SetRegion(DependencyObject obj, SharedLayoutRegion value)
{
obj.SetValue(RegionProperty, value);
}
public static readonly DependencyProperty RegionProperty =
DependencyProperty.RegisterAttached("Region", typeof(SharedLayoutRegion),
typeof(SharedLayoutCoordinator), new PropertyMetadata(null, Region_Changed));
private static void Region_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var itemsControl = (ItemsControl)d;
var newController = (SharedLayoutRegion)e.NewValue;
if (newController == null)
{
return;
}
itemsControl.SetBinding(ItemsControl.ItemsSourceProperty, new Binding(nameof(ItemsSource)) { Source = newController.Coordinator });
}
public static SharedLayoutRegion GetParentSharedLayoutController(DependencyObject obj)
{
while (obj != null)
{
if (obj is ItemsControl ic)
{
var slc = GetRegion(ic);
if (slc != null)
{
return slc;
}
}
obj = VisualTreeHelper.GetParent(obj);
}
return null;
}
public IEnumerable<SharedLayoutRegion> GetPreceedingRegions(SharedLayoutRegion region)
{
return Regions.Where(r => r.Index < region.Index);
}
internal SharedLayoutRegion GetNextRegion(SharedLayoutRegion region)
{
var idx = Regions.IndexOf(region);
if (idx + 1 < Regions.Count)
{
return Regions[idx + 1];
}
return null;
}
internal SharedLayoutRegion GetPreviousRegion(SharedLayoutRegion region)
{
var idx = Regions.IndexOf(region);
if (idx > 0)
{
return Regions[idx - 1];
}
return null;
}
}
internal class SharedLayoutRegion
{
private Action InvalidateMeasureCallback;
public SharedLayoutRegion(SharedLayoutCoordinator coord, int index)
{
this.Coordinator = coord;
this.Index = index;
}
public SharedLayoutCoordinator Coordinator { get; }
public int Index { get; }
public SharedLayoutStackPanel Panel { get; set; }
public bool IsMeasureValid
=> !(Panel == null || !Panel.IsMeasureValid || Panel.IsMeasureMeaningless);
internal bool CanMeasure(Action invalidateMeasure)
{
if (Coordinator.GetPreceedingRegions(this).All(pr => pr.IsMeasureValid))
{
return true;
}
this.InvalidateMeasureCallback = invalidateMeasure;
return false;
}
public int StartOfRegion => Coordinator.GetPreviousRegion(this)?.EndOfRegion ?? 0;
public int CountInRegion { get; set; }
public int EndOfRegion => CountInRegion + StartOfRegion;
public bool HasNextRegion => Coordinator.GetNextRegion(this) != null;
internal void OnMeasure()
{
var nextRegion = Coordinator.GetNextRegion(this);
if (nextRegion != null && nextRegion.InvalidateMeasureCallback != null)
{
nextRegion.InvalidateMeasureCallback();
nextRegion.InvalidateMeasureCallback = null;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
namespace MultiRegionListBox
{
class SharedLayoutStackPanel : Panel, IScrollInfo
{
internal const double _scrollLineDelta = 16.0;
public void LineUp() => SetVerticalOffset(VerticalOffset - _scrollLineDelta);
public void LineDown() => SetVerticalOffset(VerticalOffset + _scrollLineDelta);
public void LineLeft() => SetHorizontalOffset(HorizontalOffset - 1.0);
public void LineRight() => SetHorizontalOffset(HorizontalOffset + 1.0);
public void PageUp() => SetVerticalOffset(VerticalOffset - ViewportHeight);
public void PageDown() => SetVerticalOffset(VerticalOffset + ViewportHeight);
public void PageLeft() => SetHorizontalOffset(HorizontalOffset - ViewportWidth);
public void PageRight() => SetHorizontalOffset(HorizontalOffset + ViewportWidth);
public void MouseWheelUp() => SetVerticalOffset(VerticalOffset - SystemParameters.WheelScrollLines * _scrollLineDelta);
public void MouseWheelDown() => SetVerticalOffset(VerticalOffset + SystemParameters.WheelScrollLines * _scrollLineDelta);
public void MouseWheelLeft() => SetHorizontalOffset(HorizontalOffset - 3.0 * _scrollLineDelta);
public void MouseWheelRight() => SetHorizontalOffset(HorizontalOffset + 3.0 * _scrollLineDelta);
public double ExtentWidth => Extent.Width;
public double ExtentHeight => Extent.Height;
public double ViewportWidth => Viewport.Width;
public double ViewportHeight => Viewport.Height;
public double HorizontalOffset => ComputedOffset.X;
public double VerticalOffset => ComputedOffset.Y;
public void SetHorizontalOffset(double offset)
{
if (double.IsNaN(offset))
{
throw new ArgumentOutOfRangeException();
}
if (offset < 0d)
{
offset = 0d;
}
if (offset != Offset.X)
{
Offset.X = offset;
InvalidateMeasure();
}
}
/// <summary>
/// Set the VerticalOffset to the passed value.
/// </summary>
public void SetVerticalOffset(double offset)
{
if (double.IsNaN(offset))
{
throw new ArgumentOutOfRangeException();
}
if (offset < 0d)
{
offset = 0d;
}
if (offset != Offset.Y)
{
Offset.Y = offset;
InvalidateMeasure();
}
}
public ScrollViewer ScrollOwner
{
get { return _scrollOwner; }
set
{
if (value == _scrollOwner)
{
return;
}
InvalidateMeasure();
Offset = new Vector();
Viewport = Extent = new Size();
_scrollOwner = value;
}
}
public bool CanVerticallyScroll
{
get { return true; }
set { /* noop */ }
}
public bool CanHorizontallyScroll
{
get { return false; }
set { /* noop */ }
}
internal bool IsMeasureMeaningless { get; private set; }
protected override void OnVisualParentChanged(DependencyObject oldParent)
{
base.OnVisualParentChanged(oldParent);
this.SLC = SharedLayoutCoordinator.GetParentSharedLayoutController(this);
if (SLC != null)
{
this.SLC.Panel = this;
}
InvalidateMeasure();
}
protected override Size MeasureOverride(Size viewportSize)
{
if (SLC == null || !SLC.CanMeasure(InvalidateMeasure))
{
IsMeasureMeaningless = true;
return viewportSize;
}
IsMeasureMeaningless = false;
var extent = new Size();
var countInRegion = 0; var hasNextRegion = SLC.HasNextRegion;
foreach (var child in InternalChildren.Cast<UIElement>().Skip(SLC.StartOfRegion))
{
child.Measure(new Size(viewportSize.Width, double.PositiveInfinity));
var childDesiredSize = child.DesiredSize;
if (hasNextRegion && extent.Height + childDesiredSize.Height > viewportSize.Height)
{
break;
}
extent.Width = Math.Max(extent.Width, childDesiredSize.Width);
extent.Height += childDesiredSize.Height;
SLC.CountInRegion = countInRegion += 1;
}
// Update ISI
this.Extent = extent;
this.Viewport = viewportSize;
this.ComputedOffset.Y = Bound(Offset.Y, 0, extent.Height - viewportSize.Height);
this.OnScrollChange();
SLC.OnMeasure();
return new Size(
Math.Min(extent.Width, viewportSize.Width),
Math.Min(extent.Height, viewportSize.Height));
}
private static double Bound(double c, double min, double max)
=> Math.Min(Math.Max(c, Math.Min(min, max)), Math.Max(min, max));
protected override Size ArrangeOverride(Size arrangeSize)
{
if (IsMeasureMeaningless)
{
return arrangeSize;
}
double cy = -ComputedOffset.Y;
int i = 0, i_start = SLC.StartOfRegion, i_end = SLC.EndOfRegion;
foreach (UIElement child in InternalChildren)
{
if (i >= i_start && i < i_end)
{
child.Arrange(new Rect(0, cy, Math.Max(child.DesiredSize.Width, arrangeSize.Width), child.DesiredSize.Height));
cy += child.DesiredSize.Height;
}
else if (child.RenderSize != new Size())
{
child.Arrange(new Rect());
}
i += 1;
}
return arrangeSize;
}
private void OnScrollChange() => ScrollOwner?.InvalidateScrollInfo();
public Rect MakeVisible(Visual visual, Rect rectangle)
{
// no-op
return rectangle;
}
internal ScrollViewer _scrollOwner;
internal Vector Offset;
private Size Viewport;
private Size Extent;
private Vector ComputedOffset;
private SharedLayoutRegion SLC;
}
}
<ListBox ItemsSource="{Binding mySource}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActualHeight}" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem IsChecked="{Binding checked}">
<CheckBox IsChecked="{Binding checked}" Content="{Binding Name}" />
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>