combobox combobox - Cómo mostrar el texto predeterminado "--Seleccione Team-" en el cuadro combinado en la carga de la página en WPF?




add items (19)

En una aplicación WPF, en la aplicación MVP, tengo un cuadro combinado, para el cual visualizo los datos obtenidos de la base de datos. Antes de los elementos agregados al cuadro Combo, quiero mostrar el texto predeterminado como

" -- Selecciona un equipo --"

para que en la carga de página se muestre y al seleccionarlo, el texto se borre y los elementos se muestren.

Seleccionar datos de DB está sucediendo. Necesito mostrar el texto predeterminado hasta que el usuario seleccione un elemento del cuadro combinado.

Por favor guíame


Answers

Lo hice antes de vincular el combobox con los datos de la base de datos en código detrás de este modo:

Combobox.Items.Add("-- Select Team --");
Combobox.SelectedIndex = 0;

Un poco tarde pero ...

Una forma más simple sería agregar un elemento de datos ficticio a la lista con el parámetro IsDummy = true y asegurarse de que no sea HitTestVisable y su altura sea de 1 píxel (usando un convertidor) para que no se vea.

Luego de registrarse en SelectionChanged y en ella, establezca el índice en el índice de elementos ficticios.

Funciona como un amuleto y de esta manera no te metas con el estilo y los colores del ComboBox o el tema de tu aplicación.


Establecer IsEditable = True en el elemento Combobox. Esto mostrará la propiedad Text del Combobox


EDITAR: según los comentarios a continuación, esta no es una solución. No estoy seguro de cómo lo estaba trabajando, y no puedo verificar ese proyecto.

Es hora de actualizar esta respuesta para el último XAML.

Al encontrar esta pregunta SO para encontrar una solución a esta pregunta, descubrí que la especificación XAML actualizada tiene una solución simple.

Un atributo llamado "Placeholder" ahora está disponible para realizar esta tarea. Es tan simple como esto (en Visual Studio 2015):

<ComboBox x:Name="Selection" PlaceholderText="Select...">
    <x:String>Item 1</x:String>
    <x:String>Item 2</x:String>
    <x:String>Item 3</x:String>
</ComboBox>

No es una buena práctica ... pero funciona bien ...

<ComboBox GotFocus="Focused"  x:Name="combobox1" HorizontalAlignment="Left" Margin="8,29,0,0" VerticalAlignment="Top" Width="128" Height="117"/>

Código detrás

public partial class MainWindow : Window
{
    bool clearonce = true;
    bool fillonce = true;
    public MainWindow()
    {
        this.InitializeComponent();          
        combobox1.Items.Insert(0, " -- Select Team --");
        combobox1.SelectedIndex = 0;
    }

    private void Focused(object sender, RoutedEventArgs e)
    {
            if(clearonce)
            {
                combobox1.Items.Clear();
                clearonce = false;
            }
            if (fillonce)
            {
              //fill the combobox items here 
                for (int i = 0; i < 10; i++)
                {
                    combobox1.Items.Insert(i, i);
                }
                fillonce = false;
            }           
    }
}

La respuesta de IceForge fue bastante cercana, y es AFAIK la solución más fácil para este problema. Pero falló algo, ya que no funcionaba (al menos para mí, nunca muestra el texto).

Al final, no puede simplemente establecer la propiedad "Visibilidad" de TextBlock en "Oculto" para que se oculte cuando el elemento seleccionado del cuadro combinado no sea nulo; tienes que CONFIGURARLO de esa manera por defecto (ya que no puedes verificar nulo en disparadores , usando un Setter en XAML en el mismo lugar que los Triggers).

Aquí está la solución real basada en la suya, el Setter faltante se coloca justo antes de los desencadenantes:

<ComboBox x:Name="combo"/>
<TextBlock Text="--Select Team--" IsHitTestVisible="False">
    <TextBlock.Style>
        <Style TargetType="TextBlock">

            <Style.Setters>
                <Setter Property="Visibility" Value="Hidden"/>
            </Style.Setters>

            <Style.Triggers>
                <DataTrigger Binding="{Binding ElementName=combo,Path=SelectedItem}" Value="{x:Null}">
                    <Setter Property="Visibility" Value="Visible"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBlock.Style>
</TextBlock>

Basado en la respuesta de IceForge, preparé una solución reutilizable:

estilo xaml:

<Style x:Key="ComboBoxSelectOverlay" TargetType="TextBlock">
    <Setter Property="Grid.ZIndex" Value="10"/>
    <Setter Property="Foreground" Value="{x:Static SystemColors.GrayTextBrush}"/>
    <Setter Property="Margin" Value="6,4,10,0"/>
    <Setter Property="IsHitTestVisible" Value="False"/>
    <Setter Property="Visibility" Value="Hidden"/>
    <Style.Triggers>
        <DataTrigger Binding="{Binding}" Value="{x:Null}">
            <Setter Property="Visibility" Value="Visible"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

ejemplo de uso:

