Вопрос об упаковке ItemsControl - PullRequest
0 голосов
/ 31 марта 2019

Я сделал небольшой пользовательский 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 ]

1 голос
/ 01 апреля 2019

У меня есть простое решение с 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

1 голос
/ 01 апреля 2019

Ваш разделитель идет впереди в следующей строке, потому что он находится перед LinkId TextBox, и оба они связаны друг с другом внутри WrapPanel. Вся WrapPanel переходит на следующую строку.

Родительская панель над WrapPanels не знает и не заботится о том, что внутри них.

Example showing the approximate Layout

Кстати, я не знаю, было ли это вашим намерением, но в написанном вами коде вы генерируете разные WrapPanel для каждого элемента в вашей коллекции. В вашем списке тестов 20 WrapPanels.

Если, как вы говорите, разделитель после последнего не является проблемой, это достаточно хорошее решение:

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

Если это только для целей отображения данных, учтите, что эти тексбоксы в строке могут быть одной строкой внутри TextBox. Возможно, вы могли бы использовать одну отформатированную строку и превратить соответствующие части в ссылки. Это переместит часть действия с разделителем строк в ViewModel и значительно упростит ваш код XAML.

0 голосов
/ 01 апреля 2019

Вы можете попробовать поставить разделитель после текста и скрыть последний 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>

Это должно сохранитьразделитель и текст в одной строке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...