2010-02-06 9 views
7

Quiero mostrar una plantilla/artículo personalizado como elemento seleccionado en ComboBox (este elemento no existe en la lista de elementos y se actualiza de manera diferente). Esto ni siquiera necesita ser un elemento, solo proporcionar una vista personalizada funcionaría.WPF: Cómo personalizar SelectionBoxItem en ComboBox

¿Cómo puedo hacer esto mientras estoy dentro del tema ComboBox actual (para que no haya reemplazo de ControlTemplate)? Por lo que veo, todas las propiedades de SelectionBox * no son editables e internamente ComboBox usa ContentPresenter sin nombre.

+0

Esto es lamer para confundir a tus usuarios, la gente espera que una caja combro se comporte como una caja combro. –

+0

Bueno, es un ComboBox con CheckBoxes que podría ser poco común, pero no diría que esto es demasiado confuso (o malo). Usar una ventana emergente adicional se siente como una exageración, y este elemento simplemente no es lo suficientemente importante como para ser una lista de CheckBox de tamaño completo. –

+0

Obviamente no puedo mostrar un elemento seleccionado porque no tengo un solo elemento seleccionado. –

Respuesta

18

lo haría así:

<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="..." 
    ... /> 

La razón por la que esto funciona es que CombinedTemplate normalmente sólo utiliza NormalItemTemplate para presentar sus datos, pero si no hay ComboBoxItem ancestro asume que es en el cuadro de selección de modo usa SelectionBoxTemplate.

Tenga en cuenta que los tres DataTemplates podrían incluirse en cualquier nivel de ResourceDictionary (no sólo a nivel Window) o incluso directamente en el ComboBox, dependiendo de su preferencia.

+0

Gracias, definitivamente voy a probar este. –

+0

Es una solución de trabajo * y * elegante, ¡muchas gracias! –

+2

Sin embargo, esto genera una excepción vinculante: 'No se puede encontrar el origen para el enlace con la referencia 'RelativeSource FindAncestor, AncestorType =' System.Windows.Controls.ComboBoxItem ', AncestorLevel =' 1'''. Creo que configurar 'ItemTemplateSelector' es un mejor enfoque. Aquí hay un ejemplo: 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

Necesita buscar en Triggers y Styles. También puede ser que desee ver en algunas de mis preguntas más viejos aquí en StackOverflow que ayudó a conquistar estos problemas:

+0

Gracias, desafortunadamente eso era demasiado abstracto en comparación con otras respuestas. Tengo una idea general de cómo funcionan los desencadenantes, fue la solución {x: Null} que no pude inventar. –

0

Si lo he entendido bien, usted quiere un control que ¿Se muestra algo arbitrario junto con un botón desplegable que muestra una lista de elementos con casillas de verificación junto a ellos?

Ni siquiera me molestaría en intentar restylear ComboBox para lograrlo. El problema es que ComboBox es más especializado en un camino diferente de lo que necesita. Si nos fijamos en el ComboBox ControlTemplate Example, verá que simplemente utiliza un control Popup para mostrar la lista de valores posibles.

Puede tomar piezas de esa plantilla como guía para crear un UserControl que sea más fácil de entender y que proporcione mejor lo que desea. Incluso podrá agregar una propiedad SelectedItems y tal que no proporcione.

Un ejemplo de lo que quiero decir con orientación: el Popup tiene una propiedad IsOpen. En la plantilla de control, se establece en {TemplateBinding IsDropDownOpen}, lo que significa que la clase ComboBox tiene una propiedad IsDropDownOpen que se cambia para controlar la expansión/colapso del Popup.

+0

El problema con los controles personalizados es que no tienen estilos incorporados. Ya he creado un control personalizado con SelectedItems, pero dentro de él se basa en un ComboBox ya que quiero que los estilos predeterminados trabajen con él sin repetirlos. –

0

comentario de Alexey Mitev sobre Ray Burns answer me inspiró a escribir la siguiente clase de utilidad razonablemente corto, que ahora utilizo en todos mis proyectos 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 esto en la caja de herramientas, es posible escribir XAML como este:

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