web-dev-qa-db-fra.com

Comment afficher le texte par défaut "--Select Team -" dans la liste déroulante lors du chargement de page dans WPF?

Dans une application WPF, dans l'application MVP, j'ai une liste déroulante pour laquelle j'affiche les données extraites de Database. Avant que les éléments ajoutés à la liste déroulante, je veux afficher le texte par défaut tel que

" -- Choisis une équipe --"

de sorte que sur pageload, il affiche et sélectionne le texte doit être effacé et les éléments doivent être affichés.

La sélection des données de la base de données est en cours. Je dois afficher le texte par défaut jusqu'à ce que l'utilisateur sélectionne un élément de la liste déroulante.

Guidez-moi s'il-vous-plaît

92
Ramm

Le moyen le plus simple que j'ai trouvé est le suivant:

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

Vous aurez évidemment besoin d'ajouter vos autres options, mais c'est probablement la manière la plus simple de le faire.

Il existe toutefois un inconvénient à cette méthode: si le texte de votre liste déroulante ne peut pas être modifié, il est toujours sélectionnable. Cependant, étant donné la qualité médiocre et la complexité de toutes les alternatives que j'ai trouvées à ce jour, il s'agit probablement de la meilleure option.

89
Chris Walter

Vous pouvez le faire sans code derrière en utilisant une IValueConverter.

<Grid>
   <ComboBox
       x:Name="comboBox1"
       ItemsSource="{Binding MyItemSource}"  />
   <TextBlock
       Visibility="{Binding SelectedItem, ElementName=comboBox1, Converter={StaticResource NullToVisibilityConverter}}"
       IsHitTestVisible="False"
       Text="... Select Team ..." />
</Grid>

Ici vous avez la classe de convertisseur que vous pouvez réutiliser.

public class NullToVisibilityConverter : IValueConverter
{
    #region Implementation of IValueConverter

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value == null ? Visibility.Visible : Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

Et enfin, vous devez déclarer votre convertisseur dans une section de ressources.

<Converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />

Où Convertisseurs est l'endroit où vous avez placé la classe de convertisseur. Un exemple est:

xmlns:Converters="clr-namespace:MyProject.Resources.Converters"

La très belle chose à propos de cette approche est de ne pas répéter le code dans votre code derrière.

86
Tri Q Tran

J'aime la réponse de Tri Q, mais ces convertisseurs de valeur sont difficiles à utiliser. PaulB l'a fait avec un gestionnaire d'événements, mais c'est également inutile. Voici une solution XAML pure:

<ContentControl Content="{Binding YourChoices}">
    <ContentControl.ContentTemplate>
        <DataTemplate>
            <Grid>
                <ComboBox x:Name="cb" ItemsSource="{Binding}"/>
                <TextBlock x:Name="tb" Text="Select Something" IsHitTestVisible="False" Visibility="Hidden"/>
            </Grid>
            <DataTemplate.Triggers>
                <Trigger SourceName="cb" Property="SelectedItem" Value="{x:Null}">
                    <Setter TargetName="tb" Property="Visibility" Value="Visible"/>
                </Trigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </ContentControl.ContentTemplate> 
</ContentControl>
46
HappyNomad

Personne n'a dit qu'une solution pure en xaml devait être compliquée. Voici un exemple simple, avec 1 déclencheur de données dans la zone de texte. Marge et position comme souhaité

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

Définissez IsEditable = True sur l'élément Combobox. Ceci affichera la propriété Text de la liste déroulante.

20
medusa

Je ne sais pas s'il est directement pris en charge mais vous pouvez superposer le combo avec une étiquette et le définir sur masqué si la sélection n'est pas nulle.

par exemple.

<Grid>
   <ComboBox Text="Test" Height="23" SelectionChanged="comboBox1_SelectionChanged" Name="comboBox1" VerticalAlignment="Top" ItemsSource="{Binding Source=ABCD}"  />
   <TextBlock IsHitTestVisible="False" Margin="10,5,0,0" Name="txtSelectTeam" Foreground="Gray" Text="Select Team ..."></TextBlock>
</Grid>

Puis dans la sélection a changé de gestionnaire ...

private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    txtSelectTeam.Visibility = comboBox1.SelectedItem == null ? Visibility.Visible : Visibility.Hidden;
}
14
PaulB

Basé sur la réponse d'IceForge j'ai préparé une solution réutilisable:

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

exemple d'utilisation:

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

Pas essayé avec les combos mais cela a fonctionné pour moi avec d'autres contrôles ...

ageektrapped blogpost

Il utilise la couche adorner ici pour afficher un filigrane.

4
Crippeoblade

La solution de HappyNomad était très bonne et m'a permis d'arriver finalement à cette solution légèrement différente. 

<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>
2
Nick Falco

Le moyen le plus simple consiste à utiliser CompositeCollection pour fusionner le texte et les données par défaut de la base de données directement dans ComboBox, par exemple.

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

Et dans Ressources, définissez StaticResource pour lier les options ComboBox à votre DataContext, car la liaison directe dans CollectionContainer ne fonctionne pas correctement.

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

De cette façon, vous pouvez définir vos options ComboBox uniquement dans xaml, par exemple. 

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

