ItemsControl包装问题

时间:2019-03-31 14:04:17

标签: c# wpf

我做了一些小的自定义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进入下一行的开始。

我了解问题出在哪里,但我不知道如何解决。

Result

感谢您的咨询。

3 个答案:

答案 0 :(得分:1)

您的定界符在下一行的最前面,因为它在LinkId TextBox的前面,并且两者都在WrapPanel中绑定在一起。整个WrapPanel流到下一行。

WrapPanel上方的父面板不知道,也不在乎其中的内容。

Example showing the approximate Layout

顺便说一句,我不知道这是否是您的意图,但是在编写代码时,您会为集合中的每个项目生成一个不同的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>

结果:

test run

答案 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>

这应该使分隔符和文本保持在同一行。