我有一个包含父项和几个孩子的TreeView。孩子们又包括一个带有自己孩子的WrapPanel(“一个”,“两个”,“三个”等)。当父窗口不足以容纳它们时,如何将这些最后的元素包裹起来?
这是我的代码:
<TreeView ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<TreeViewItem Header="Parent" IsExpanded="True" >
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Items>
<TextBlock Text="One" />
<TextBlock Text="Two" />
<TextBlock Text="Three" />
<TextBlock Text="Four" />
<TextBlock Text="Five" />
<TextBlock Text="Six" />
<TextBlock Text="Seven" />
<TextBlock Text="Eight" />
<TextBlock Text="Nine" />
<TextBlock Text="Ten" />
</ItemsControl.Items>
</ItemsControl>
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Items>
<TextBlock Text="One" />
<TextBlock Text="Two" />
<TextBlock Text="Three" />
<TextBlock Text="Four" />
<TextBlock Text="Five" />
<TextBlock Text="Six" />
<TextBlock Text="Seven" />
<TextBlock Text="Eight" />
<TextBlock Text="Nine" />
<TextBlock Text="Ten" />
</ItemsControl.Items>
</ItemsControl>
</TreeViewItem>
</TreeView>
这就是它目前产生的东西:
答案 0 :(得分:1)
请测试此解决方法。您必须将SizeChanged="TreeView_SizeChanged"
添加到TreeView
项目。
private void TreeView_SizeChanged(object sender, SizeChangedEventArgs e)
{
var treeView = (TreeView) sender;
foreach (TreeViewItem treeViewItem in treeView.Items)
{
foreach (ItemsControl ic in treeViewItem.Items)
{
Point relativeLocation = ic.TranslatePoint(new Point(0, 0), treeView);
var wpMaxWidth = treeView.ActualWidth - relativeLocation.X;
WrapPanel itemsWp = GetVisualChild<WrapPanel>(ic);
itemsWp.MaxWidth = wpMaxWidth;
}
}
}
private static T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual) VisualTreeHelper.GetChild(parent, i);
var child = v as T;
return child ?? GetVisualChild<T>(v);
}
return null;
}
解决方案2:使用MultiBinding
和IMultiValueConverter
。
...
<WrapPanel.MaxWidth>
<MultiBinding Converter="{StaticResource Converter1}">
<MultiBinding.Bindings>
<Binding RelativeSource="{RelativeSource Self}"/>
<Binding RelativeSource="{RelativeSource Mode=FindAncestor,AncestorType=TreeView}"/>
</MultiBinding.Bindings>
</MultiBinding>
</WrapPanel.MaxWidth>
...
IMultiValueConverter
Convert
方法实施:
...
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var wp = values[0] as WrapPanel;
var tv = values[1] as TreeView;
if (wp == null || tv == null)
return 0d;
Point relativeLocation = wp.TranslatePoint(new Point(0, 0), tv);
var wpMaxWidth = tv.ActualWidth - relativeLocation.X;
return wpMaxWidth;
}
...
注意:您必须在更改容器大小后刷新Binding
。
答案 1 :(得分:1)
我修改了 dbvega's 解决方案 2 以考虑垂直滚动条宽度和垂直滚动条可见性。还添加了对树视图宽度变化的重新计算。 XAML:
Widget time(String value) {
Widget w;
if (value == "مرة واحدة في اليوم"){
w = SizedBox(
height: 1,
);}
else if (value == 'مرتان في اليوم') {
w = AddContainer(
text: DateFormat('hh:mm').format(updatedTime2), // ("وقت الدواء"),
onPressed: () {
showModalBottomSheet(
context: context, builder: buildShowModalBottomSheetMedTime2);
});
} else if (value == "ثلاث مرات في اليوم") {
w = Column(
children: [
AddContainer(
text: DateFormat('hh:mm').format(updatedTime2), // ("وقت الدواء"),
onPressed: () {
showModalBottomSheet(
context: context,
builder: buildShowModalBottomSheetMedTime2);
}),
SizedBox(height: 20),
AddContainer(
text: DateFormat('hh:mm').format(updatedTime3), // ("وقت الدواء"),
onPressed: () {
showModalBottomSheet(
context: context,
builder: buildShowModalBottomSheetMedTime3);
}),
SizedBox(height: 20),
],
);
} else if (value == "اربعة مرات في اليوم") {
w = Column(
children: [
AddContainer(
text: DateFormat('hh:mm').format(updatedTime2), // ("وقت الدواء"),
onPressed: () {
showModalBottomSheet(
context: context,
builder: buildShowModalBottomSheetMedTime2);
}),
SizedBox(height: 20),
AddContainer(
text: DateFormat('hh:mm').format(updatedTime3), // ("وقت الدواء"),
onPressed: () {
showModalBottomSheet(
context: context,
builder: buildShowModalBottomSheetMedTime3);
}),
SizedBox(height: 20),
AddContainer(
text: DateFormat('hh:mm').format(updatedTime4), // ("وقت الدواء"),
onPressed: () {
showModalBottomSheet(
context: context,
builder: buildShowModalBottomSheetMedTime4);
}),
SizedBox(height: 20),
],
);
}
return w;
}
转换器:
...
<WrapPanel.Width>
<MultiBinding Converter="{StaticResource Converter1}">
<MultiBinding.Bindings>
<Binding RelativeSource="{RelativeSource Self}"/>
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ScrollViewer}"/>
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ScrollViewer}" Path="ViewportWidth"/>
</MultiBinding.Bindings>
</MultiBinding>
</WrapPanel.Width>
...
答案 2 :(得分:0)
我问这个问题已经有几年了,在此期间我学到了很多关于 WPF 布局的知识。事实上,这可以单独在 XAML 中完成,我现在将其发布给将来遇到此问题的任何人。关键是模板化 TreeViewItem 并检查它用于填充树的每个级别的 XAML。
简而言之,TreeView 的每一层都由一个包含两行三列的网格组成。第一行用于 TreeViewItem 标题,而第二行用于子项。
还有三列。第 0 列用于扩展图标,第 1 列用于标题内容,第 2 列用于所有剩余空间。孩子们都坐在一个 ContentPresenter 中,它占据第二行的第 1 列和第 2 列。此网格的列定义揭示了一些有趣的事情:
<ColumnDefinition MinWidth="19" Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
扩展器图标默认为 16x16,但第 0 列的 MinWidth 设置为 19 像素。因此,除非您更改扩展器图标,否则 19 像素将是 ContentPresenter 开头的缩进。因此,您需要做的就是将每个子项 ItemsControl 绑定到其 TreeViewItem 的宽度,并在 右侧-hand 侧给它一个 19 像素的填充(或任何扩展器图标的宽度) :
<ItemsControl Width="{Binding RelativeSource={RelativeSource AncestorType= {x:Type TreeViewItem}, AncestorLevel=1}, Path=ActualWidth}" Padding="0 0 19 0">
结果是像素完美的包装:
(请注意,此解决方案还将适应滚动条的出现和消失,并且会在出现和消失时相应地重新排列 WrapPanel 项)。