Подтвердить что ты не робот

Как можно добавить разделитель между элементами в ItemsControl

Мне нужно отобразить список чисел из коллекции в элементе управления. Таким образом, элементы: "1", "2", "3".

Когда они отображаются, я нуждаюсь в них, разделенных запятой (или чем-то подобным). Таким образом, вышеуказанные 3 элемента будут выглядеть так: "1, 2, 3".

Как добавить разделитель к отдельным элементам без привязки к концу списка?

Я не зацикливаюсь на использовании ItemsControl, но это то, что я начал использовать.

4b9b3361

Ответ 1

<ItemsControl ItemsSource="{Binding Numbers}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <!-- could use a WrapPanel if more appropriate for your scenario -->
            <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock x:Name="commaTextBlock" Text=", "/>
                <TextBlock Text="{Binding .}"/>
            </StackPanel>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding RelativeSource={RelativeSource PreviousData}}" Value="{x:Null}">
                    <Setter Property="Visibility" TargetName="commaTextBlock" Value="Collapsed"/>
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>

    </ItemsControl.ItemTemplate>
</ItemsControl>

Я пришел к вашему вопросу, потому что искал решение в Silverlight, у которого нет предыдущего относительного источника данных.

Ответ 2

Текущий принятый ответ дал мне ошибку привязки xaml для каждого шаблона, что, я думаю, может повлиять на производительность. Вместо этого я сделал следующее, используя AlternationIndex, чтобы скрыть первый разделитель. (Вдохновленный этот ответ.)

<ItemsControl ItemsSource="{Binding Numbers}" AlternationCount="{Binding RelativeSource={RelativeSource Self}, Path=Items.Count}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock x:Name="SeparatorTextBlock" Text=", "/>
                <TextBlock Text="{Binding .}"/>
            </StackPanel>
        <DataTemplate.Triggers>
            <Trigger Property="ItemsControl.AlternationIndex" Value="0">
                <Setter Property="Visibility" TargetName="SeparatorTextBlock" Value="Collapsed" />
            </Trigger>
         </DataTemplate.Triggers>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Ответ 3

Для более обобщенного решения, совместимого с Silverlight, я получил элемент управления из ItemsControl (SeperatedItemsControl). Каждый элемент завернут в элемент SeperatedItemsControlItem, как и ListBox ListBoxItem. Шаблон для SeperatedItemsControlItem содержит разделитель и ContentPresenter. Сепаратор для первого элемента коллекции скрыт. Вы можете легко изменить это решение, чтобы сделать горизонтальный разделитель строк между элементами, для чего я его создал.

MainWindow.xaml:

<Window x:Class="ItemsControlWithSeperator.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:local="clr-namespace:ItemsControlWithSeperator"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
    <local:ViewModel x:Key="vm" />

</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource vm}">

    <local:SeperatedItemsControl ItemsSource="{Binding Data}">
        <local:SeperatedItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal" />
            </ItemsPanelTemplate>
        </local:SeperatedItemsControl.ItemsPanel>
        <local:SeperatedItemsControl.ItemContainerStyle>
            <Style TargetType="local:SeperatedItemsControlItem">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="local:SeperatedItemsControlItem" >
                            <StackPanel Orientation="Horizontal">
                                <TextBlock x:Name="seperator">,</TextBlock>
                                <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}"/>
                            </StackPanel>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </local:SeperatedItemsControl.ItemContainerStyle>
    </local:SeperatedItemsControl>
</Grid>

Код С#:

using System;
using System.Windows;
using System.Windows.Controls;

namespace ItemsControlWithSeperator
{

    public class ViewModel
    {
        public string[] Data { get { return new[] { "Amy", "Bob", "Charlie" }; } }
    }

    public class SeperatedItemsControl : ItemsControl
    {

