c# - ¿Cómo se puede agregar un separador entre elementos en un ItemsControl?
listbox itemtemplate wpf (5)
Para una solución más general compatible con Silverlight, obtuve un control de ItemsControl (SeperatedItemsControl). Cada elemento está envuelto en un SeperatedItemsControlItem, al igual que ListBoxItem de ListBox. La plantilla para SeperatedItemsControlItem contiene un separador y un ContentPresenter. El separador del primer elemento de la colección está oculto. Puede modificar fácilmente esta solución para hacer un separador de barra horizontal entre elementos, que es para lo que lo he creado.
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 # Code:
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;
}
}
}
}
}
Necesito mostrar una lista de números de una colección en un Control de artículos. Entonces los artículos son: "1", "2", "3"
.
Cuando se procesan, los necesito separados por una coma (o algo similar). Por lo tanto, los 3 elementos anteriores se verían así: "1, 2, 3"
.
¿Cómo puedo agregar un separador a los elementos individuales, sin tener uno tachado al final de la lista?
No estoy atascado en el uso de ItemsControl, pero eso es lo que comencé a usar.
Pensé que debería dar la solución con la que terminé.
Terminé vinculando mi colección de elementos al texto de un bloque de texto y utilizando un convertidor de valor para cambiar la colección de elementos enlazados en la cadena formateada.
También puede realizar la multibusión en ItemsControl.AlternationIndex y ItemsControl.Count y comparar el AlternationIndex a Count para ver si es el último elemento.
Establezca AlternationIndex lo suficientemente alto como para acomodar todos sus elementos y luego realice un LastItemConverter con un método Convert que se parezca a esto:
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;
}
La respuesta aceptada actual me dio un error de enlace xaml para cada plantilla, lo que me preocupaba podría estar afectando el rendimiento. En cambio, hice lo siguiente, usando el AlternationIndex para ocultar el primer separador. (Inspirado por esta respuesta )
<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>
<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>
Llegué a su pregunta porque estaba buscando una solución en Silverlight, que no tiene una fuente de datos relativa anterior.