2010-05-25 42 views
8

G'day!¿Puede una WPF ComboBox mostrar texto alternativo cuando su selección es nula?

Quiero que mi WPF ComboBox muestre algún texto alternativo cuando su selección enlazada a datos es null.

El modelo de vista tiene las propiedades esperadas:

public ThingoSelectionViewModel : INotifyPropertyChanged { 
    public ThingoSelectionViewModel(IProvideThingos) { 
     this.Thingos = IProvideThingos.GetThingos(); 
    } 

    public ObservableCollection<Thingo> Thingos { get; set; } 

    public Thingo SelectedThingo { 
     get { return this.selectedThingo; } 
     set { // set this.selectedThingo and raise the property change notification 
    } 

    // ... 

} 

La vista ha XAML de unión al modelo de vista de la forma esperada:

<ComboBox x:Name="ComboboxDrive" SelectedItem="{Binding Path=SelectedThingo}" 
      IsEditable="false" HorizontalAlignment="Left" MinWidth="100" 
      IsReadOnly="false" Style="{StaticResource ComboboxStyle}" 
      Grid.Column="1" Grid.Row="1" Margin="5" SelectedIndex="0"> 
    <ComboBox.ItemsSource> 
     <CompositeCollection> 
     <ComboBoxItem IsEnabled="False">Select a thingo</ComboBoxItem> 
     <CollectionContainer 
      Collection="{Binding Source={StaticResource Thingos}}" /> 
     </CompositeCollection> 
    </ComboBox.ItemsSource> 
</ComboBox> 

El ComboBoxItem encajada en la parte superior es una forma de obtener un elemento adicional en la parte superior. Es puro cromado: el modelo de vista se mantiene puro y simple. Solo hay un problema: los usuarios quieren que se muestre "Seleccionar una cosa" siempre que la selección de ComboBox 'sea nula.

Los usuarios hacen no quieren una cosa seleccionada por defecto. Quieren ver un mensaje indicándoles que seleccionen una cosa.

me gustaría evitar tener que contaminan el modelo de vista con una clase ThingoWrapper con un método ToString regresar "Seleccionar un thingo" si su propiedad .ActualThingo es nulo, envolviendo cada Thingo como lo pueblan Thingos, y averiguar alguna manera de evitar que el usuario seleccione nulled Thingo.

¿Hay alguna manera de mostrar "Seleccione una cosa" dentro de los límites ComboBox 'usando XAML puro, o XAML puro y algunas líneas de código en la clase de código subyacente de la vista?

+0

Fwiw: Terminé la aplicación de la ThingoWrapper, modificando el ThingoSelectionViewModel para hacer frente a la selección del valor nulo envuelto, y encontrar maneras de seleccionar automáticamente los objetos Whatsy y Fadoozamy así que no tuve que envolverlos también. –

Respuesta

3

La ruta de menor resistencia aquí que he encontrado es usar Null Object Pattern Para ver un ejemplo del uso de este patrón en .NET Framework, considere el valor estático Double.NaN si crea un objeto nulo para su Thingo, en su modelo de vista puede agregarlo al frente de su lista para indicar que "no se seleccionó nada". Cree un DataTemplate para la clase Thingo que tiene un DataTrigger para la instancia del Objeto Nulo que muestra "Seleccionar un valor".

Podría dar una muestra del código pero ya pasó la hora de acostarme.

3

Editar: Parece que la idea del disparador es un no ir. He añadido lo siguiente a la plantilla de control de un cuadro combinado de prueba fue en vano:

<Trigger Property="SelectedItem" Value="{x:Null}"> 
     <Setter Property="Text" Value="No Item Selected"/> 
    </Trigger> 

Además, al intentar editar la plantilla de control de mezcla (Edición actual) me quedo con un cuadro combinado sin rasgos distintivos, sin colores, solo un botón feo (pero hay un menú desplegable sin bordes). Pruebe la sugerencia de alguien más (quizás Mike Brown).

original:

Se puede utilizar un disparador en la plantilla de control. Aquí hay un ejemplo usando un ListBox de una aplicación en la que estoy trabajando.

<ControlTemplate x:Key="SnazzyFormListBoxTemplate" TargetType="{x:Type ListBox}"> 
    <Microsoft_Windows_Themes:ClassicBorderDecorator x:Name="Bd" SnapsToDevicePixels="True" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderStyle="Sunken" BorderThickness="{TemplateBinding BorderThickness}"> 
     <ScrollViewer Padding="{TemplateBinding Padding}" Focusable="False" Template="{DynamicResource SnazzyScrollViewerControlTemplate}"> 
      <Grid> 
      <TextBlock x:Name="textBlock" Text="No Items" FontFamily="Arial" FontWeight="Bold" FontSize="13.333" Foreground="#4D000000" RenderTransformOrigin="0.5,0.5" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,10"/> 
      <ItemsPresenter x:Name="itemsPresenter" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> 
      </Grid> 
     </ScrollViewer> 
    </Microsoft_Windows_Themes:ClassicBorderDecorator> 
    <ControlTemplate.Triggers> 
     <Trigger Property="Selector.IsSelected" Value="True"/> 
     <Trigger Property="HasItems" Value="False"> 
      <Setter Property="Visibility" TargetName="textBlock" Value="Visible"/> 
      <Setter Property="Visibility" TargetName="itemsPresenter" Value="Collapsed"/> 
     </Trigger> 
     <Trigger Property="HasItems" Value="True"> 
      <Setter Property="Visibility" TargetName="textBlock" Value="Collapsed"/> 
      <Setter Property="Visibility" TargetName="itemsPresenter" Value="Visible"/> 
     </Trigger> 
    </ControlTemplate.Triggers> 
</ControlTemplate> 

El ControlTemplate anterior tiene un disparador que comprueba los elementos HasItems. Si es False, se muestra un bloque de texto que dice "Sin elementos" en el medio del ListBox. Si hay elementos, se muestran.

En su caso, cambie el activador para verificar si ItemSelected es x: Null y establezca la propiedad Text en "Nothing Selected".

+0

El usuario pregunta cómo tener un valor de "nada seleccionado" en el cuadro combinado, para no tener un aspecto diferente para un cuadro combinado vacío. –

+0

Lo sé. Pueden establecer un desencadenante en la plantilla de control que verifica el estado de los elementos seleccionados y, si es nulo, establecer el valor del texto del cuadro en "Nada seleccionado". Esperaba que el usuario pudiera inferir eso del ejemplo dado. Actualicé mi respuesta para un mejor ajuste. – CodeWarrior

+0

@Mike Brown: He editado mi respuesta, por lo que te agradecería que pudieras desconectar el voto negativo. – CodeWarrior

4

no puede utilizar una plantilla de control de disparo, pero se puede configurar una plantilla de elemento simple para el cuadro combinado:

<ComboBox ItemsSource="{Binding}" > 
     <ComboBox.ItemTemplate> 
      <DataTemplate> 
       <TextBlock x:Name="displayText" Text="{Binding}" /> 
       <DataTemplate.Triggers> 
        <DataTrigger Binding="{Binding}" Value="{x:Null}"> 
         <Setter TargetName="displayText" Property="Text" Value="Default Value" /> 
        </DataTrigger> 
       </DataTemplate.Triggers> 
      </DataTemplate> 
     </ComboBox.ItemTemplate> 
    </ComboBox> 
+0

Hmm. Tendré que verificar esto, también. –

8

¿Cómo es su requerimiento estricto MVVM? ¿Puedes tener un pequeño código detrás de la vista?

Tal vez podría contener el cuadro combinado en una cuadrícula, algo como esto:

<Grid> 
    <ComboBox x:Name="ComboBoxControl" 
       SelectionChanged="ComboBoxControl_SelectionChanged" 
       HorizontalAlignment="Left" VerticalAlignment="Top" 
       MinWidth="{Binding ElementName=UnselectedText, Path=ActualWidth}"> 
     <ComboBoxItem>One</ComboBoxItem> 
     <ComboBoxItem>Two</ComboBoxItem> 
     <ComboBoxItem>Three</ComboBoxItem> 
    </ComboBox> 
    <TextBlock IsHitTestVisible="False" 
       x:Name="UnselectedText" 
       HorizontalAlignment="Left" 
       Text="Select an option..." 
       VerticalAlignment="Top" Margin="4" 
       Padding="0,0,30,0" /> 
</Grid> 

Luego, en el código subyacente, insertar alguna lógica en un controlador de eventos:

Private Sub ComboBoxControl_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs) 
    If ComboBoxControl.SelectedIndex = -1 Then 
     UnselectedText.Visibility = Windows.Visibility.Visible 
    Else 
     UnselectedText.Visibility = Windows.Visibility.Hidden 
    End If 
