2010-03-24 9 views
53

Ho bisogno di visualizzare un elenco di numeri da una raccolta in un controllo elementi. Quindi gli elementi sono: "1", "2", "3".Come si può aggiungere un separatore tra gli elementi in un ItemsControl

Quando sono renderizzati, ho bisogno di loro separati da una virgola (o qualcosa di simile). Quindi le 3 voci precedenti dovrebbero assomigliare a questo: "1, 2, 3".

Come posso aggiungere un separatore alle singole voci, senza averne una virata alla fine della lista?

Non sono bloccato sull'utilizzo di ItemsControl, ma è quello che avevo iniziato a utilizzare.

risposta

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

sono arrivato alla tua domanda, perché ero alla ricerca di una soluzione in Silverlight, che non ha una fonte di dati precedenti relativi.

+1

@foson: non ho mai trovato uno. Ho finito per usare un margine negativo per "tagliare" il trailing "," nel testo. Sembrava sporco, ma ha funzionato. –

+0

Qualsiasi soluzione alternativa per 'PreviousData' in Silverlight? – Shimmy

+0

E riguardo l'uso dell'indice dell'articolo e il confronto con lo zero? – MikeKulls

1

Ho pensato che avrei dovuto dare la soluzione con cui ho finito.

Ho finito per legare la mia collezione di elementi al Testo di un TextBlock e usare un convertitore di valori per cambiare la collezione vincolata di elementi nella stringa formattata.

3

È anche possibile eseguire il multibind a ItemsControl.AlternationIndex e ItemsControl.Count e confrontare l'AlternationIndex in Count per vedere se si è l'ultimo elemento.

Impostare l'AlternationIndex abbastanza alto per accogliere tutti gli oggetti poi fare un LastItemConverter con un metodo Convert cercando qualcosa di simile:

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; 
    } 
+0

Uno può posizionare il divisore prima del contenuto di un elemento nel modello. Quindi è sufficiente associare a ** AlternationIndex ** solo e nascondere il divisore nel primo elemento (AlternationIndex == 0). – baSSiLL

4

Per una soluzione Silverlight compatibile più generalizzata, ho derivato un controllo da ItemsControl (SeperatedItemsControl). Ogni elemento è racchiuso in un SeperatedItemsControlItem, proprio come ListBoxItem di ListBox. Il modello per SeperatedItemsControlItem contiene un separatore e un ContentPresenter. Il separatore per il primo elemento nella raccolta è nascosto. È possibile modificare facilmente questa soluzione per creare un separatore di barre orizzontali tra gli elementi, che è ciò per cui l'ho creato.

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# Codice:

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; 
       } 
      } 
     } 
    } 
} 
14

The current accepted answer mi ha dato un errore di XAML vincolante per ogni template, che ero preoccupato potrebbe essere compromettere le prestazioni. Invece, ho fatto il seguito, usando l'AlternationIndex per nascondere il primo separatore. (Ispirato da this answer.)

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

Divertente. Sono tornato qui dopo aver riscontrato problemi con le virgole dopo che il conteggio delle liste cambiava usando la risposta accettata. La tua soluzione ha risolto il mio problema, grazie! –

+1

Great - grazie a @Carter !! – Chris

+0

Per me funziona senza punto su questa linea invece di luka

Problemi correlati