Как добавить разделитель между элементами в ItemsControl - PullRequest
55 голосов
/ 24 марта 2010

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

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

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

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

Ответы [ 5 ]

104 голосов
/ 28 июля 2010
<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, у которого нет предыдущего источника данных.

19 голосов
/ 07 декабря 2015

Текущий принятый ответ дал мне ошибку привязки 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>
5 голосов
/ 12 ноября 2011

Для более обобщенного 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>

C # код:

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;
                }
            }
        }
    }
}
3 голосов
/ 21 октября 2010

Вы также можете выполнить многократное связывание с 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;
    }
1 голос
/ 25 марта 2010

Я подумал, что должен дать решение, с которым я столкнулся.

Я закончил тем, что привязал свою коллекцию элементов к тексту текстового блока и использовал преобразователь значений, чтобы изменить связанную коллекцию элементов в форматированную строку.

...