La réponse d'IceForge était assez proche, et c'est autant que je sache, la solution la plus simple à ce problème. Mais il manquait quelque chose, car cela ne fonctionnait pas (du moins pour moi, le texte ne s'affiche jamais).

En fin de compte, vous ne pouvez pas simplement définir la propriété "Visibility" du TextBlock sur "Caché" pour qu'il soit masqué lorsque l'élément sélectionné de la liste déroulante n'est pas null; vous devez le configurer de cette façon par défaut (puisque vous ne pouvez pas cocher non dans les déclencheurs , en utilisant un Setter en XAML au même endroit que les déclencheurs.

Voici la solution réelle basée sur la sienne, le Setter manquant étant placé juste avant les déclencheurs:

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

Je recommanderais ce qui suit:

Définir un comportement

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

Utilisateur le comportement

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

EDIT: Selon les commentaires ci-dessous, ce n'est pas une solution. Je ne sais pas comment j'ai fonctionné et je ne peux pas vérifier ce projet.

Il est temps de mettre à jour cette réponse pour le dernier XAML. 

En trouvant cette SO question en cherchant une solution à cette question, j’ai alors constaté que la spécification XAML mise à jour offrait une solution simple.

Un attribut appelé "Placeholder" est maintenant disponible pour accomplir cette tâche. C'est aussi simple que cela (dans 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>
1
Robb Sadler

Je l'ai fait avant de lier la combobox avec les données de la base de données dans codebehind comme ceci -

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

Un peu tard mais ..

Une méthode plus simple consisterait à ajouter un élément de données factice à la liste avec le paramètre IsDummy = true et à vous assurer qu'il ne s'agit pas de HitTestVisable et que sa hauteur est de 1 pixel (à l'aide d'un convertisseur) afin qu'il ne soit pas visible.

Il suffit ensuite de s’inscrire à SelectionChanged et de définir l’index sur l’index factice.

Cela fonctionne comme un charme et vous éviterez ainsi le style et les couleurs de la ComboBox ou du thème de votre application.

0
Eibi

Solution 
1. Placez une étiquette sur la liste déroulante. 
2. Liez le contenu de l'étiquette à la propriété Text de la liste déroulante.
3. Définissez l'opacité de la liste déroulante sur zéro, Opacité = 0. 
4. Écrire du texte par défaut dans la propriété Text de la liste déroulante

 <Grid>
        <Label Content="{Binding ElementName=cb, Path=Text}" 
         VerticalContentAlignment="Center"
         HorizontalContentAlignment="Center"
         Height="{Binding ElementName=cb, Path=Height}"
         Width="{Binding ElementName=cb, Path=Width}"/>

         <ComboBox Name="cb"
           Text="--Select Team--" Opacity="0" 
           Height="40" Width="140" >
            <ComboBoxItem Content="Manchester United" />
            <ComboBoxItem Content="Lester" />
        </ComboBox>
    </Grid>
0
Yinon Dotan

J'utilise une classe IsNullConverter dans mon projet et cela a fonctionné pour moi . Voici le code correspondant en c #, créez un dossier nommé Converter et ajoutez cette classe dans ce dossier, car le déclencheur utilisé ne prend pas en charge la valeur pour plutôt que null, et IsNullConverter vient de le faire

 public class IsNullConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (value == null);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new InvalidOperationException("IsNullConverter can only be used OneWay.");
    }
}

ajoutez l'espace de noms dans le fichier xaml comme ceci.

xmlns:Converters="clr-namespace:TymeSheet.Converter"

veux dire

xmlns:Converters="clr-namespace:YourProjectName.Converter"

utilisez cette ligne sous les ressources pour la rendre disponible via le code xaml

<Converters:IsNullConverter x:Key="isNullConverter" />

voici le code xaml, j’ai utilisé ici le déclencheur pour que chaque fois qu’un élément est sélectionné dans la liste déroulante, la visibilité de votre texte devient fausse.

<TextBlock Text="Select Project" IsHitTestVisible="False" FontFamily="/TimeSheet;component/Resources/#Open Sans" FontSize="14" Canvas.Right="191" Canvas.Top="22">
                        <TextBlock.Resources>
                            <Converters:IsNullConverter x:Key="isNullConverter"/>
                        </TextBlock.Resources>
                        <TextBlock.Style>
                            <Style TargetType="TextBlock">
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding ElementName=ProjectComboBox,Path=SelectedItem,Converter={StaticResource isNullConverter}}" Value="False">
                                        <Setter Property="Visibility" Value="Hidden"/>
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </TextBlock.Style>
                    </TextBlock>
0
Safwan

// Code XAML

// Code 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");

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

Le code ci-dessus illustre le moyen le plus simple d'y parvenir. Après le chargement de la fenêtre, déclarez le texte de la liste déroulante, à l'aide de la propriété .Text de la liste déroulante. Cela peut être étendu au DatePicker, à la zone de texte et à d’autres contrôles.

0
Ketan Dubey

Pas la meilleure pratique..mais marche bien ...

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

Code derrière

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;
            }           
    }
}
0
Madi D.

Je crois qu'un filigrane comme mentionné dans cet article fonctionnerait bien dans ce cas

Il faut un peu de code, mais vous pouvez le réutiliser pour n’importe quelle liste déroulante ou zone de texte (et même pour les boîtes de mots de passe).

0
FearlessHyena