2010-08-25 8 views
22

Ho visto alcuni articoli che mostrano come utilizzare AlternationIndex con ListBox o ListView s, ma ho trascorso alcune ore cercando di ottenere colori di sfondo alternati sulla classe ItemsControl di base e nulla sembra funzionare. Tutti ListBox campioni che ho visto usare ListBoxItem come il tipo di destinazione per lo stile che imposta lo sfondo in base AlternationIndex - come questo da MSDN:Come utilizzare AlternationIndex in ItemsControls?

<Grid> 
    <Grid.Resources> 
     <Style x:Key="alternatingWithTriggers" TargetType="{x:Type ListBoxItem}"> 
      <Setter Property="Background" Value="Blue"/> 
      <Setter Property="Foreground" Value="White"/> 
      <Style.Triggers> 
       <Trigger Property="ListBox.AlternationIndex" Value="1"> 
        <Setter Property="Background" Value="CornflowerBlue"/> 
        <Setter Property="Foreground" Value="Black"/> 
       </Trigger> 
       <Trigger Property="ListBox.AlternationIndex" Value="2"> 
        <Setter Property="Background" Value="LightBlue"/> 
        <Setter Property="Foreground" Value="Navy"/> 
       </Trigger> 
      </Style.Triggers> 
     </Style> 

    </Grid.Resources> 
    <ListBox AlternationCount="3" ItemsSource="{StaticResource data}" 
      ItemContainerStyle="{StaticResource alternatingWithTriggers}"> 
    </ListBox> 
</Grid> 

voglio usare il ItemsControl perché non voglio che la funzionalità di selezione e Penso che il restyling di ListBox per nasconderlo potrebbe non essere la scelta migliore.

Questa è una delle cose che stavo cercando:

<DataTemplate DataType="{x:Type vm:ObservableCollectionItem}"> 
    <Grid> 
     <!-- some content here --> 
    </Grid> 
</DataTemplate> 

<!-- ... --> 

<ItemsControl 
    ItemsSource="{Binding ObservableCollectionItems}" 
    AlternationCount="2" 
> 
    <ItemsControl.ItemContainerStyle> 
     <Style> 
      <Style.Triggers> 
       <Trigger Property="ItemsControl.AlternationIndex" Value="0"> 
        <Setter Property="Grid.Background" Value="Red"></Setter> 
       </Trigger> 
       <Trigger Property="ItemsControl.AlternationIndex" Value="1"> 
        <Setter Property="Grid.Background" Value="Blue"></Setter> 
       </Trigger> 
      </Style.Triggers> 
     </Style> 
    </ItemsControl.ItemContainerStyle> 
</ItemsControl> 

Il problema che ho visto era che l'albero visivo ha una lista di ContentPresenter s che hanno ItemsControl.AlternationIndex alternano tra 0 e 1, ma il Grid in ogni ContentPresenter ha ItemsControl.AlternationIndex impostato a 0.

probabilmente c'è qualcosa ovvio che mi manca ...

risposta

38

L'ItemContainerStyle viene applicato agli elementi generati da the ItemsControl: ContentPresenter. Il ContentPresenter a sua volta contiene qualsiasi cosa tu abbia inserito nel tuo ItemTemplate. Nel caso di un ListBox, ItemContainerStyle viene applicato al ListBoxItem generato.

L'AlternationCount è, in base a ciò che è stato pubblicato, disponibile solo su questi articoli generati. Non è possibile utilizzare ItemContainerStyle per impostare lo sfondo della griglia, poiché la griglia è sconosciuta per tale stile.

Quanto segue sarebbe l'ideale, ma sfortunatamente ContentPresenter non ha proprietà di background. Funzionerebbe comunque per un ListBox (con ListBoxItems).

<ItemsControl 
    ItemsSource="{Binding ObservableCollectionItems}" 
    AlternationCount="2"> 
    <ItemsControl.ItemContainerStyle> 
     <Style TargetType="ContentPresenter"> 
      <Style.Triggers> 
       <Trigger Property="ItemsControl.AlternationIndex" Value="0"> 
        <Setter Property="Background" Value="Red"></Setter> 
       </Trigger> 
       <Trigger Property="ItemsControl.AlternationIndex" Value="1"> 
        <Setter Property="Background" Value="Blue"></Setter> 
       </Trigger> 
      </Style.Triggers> 
     </Style> 
    </ItemsControl.ItemContainerStyle> 
</ItemsControl> 

così si finisce per scrivere uno stile per la griglia, che si lega al AlternationIndex del vostro ContentPresenter genitore.

<DataTemplate DataType="{x:Type vm:ObservableCollectionItem}"> 
    <Grid> 
     <Grid.Style> 
      <Style TargetType="Grid"> 
       <Style.Triggers> 
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}, Path=(ItemsControl.AlternationIndex)}" Value="0"> 
         <Setter Property="Background" Value="Red"/> 
        </DataTrigger> 
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}, Path=(ItemsControl.AlternationIndex)}" Value="1"> 
         <Setter Property="Background" Value="Blue"/> 
        </DataTrigger> 
       </Style.Triggers> 
      </Style> 
     </Grid.Style> 
    </Grid> 
</DataTemplate> 
+0

Ha funzionato! Grazie! Mi chiedo se questa associazione di ricerca si ridimensionerà su grandi ItemControls, ma fortunatamente il mio non è il caso. –

+1

Il secondo esempio funziona, il primo no, poiché ContentPresenter * non * ha una proprietà 'Background'. – Will

+8