End Sub 

Ajuste de la IsHitTestVisible="False" DependencyProperty en TextBlock permite pasar los eventos del mouse para que pueda hacer clic en ComboBox, y establecer la visibilidad en Hidden en el código subyacente evita que el diseño de una apariencia predeterminada de ComboBox salte cuando el indicador el texto está oculto

+1

Incluso si quiere hacerlo de esa manera, no tiene que romper su MVVM. Simplemente use un convertidor para convertir la selección a visibilidad. – Ben

1

Sé que este es un hilo viejo, pero así es como lo hago. Después de buscar la colección de Thingos, simplemente inserto un nuevo Thingo con un valor de identificación falso y un valor de visualización de "Seleccione una cosa".

public ThingoSelectionViewModel(IProvideThingos) { 
      this.Thingos = IProvideThingos.GetThingos(); 
      Thingo newThingo = new Thingo(); 
      newThingo.ThingoID = -1; 
      newThingo.ThingoDisplayName = "Select a thingo"; 
      this.Thingos.Insert(0, newThingo); 
     } 

Ahora, cuando el ComboBox es de datos, el primer elemento es "Seleccione una cosa". Luego, cuando se selecciona un Thingo, pruebo la ID del SelectedThingo y actúo en consecuencia.

+0

Eso es básicamente el Patrón de Objeto Nulo. A veces uso una propiedad estática de la clase para exponer el Objeto nulo para que la gente pueda verificar su cosa contra Thingo.NotSpecified. –