        public Style ItemContainerStyle
        {
            get { return (Style)base.GetValue(SeperatedItemsControl.ItemContainerStyleProperty); }
            set { base.SetValue(SeperatedItemsControl.ItemContainerStyleProperty, value); }
        }

        public static readonly DependencyProperty ItemContainerStyleProperty =
            DependencyProperty.Register("ItemContainerStyle", typeof(Style), typeof(SeperatedItemsControl), null);

        protected override DependencyObject GetContainerForItemOverride()
        {
            return new SeperatedItemsControlItem();
        }
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return item is SeperatedItemsControlItem;
        }
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            //begin code copied from ListBox class

            if (object.ReferenceEquals(element, item))
            {
                return;
            }

            ContentPresenter contentPresenter = element as ContentPresenter;
            ContentControl contentControl = null;
            if (contentPresenter == null)
            {
                contentControl = (element as ContentControl);
                if (contentControl == null)
                {
                    return;
                }
            }
            DataTemplate contentTemplate = null;
            if (this.ItemTemplate != null && this.DisplayMemberPath != null)
            {
                throw new InvalidOperationException();
            }
            if (!(item is UIElement))
            {
                if (this.ItemTemplate != null)
                {
                    contentTemplate = this.ItemTemplate;
                }

            }
            if (contentPresenter != null)
            {
                contentPresenter.Content = item;
                contentPresenter.ContentTemplate = contentTemplate;
            }
            else
            {
                contentControl.Content = item;
                contentControl.ContentTemplate = contentTemplate;
            }

            if (ItemContainerStyle != null && contentControl.Style == null)
            {
                contentControl.Style = ItemContainerStyle;
            }

            //end code copied from ListBox class

            if (this.Items.Count > 0)
            {
                if (object.ReferenceEquals(this.Items[0], item))
                {
                    var container = element as SeperatedItemsControlItem;
                    container.IsFirstItem = true;
                }
            }
        }
        protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);
            if (Items.Count > 1)
            {
                var container = (ItemContainerGenerator.ContainerFromIndex(1) as SeperatedItemsControlItem);
                if (container != null) container.IsFirstItem = false;
            }
            if (Items.Count > 0)
            {
               var container = (ItemContainerGenerator.ContainerFromIndex(0) as SeperatedItemsControlItem);
               if (container != null) container.IsFirstItem = true;
           }
       }

    }

    public class SeperatedItemsControlItem : ContentControl
    {
        private bool isFirstItem;
        public bool IsFirstItem 
        {
            get { return isFirstItem; }
            set 
            {
                if (isFirstItem != value)
                {
                    isFirstItem = value;
                    var seperator = this.GetTemplateChild("seperator") as FrameworkElement;
                    if (seperator != null)
                    {
                        seperator.Visibility = isFirstItem ? Visibility.Collapsed : Visibility.Visible;
                    }
                }
            }
        }    
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (IsFirstItem)
            {
                var seperator = this.GetTemplateChild("seperator") as FrameworkElement;
                if (seperator != null)
                {
                    seperator.Visibility = Visibility.Collapsed;
                }
            }
        }
    }
}

Ответ 4

Вы также можете использовать multibind для ItemsControl.AlternationIndex и ItemsControl.Count и сравнивать AlternationIndex с Count, чтобы узнать, являетесь ли вы последним.

Установите AlternationIndex достаточно высоко, чтобы разместить все ваши предметы, а затем создайте LastItemConverter с помощью метода Convert, который выглядит примерно так:

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var alterationCount = (int)values[0];
        var itemCount = (int)values[1];
        if (itemCount > 1)
        {
            return alterationCount == (itemCount - 1) ? Visibility.Collapsed : Visibility.Visible;
        }

        return Visibility.Collapsed;
    }

Ответ 5

Я решил, что должен дать решение, в котором я закончил.

В итоге я привязал свою коллекцию элементов к тексту TextBlock и использовал конвертер значений для изменения связанной коллекции элементов в форматированную строку.