<Grid>
     <ComboBox x:Name="cmb"
               ItemsSource="{Binding Teams}" 
               SelectedItem="{Binding SelectedTeam}"/>
     <TextBlock DataContext="{Binding ElementName=cmb,Path=SelectedItem}"
               Text=" -- Select Team --" 
               Style="{StaticResource ComboBoxSelectOverlay}"/>
</Grid>

La forma más fácil que he encontrado para hacer esto es:

<ComboBox Name="MyComboBox"
 IsEditable="True"
 IsReadOnly="True"
 Text="-- Select Team --" />

Obviamente tendrá que agregar sus otras opciones, pero esta es probablemente la forma más sencilla de hacerlo.

Sin embargo, hay un inconveniente en este método, que es cuando el texto dentro de su cuadro combinado no será editable, todavía es seleccionable. Sin embargo, dada la baja calidad y complejidad de cada alternativa que he encontrado hasta la fecha, esta es probablemente la mejor opción.


La solución de HappyNomad fue muy buena y finalmente me ayudó a llegar a esta solución ligeramente diferente.

<ComboBox x:Name="ComboBoxUploadProject" 
    Grid.Row="2"
    Width="200" 
    Height="23"                           
    Margin="64,0,0,0"
    ItemsSource="{Binding projectList}"
    SelectedValue ="{Binding projectSelect}" 
    DisplayMemberPath="projectName"
    SelectedValuePath="projectId"
    >
    <ComboBox.Template>
        <ControlTemplate TargetType="ComboBox">
            <Grid>
                <ComboBox x:Name="cb" 
                    DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" 
                    ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource TemplatedParent}}"
                    SelectedValue ="{Binding SelectedValue, RelativeSource={RelativeSource TemplatedParent}}" 
                    DisplayMemberPath="projectName"
                    SelectedValuePath="projectId"
                    />
                <TextBlock x:Name="tb" Text="Select Item..." Margin="3,3,0,0" IsHitTestVisible="False" Visibility="Hidden"/>
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger SourceName="cb" Property="SelectedItem" Value="{x:Null}">
                    <Setter TargetName="tb" Property="Visibility" Value="Visible"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </ComboBox.Template>
</ComboBox>

Yo recomendaría lo siguiente:

Definir un comportamiento

public static class ComboBoxBehaviors
{
    public static readonly DependencyProperty DefaultTextProperty =
        DependencyProperty.RegisterAttached("DefaultText", typeof(String), typeof(ComboBox), new PropertyMetadata(null));

    public static String GetDefaultText(DependencyObject obj)
    {
        return (String)obj.GetValue(DefaultTextProperty);
    }

    public static void SetDefaultText(DependencyObject obj, String value)
    {
        var combo = (ComboBox)obj;

        RefreshDefaultText(combo, value);

        combo.SelectionChanged += (sender, _) => RefreshDefaultText((ComboBox)sender, GetDefaultText((ComboBox)sender));

        obj.SetValue(DefaultTextProperty, value);
    }

    static void RefreshDefaultText(ComboBox combo, string text)
    {
        // if item is selected and DefaultText is set
        if (combo.SelectedIndex == -1 && !String.IsNullOrEmpty(text))
        {
            // Show DefaultText
            var visual = new TextBlock()
            {
                FontStyle = FontStyles.Italic,
                Text = text,
                Foreground = Brushes.Gray
            };

            combo.Background = new VisualBrush(visual)
            {
                Stretch = Stretch.None,
                AlignmentX = AlignmentX.Left,
                AlignmentY = AlignmentY.Center,
                Transform = new TranslateTransform(3, 0)
            };
        }
        else
        {
            // Hide DefaultText
            combo.Background = null;
        }
    }
}

Usuario el comportamiento

<ComboBox Name="cmb" Margin="72,121,0,0" VerticalAlignment="Top"
          local:ComboBoxBehaviors.DefaultText="-- Select Team --"/>

Nadie dijo que una solución xaml pura tiene que ser complicada. Aquí hay uno simple, con 1 desencadenante de datos en el cuadro de texto. Margen y posición según lo deseado

<Grid>
    <ComboBox x:Name="mybox" ItemsSource="{Binding}"/>
    <TextBlock Text="Select Something" IsHitTestVisible="False">
           <TextBlock.Style>
                <Style TargetType="TextBlock">
                      <Setter Property="Visibility" Value="Hidden"/>
                      <Style.Triggers>
                            <DataTrigger Binding="{Binding ElementName=mybox,Path=SelectedItem}" Value="{x:Null}">
                                  <Setter Property="Visibility" Value="Visible"/>
                             </DataTrigger>
                      </Style.Triggers>
                </Style>
           </TextBlock.Style>
     </TextBlock>
</Grid>

Solo establece el atributo IsEditable en true

<ComboBox Name="comboBox1"            
          Text="--Select Team--"
          IsEditable="true"  <---- that's all!
          IsReadOnly="true"/>

Sé que esto es semi viejo, pero ¿qué tal de esta manera?

<DataTemplate x:Key="italComboWM">
    <TextBlock FontSize="11" FontFamily="Segoe UI" FontStyle="Italic" Text="--Select an item--" />
</DataTemplate>

