web-dev-qa-db-ja.com

ItemsControlの項目間にセパレーターを追加する方法

コレクションの番号のリストをItemsコントロールに表示する必要があります。したがって、アイテムは"1", "2", "3"です。

それらがレンダリングされるとき、私はそれらをコンマ(または同様の何か)で区切る必要があります。したがって、上記の3つのアイテムは"1, 2, 3"のようになります。

リストの最後にセパレータを付けずに、個々のアイテムにセパレータを追加するにはどうすればよいですか?

ItemsControlの使用にこだわっていませんが、それが私が使い始めたものです。

60
Nathan
<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のソリューションを探していたので、あなたの質問に行きました。

105
Kent Boogaart

現在受け入れられている回答 は、すべてのテンプレートに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>
20
Chris

より一般的なSilverlight互換のソリューションとして、ItemsControl(SeperatedItemsControl)からコントロールを派生させました。各項目は、ListBoxのListBoxItemと同じようにSeperatedItemsControlItemでラップされます。 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;
                }
            }
        }
    }
}
5
foson

ItemsControl.AlternationIndexおよびItemsControl.Countにマルチバインドし、AlternationIndexをCountと比較して、最後のアイテムかどうかを確認することもできます。

すべてのアイテムに対応できるようにAlternationIndexを十分に高く設定してから、次のようなConvertメソッドでLastItemConverterを作成します。

    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;
    }
3
Mo0gles

最終的に解決策を提供する必要があると考えました。

結局、項目のコレクションをTextBlockのTextにバインドし、値コンバーターを使用して、バインドされた項目のコレクションをフォーマットされた文字列に変更しました。

1
Nathan