Come ho detto subito prima dell'esempio;) – Bubblewrap

24

hm .. Dopo circa 2 ore a giocare in giro, ho finalmente trovato la soluzione che funziona semplicemente:

 <ItemsControl ItemsSource="{Binding}" AlternationCount="2"> 
       <ItemsControl.ItemTemplate> 
        <DataTemplate> 
         <Grid Background="Transparent" x:Name="__PART_GRID"></Grid> 
         <DataTemplate.Triggers> 
          <Trigger Property="ItemsControl.AlternationIndex" Value="0"> 
           <Setter TargetName="__PART_GRID" Property="Background" Value="Red"/> 
          </Trigger> 
          <Trigger Property="ItemsControl.AlternationIndex" Value="1"> 
           <Setter TargetName="__PART_GRID" Property="Background" Value="Blue"/> 
          </Trigger> 
         </DataTemplate.Triggers> 
        </DataTemplate> 
       </ItemsControl.ItemTemplate> 
     </ItemsControl> 

Spero che questa risposta aiuta gli altri a risparmiare un po 'di tempo.

+0

Questa soluzione è ** molto ** più chiara di quella suggerita da @Bubblewrap quindi ... +1! Grazie! – AxelEckenberger

+0

Sicuramente una soluzione molto più pulita, IMO. Potrebbe essere interessante notare che il componente denominato DEVE venire prima dei trigger o genererà un errore. (Tuttavia, il messaggio di errore è abbastanza chiaro su quale sia il problema) – Herohtar

0

Se non si desidera utilizzare l'approccio DataTemplate, è possibile creare un controllo personalizzato che utilizza uno ContentControl come contenitore dell'elemento, consentendo quindi di specificare un colore di sfondo.

Classe:

public class ItemsControlAlternating : ItemsControl 
{ 
    static ItemsControlAlternating() 
    { 
     DefaultStyleKeyProperty.OverrideMetadata(typeof(ItemsControlAlternating), 
       new FrameworkPropertyMetadata(typeof(ItemsControlAlternating))); 
    } 

    protected override DependencyObject GetContainerForItemOverride() 
    { 
     return new ContentControl(); 
    } 

    protected override bool IsItemItsOwnContainerOverride(object item) 
    { 
     return item is ContentControl; 
    } 
} 

dizionario risorse:

<Style TargetType="{x:Type c:ItemsControlAlternating}"> 
    <Setter Property="AlternationCount" Value="2"/> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type c:ItemsControlAlternating}"> 
       <ItemsPresenter/> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
    <Setter Property="ItemContainerStyle"> 
     <Setter.Value> 
      <Style TargetType="{x:Type ContentControl}"> 
       <Setter Property="Template"> 
        <Setter.Value> 
         <ControlTemplate TargetType="{x:Type ContentControl}"> 
          <Border Background="{TemplateBinding Background}"> 
           <ContentPresenter/> 
          </Border> 
         </ControlTemplate> 
        </Setter.Value> 
       </Setter> 
       <Style.Triggers> 
        <Trigger Property="ItemsControl.AlternationIndex" Value="0"> 
         <Setter Property="Background" Value="Gray"/> 
        </Trigger> 
        <Trigger Property="ItemsControl.AlternationIndex" Value="1"> 
         <Setter Property="Background" Value="White"/> 
        </Trigger> 
       </Style.Triggers> 
      </Style> 
     </Setter.Value> 
    </Setter> 
</Style> 
0

Io non so come nessuna delle risposte precedenti sono legittime. Non potevo far funzionare nessuno di loro (non ho provato quello di Jacobi). Ad ogni modo, ho trovato il percorso verso l'illuminazione qui: http://www.dotnetcurry.com/wpf/1211/wpf-items-control-advanced-topic, che mi ha portato ad aggiungere quanto segue in xaml.cs code-behind:

public sealed class CustomItemsControl : ItemsControl 
{ 
    protected override DependencyObject GetContainerForItemOverride() 
    { 
     return new ContentControl(); 
    } 
} 

e questo in XAML per sé

<local:CustomItemsControl AlternationCount="2" 
      ItemsSource="{Binding Cells, Mode=OneWay}"> 
     <local:CustomItemsControl.ItemContainerStyle> 
      <Style TargetType="ContentControl"> 
       <Setter Property="Template"> 
        <Setter.Value> 
         <ControlTemplate TargetType="ContentControl"> 
          <Border Background="{TemplateBinding Background}"> 
           <ContentPresenter/> 
          </Border> 
         </ControlTemplate> 
        </Setter.Value> 
       </Setter> 

       <Style.Triggers> 
        <Trigger Property="ItemsControl.AlternationIndex" Value="0"> 
         <Setter Property="Background" Value="WhiteSmoke"/> 
        </Trigger> 
        <Trigger Property="ItemsControl.AlternationIndex" Value="1"> 
         <Setter Property="Background" Value="LightGray"/> 
        </Trigger> 
       </Style.Triggers> 
      </Style> 
     </local:CustomItemsControl.ItemContainerStyle> 
    </local:CustomItemsControl> 

Questo è stato così dannatamente difficile trovare una soluzione di lavoro a che io sono in realtà arrabbiato

1

Oppure, come ho trovato su un altro post, e funziona benissimo per me ... è semplicemente possibile utilizzare un binding ...

{Binding 
    RelativeSource={RelativeSource Mode=TemplatedParent}, 
    Path=(ItemsControl.AlternationIndex)} 

NB: ricordati di aggiungere AlterationCount = "100" sul tuo ItemsControl