2010-02-06 20 views
7

Desidero visualizzare un modello/elemento personalizzato come elemento selezionato in ComboBox (questo elemento in realtà non esiste nell'elenco di elementi e viene aggiornato in modo diverso). Questo non ha nemmeno bisogno di essere un oggetto, solo fornendo una visualizzazione personalizzata funzionerebbe.WPF: Come personalizzare SelectionBoxItem in ComboBox

Come posso farlo rimanendo all'interno del tema corrente di ComboBox (quindi non è possibile la sostituzione di ControlTemplate)? Per quanto vedo, tutte le proprietà di SelectionBox * non sono modificabili e internamente ComboBox utilizza ContentPresenter senza nome.

+0

Questo è un modo per confondere i propri utenti, le persone si aspettano che una casella di controllo si comporti come una casella di controllo. –

+0

Bene, si tratta di un ComboBox con CheckBox che potrebbe non essere comune, ma non direi che questo è troppo confuso (o Evil). L'utilizzo di una finestra popup aggiuntiva sembra un overkill e questo elemento non è abbastanza importante per essere un elenco CheckBox a grandezza naturale. –

+0

Ovviamente non posso mostrare un elemento selezionato perché non ho un singolo elemento selezionato. –

risposta

18

farei così:

<Window.Resources> 

    <DataTemplate x:Key="NormalItemTemplate" ...> 
    ... 
    </DataTemplate> 

    <DataTemplate x:Key="SelectionBoxTemplate" ...> 
    ... 
    </DataTemplate> 

    <DataTemplate x:Key="CombinedTemplate"> 
    <ContentPresenter x:Name="Presenter" 
     Content="{Binding}" 
     ContentTemplate="{StaticResource NormalItemTemplate}" /> 
    <DataTemplate.Triggers> 
     <DataTrigger 
     Binding="{Binding RelativeSource={RelativeSource FindAncestor,ComboBoxItem,1}}" 
     Value="{x:Null}"> 
     <Setter TargetName="Presenter" Property="ContentTemplate" 
       Value="{StaticResource SelectionBoxTemplate}" /> 
     </DataTrigger> 
    </DataTemplate.Triggers> 
    </DataTemplate> 

</Window.Resources> 

... 

<ComboBox 
    ItemTemplate="{StaticResource CombinedTemplate}" 
    ItemsSource="..." 
    ... /> 

Il motivo per cui funziona è che CombinedTemplate normalmente solo utilizza NormalItemTemplate per presentare i dati, ma se non c'è ComboBoxItem antenato assume esso è nella confezione selezione in modo usa SelectionBoxTemplate.

Nota che i tre DataTemplates potrebbero essere inclusi in qualsiasi livello di ResourceDictionary (non solo a livello di Window) o anche direttamente all'interno del ComboBox, a seconda delle preferenze.

+0

Grazie, proverò sicuramente questo. –

+0

È sia un'elegante soluzione * che * di lavoro, grazie mille! –

+2

Tuttavia, ciò genera un'eccezione di collegamento: "Impossibile trovare l'origine per il binding con riferimento" RelativeSource FindAncestor, AncestorType = 'System.Windows.Controls.ComboBoxItem', AncestorLevel = '1'''. Penso che l'impostazione di 'ItemTemplateSelector' sia un approccio migliore. Ecco un esempio: http://social.msdn.microsoft.com/Forums/vstudio/en-US/0467c9ca-efb2-4506-96e7-08ce3356860a/combobox-one-template-for-selected-item-one-for -the-dropdown-list? forum = wpf –

-1

È necessario esaminare Triggers e Styles. Si potrebbe anche voler guardare in alcune delle mie domande più anziani qui su StackOverflow che mi ha aiutato a conquistare questi problemi:

+0

Grazie, purtroppo, era un po 'troppo astratto rispetto ad altre risposte. Ho un'idea generale di come funzionano i trigger, era la soluzione {x: Null} che non potevo inventare. –

0

Se ho questo diritto, si vuole un controllo che ha qualcosa di arbitrario visualizzato insieme a un pulsante a discesa che visualizza un elenco di elementi con caselle di controllo accanto a loro?

Non mi preoccuperei nemmeno di provare a restyling un ComboBox per raggiungere questo obiettivo. Il problema è che ComboBox è più specializzato in un percorso diverso da quello che ti serve. Se guardi lo ComboBox ControlTemplate Example, vedrai che utilizza semplicemente un controllo Popup per visualizzare l'elenco di valori possibili.

È possibile prendere pezzi di quel modello come guida per la creazione di uno UserControl che è più facile da capire e fornisce meglio quello che vuoi. Potrai persino aggiungere una proprietà SelectedItems e tale valore non è fornito da ComboBox.

Un esempio di cosa intendo per guida: il Popup ha una proprietà IsOpen. Nel modello di controllo, è impostato su {TemplateBinding IsDropDownOpen}, il che significa che la classe ComboBox ha una proprietà IsDropDownOpen modificata per controllare l'espansione/compressione dello Popup.

+0

Il problema con i controlli personalizzati è che non sono formattati dagli stili incorporati. Ho già creato un controllo personalizzato con SelectedItems, ma al suo interno si basa su un ComboBox poiché desidero che gli stili di default lavorino con esso senza doverli ripetere. –

0

commento di Alexey Mitev su Ray Burns' answer mi ha ispirato a scrivere la seguente classe di utilità ragionevolmente breve, che ora uso in tutti i miei progetti di WPF:

public class ComboBoxItemTemplateSelector : DataTemplateSelector 
{ 
    public List<DataTemplate> SelectedItemTemplates { get; } = new List<DataTemplate>(); 
    public List<DataTemplate> DropDownItemTemplates { get; } = new List<DataTemplate>(); 

    public override DataTemplate SelectTemplate(object item, DependencyObject container) 
    { 
     return GetVisualParent<ComboBoxItem>(container) == null 
      ? ChooseFrom(SelectedItemTemplates, item) 
      : ChooseFrom(DropDownItemTemplates, item); 
    } 

    private static DataTemplate ChooseFrom(IEnumerable<DataTemplate> templates, object item) 
    { 
     if (item == null) 
      return null; 
     var targetType = item.GetType(); 
     return templates.FirstOrDefault(t => (t.DataType as Type) == targetType); 
    } 

    private static T GetVisualParent<T>(DependencyObject child) where T : Visual 
    { 
     while (child != null && !(child is T)) 
      child = VisualTreeHelper.GetParent(child); 
     return child as T; 
    } 
} 

Con che nella casella degli strumenti, è possibile scrivere XAML come questo:

<UserControl.Resources> 
    <DataTemplate x:Key="SelectedItemTemplateForInt" DataType="{x:Type system:Int32}"> 
     <!-- ... --> 
    </DataTemplate> 

    <DataTemplate x:Key="SelectedItemTemplateForDouble" DataType="{x:Type system:Double}"> 
     <!-- ... --> 
    </DataTemplate> 

    <DataTemplate x:Key="DropDownItemTemplateForInt" DataType="{x:Type system:Int32}"> 
     <!-- ... --> 
    </DataTemplate> 

    <DataTemplate x:Key="DropDownItemTemplateForDouble" DataType="{x:Type system:Double}"> 
     <!-- ... --> 
    </DataTemplate> 
</UserControl.Resources> 

<ComboBox> 
    <ComboBox.ItemTemplateSelector> 
     <local:ComboBoxItemTemplateSelector> 
      <local:ComboBoxItemTemplateSelector.SelectedItemTemplates> 
       <StaticResource ResourceKey="SelectedItemTemplateForInt" /> 
       <StaticResource ResourceKey="SelectedItemTemplateForDouble" /> 
      </local:ComboBoxItemTemplateSelector.SelectedItemTemplates> 

      <local:ComboBoxItemTemplateSelector.DropDownItemTemplates> 
       <StaticResource ResourceKey="DropDownItemTemplateForInt" /> 
       <StaticResource ResourceKey="DropDownItemTemplateForDouble" /> 
      </local:ComboBoxItemTemplateSelector.DropDownItemTemplates> 
     </local:ComboBoxItemTemplateSelector> 
    </ComboBox.ItemTemplateSelector> 
</ComboBox>