我做了一些小的自定义ItemsControl,以将某些列表显示为项目行。而且效果几乎完美。
<ItemsControl.ItemTemplate>
<DataTemplate>
<WrapPanel Orientation="Horizontal">
<TextBlock x:Name="delimiter" Text=";" Margin="0 0 5 0"/>
<TextBlock Text="{Binding LinkId}" />
</WrapPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource PreviousData}}" Value="{x:Null}">
<Setter Property="Visibility" TargetName="delimiter" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
我的问题出在定界符中:当ItemsConteol包装到下一行时,定界符char进入下一行的开始。
我了解问题出在哪里,但我不知道如何解决。
感谢您的咨询。
答案 0 :(得分:1)
您的定界符在下一行的最前面,因为它在LinkId TextBox的前面,并且两者都在WrapPanel中绑定在一起。整个WrapPanel流到下一行。
WrapPanel上方的父面板不知道,也不在乎其中的内容。
顺便说一句,我不知道这是否是您的意图,但是在编写代码时,您会为集合中的每个项目生成一个不同的WrapPanel。就目前而言,您的测试列表中有20个WrapPanel。
如果您说的最后一个定界符没问题,那么这是一个足够好的解决方案:
<ItemsControl
ItemsSource="{Binding YourItems}"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel
Orientation="Horizontal"
></WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel
Orientation="Horizontal"
>
<TextBlock
Text="{Binding LinkId}"
></TextBlock>
<TextBlock
Text="; "
></TextBlock>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
如果这仅是出于数据显示目的,请考虑这些行中的那些texbox可能是TextBox内部的单个String。也许您可以使用单个格式化的String并将相关部分制作为Links。这会将动作的字符串定界符部分移至ViewModel,并简化了很多XAML代码。
答案 1 :(得分:1)
我有一个DataTemplateSelector
的简单解决方案。它是这样的:
您定义一个选择器,以区分最后一个项目。
class LastElementSelector : DataTemplateSelector
{
public DataTemplate NormalTemplate { get; set; }
public DataTemplate LastTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
return IsLast(container) ? LastTemplate : NormalTemplate;
}
bool IsLast(DependencyObject container)
{
var itemsControl = FindParentOrSelf<ItemsControl>(container);
var idx = itemsControl.ItemContainerGenerator.IndexFromContainer(container);
var count = itemsControl.Items.Count;
return idx == count - 1;
}
T FindParentOrSelf<T>(DependencyObject from) where T : DependencyObject
{
for (var curr = from; curr != null; curr = VisualTreeHelper.GetParent(curr))
if (curr is T t)
return t;
return null;
}
}
有了这个,您可以为普通物品和最后一件物品使用不同的模板:
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplateSelector>
<local:LastElementSelector>
<local:LastElementSelector.NormalTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding Mode=OneWay}"/><!-- no space between runs
--><Run Text="; " Foreground="Red" FontWeight="Bold"/>
</TextBlock>
</DataTemplate>
</local:LastElementSelector.NormalTemplate>
<local:LastElementSelector.LastTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</local:LastElementSelector.LastTemplate>
</local:LastElementSelector>
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
结果:
答案 2 :(得分:0)
您可以尝试将定界符放在之后,并通过将其Run
属性设置为空字符串来隐藏最后一个Text
元素:
<ItemsControl AlternationCount="{Binding RelativeSource={RelativeSource Self}, Path=Items.Count}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding Path=., Mode=OneWay}" /><Run x:Name="delimiter" Text="; "/>
</TextBlock>
<DataTemplate.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Text" TargetName="delimiter" Value=""/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
这应该使分隔符和文本保持在同一行。