+0

Figuras ... A menudo me preguntan si uso o utilizo "tal o cual" una metodología o proceso, y creo que la respuesta es 'no'. Solo para descubrir que la respuesta es 'Sí', pero simplemente no sabía cómo se llamaba oficialmente. Gracias. –

0

Sé que estoy resucitando una publicación anterior, pero esta fue la primera que apareció en mi búsqueda en Google. En Visual Studio, puede optar por establecer la Selección predeterminada en 0, en lugar de -1, y simplemente haga que su primera selección sea el texto predeterminado.

<ComboBox x:name="ThingoSelector" SelectedIndex="0"> 
    <ComboBoxItem IsEnabled="False">Choose Thingo</ComboBoxItem> 
    <ComboBoxItem>Thingo 1</ComboBoxItem> 
</ComboBox> 
0

Otra opción:

<ComboBox> 
 
    <ComboBoxItem Visibility="Collapsed" IsSelected="True"> 
 
    <TextBlock Text="Choose item" /> 
 
    </ComboBoxItem> 
 
    <ComboBoxItem> 
 
    <TextBlock Text="Item 1" /> 
 
    </ComboBoxItem> 
 
    <ComboBoxItem> 
 
    <TextBlock Text="Item 2" /> 
 
    </ComboBoxItem> 
 
</ComboBox>

Cuestiones relacionadas