<ComboBox EmptySelectionBoxTemplate="{StaticResource italComboWM}" />

No lo intenté con cuadros combinados pero esto me ha funcionado con otros controles ...

blogpost ageektrapped

Él usa la capa adorner aquí para mostrar una marca de agua.


InitializeComponent()
yourcombobox.text=" -- Select Team --";

El código anterior muestra la forma más sencilla de lograrlo. Después de la carga de la ventana, declare el texto del cuadro combinado, utilizando la propiedad .Text del cuadro combinado. Esto también se puede extender al DatePicker, Textbox y otros controles.


Creo que una marca de agua como se menciona en esta publicación funcionaría bien en este caso

Se necesita un poco de código, pero puedes reutilizarlo para cualquier cuadro combinado o cuadro de texto (e incluso cajas de contraseña), así que prefiero esta opción.


// Código XAML

// Código de ViewModel

    private CategoryModel _SelectedCategory;
    public CategoryModel SelectedCategory
    {
        get { return _SelectedCategory; }
        set
        {
            _SelectedCategory = value;
            OnPropertyChanged("SelectedCategory");
        }
    }

    private ObservableCollection<CategoryModel> _Categories;
    public ObservableCollection<CategoryModel> Categories
    {
        get { return _Categories; }
        set
        {
            _Categories = value;
            _Categories.Insert(0, new CategoryModel()
            {
                CategoryId = 0,
                CategoryName = " -- Select Category -- "
            });
            SelectedCategory = _Categories[0];
            OnPropertyChanged("Categories");

        }
    }

La forma más sencilla es utilizar CompositeCollection para fusionar texto y datos predeterminados de la base de datos directamente en ComboBox, por ejemplo:

    <ComboBox x:Name="SelectTeamComboBox" SelectedIndex="0">
        <ComboBox.ItemsSource>
            <CompositeCollection>
                <ComboBoxItem Visibility="Collapsed">-- Select Team --</ComboBoxItem>
                <CollectionContainer Collection="{Binding Source={StaticResource ResourceKey=MyComboOptions}}"/>
            </CompositeCollection>
        </ComboBox.ItemsSource>
    </ComboBox>

Y en Resources define StaticResource para vincular las opciones de ComboBox a su DataContext, porque el enlace directo en CollectionContainer no funciona correctamente.

<Window.Resources>
    <CollectionViewSource Source="{Binding}" x:Key="MyComboOptions" />
</Window.Resources>

De esta forma, puede definir sus opciones de ComboBox solo en xaml, por ejemplo

   <ComboBox x:Name="SelectTeamComboBox" SelectedIndex="0">
        <ComboBox.ItemsSource>
            <CompositeCollection>
                <ComboBoxItem Visibility="Collapsed">-- Select Team --</ComboBoxItem>
                <ComboBoxItem >Option 1</ComboBoxItem>
                <ComboBoxItem >Option 2</ComboBoxItem>
            </CompositeCollection>
        </ComboBox.ItemsSource>
    </ComboBox>

Puede anular el ComboBox y cambiar el SelectionBoxItemTemplate directamente.

public class SelectionComboBox : ComboBox
{
    #region Properties

    #region Dependency Properties
    public DataTemplate AltSelectionBoxItemTemplate
    {
        get { return (DataTemplate)GetValue(AltSelectionBoxItemTemplateProperty); }
        set { SetValue(AltSelectionBoxItemTemplateProperty, value); }
    }

    public static readonly DependencyProperty AltSelectionBoxItemTemplateProperty =
        DependencyProperty.Register("AltSelectionBoxItemTemplate", typeof(DataTemplate), typeof(SelectionComboBox), new UIPropertyMetadata(null, (s, e) =>
        {
            // For new changes...
            if ((s is SelectionComboBox) && ((SelectionComboBox)s).Presenter != null && (e.NewValue is DataTemplate))
                ((SelectionComboBox)s).Presenter.ContentTemplate = (DataTemplate)e.NewValue;

            // Set the new value
            ((SelectionComboBox)s).AltSelectionBoxItemTemplate = (DataTemplate)e.NewValue;
        }));
    #endregion

    #region Internals

    #region Elements
    ContentPresenter Presenter { get; set; }
    #endregion

    #endregion

    #endregion

    #region Constructors
    #endregion

    #region Methods

    #region Overrides
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        Presenter = this.GetTemplateChild("contentPresenter") as ContentPresenter;

        // Directly Set the selected item template
        if (AltSelectionBoxItemTemplate != null) Presenter.ContentTemplate = AltSelectionBoxItemTemplate;
    }
    #endregion

    #endregion

}

Una vez que definas el control, puedes ponerle estilo.

<controls:SelectionComboBox ItemsSource="{Binding ...}" SelectedItem="{Binding ..., Mode=TwoWay}">
    <controls:SelectionComboBox.AltSelectionBoxItemTemplate>
        <DataTemplate>
            <!-- My Template Goes Here... -->
        </DataTemplate>
    </controls:SelectionComboBox.AltSelectionBoxItemTemplate>
</controls:SelectionComboBox>




wpf combobox