Como exibir o texto padrão “--Selecionar equipe -” na caixa de combinação no pageload no WPF?


109

Em um aplicativo WPF, em um aplicativo MVP, tenho uma caixa de combinação, para a qual exibo os dados buscados no banco de dados. Antes dos itens adicionados à caixa Combo, quero exibir o texto padrão, como

" -- Selecionar time --"

de modo que ao carregar a página seja exibido e ao selecioná-lo, o texto deve ser apagado e os itens devem ser exibidos.

A seleção de dados do banco de dados está acontecendo. Preciso exibir o texto padrão até que o usuário selecione um item da caixa de combinação.

Por favor me guie

Respostas:


107

A maneira mais fácil que encontrei de fazer isso é:

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

Obviamente, você precisará adicionar outras opções, mas esta é provavelmente a maneira mais simples de fazer isso.

No entanto, há uma desvantagem neste método: embora o texto dentro da sua caixa de combinação não seja editável, ele ainda pode ser selecionado. No entanto, dada a baixa qualidade e complexidade de todas as alternativas que encontrei até agora, esta é provavelmente a melhor opção que existe.


Ótima resposta, Chris! Eu apenas adicionaria Focusable = "True", mas isso é apenas uma mudança cosmética.
Slavisa

6
resposta perfeita Chris. Uma propriedade pode fazer uma diferença tão grande: D
Aster Veigas

4
Focusable="False" IsEditable="True" IsReadOnly="True"
Kamil Lelonek

1
+1. Infelizmente, ele não funciona com itens mistos (por exemplo, se os itens da caixa de combinação forem imagens).
greenoldman

11
Solução simples e funcional. Os controles WPF têm esse tipo de problema em toda a estrutura. Aqui e ali, os controles estão sem recursos que a maioria dos desenvolvedores precisaria. Como resultado, os desenvolvedores perdem seu tempo procurando soluções, comprando controles alternativos de terceiros ou implementando soluções alternativas ... A equipe do WPF usa seus controles para seus próprios desenvolvimentos?
Damn Vegetables

90

Você pode fazer isso sem qualquer código por trás, usando um IValueConverter.

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

Aqui você tem a classe do conversor que pode ser reutilizada.

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
}

E, finalmente, você precisa declarar seu conversor em uma seção de recursos.

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

Onde Conversores é o local em que você colocou a classe de conversores. Um exemplo é:

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

O bom dessa abordagem é não haver repetição de código em seu código.


Gostaria de usar isso, mas parece que não é permitido nos casos em que a combobox é o cabeçalho de um datagrid. . . XAML fornece um erro de que o cabeçalho já está definido (ou talvez não possa ser definido mais de uma vez). Alguma ideia? Estou pensando apenas em usar uma opção de valor nulo, que permitirá uma reinicialização selecionando-a, mas parece um pouco desleixado.
Paul Gibson

1
Uma grande razão pela qual esta é uma solução excelente é porque quase todos os projetos WPF vinculados a dados usam um NullToVisibilityConverter, então ele já está lá na maioria das vezes - pode muito bem fazer uso dele!
partee

2
Na verdade, você pode usar um DataTriggerpara evitar até mesmo o código do conversor aqui :)
Billy ONeal

49

Gosto da resposta do Tri Q, mas esses conversores de valor são difíceis de usar. PaulB fez isso com um manipulador de eventos, mas isso também é desnecessário. Esta é uma solução XAML pura:

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

33

Ninguém disse que uma solução xaml pura tem que ser complicada. Aqui está um simples, com 1 gatilho de dados na caixa de texto. Margem e posição conforme desejado

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

5
Eu precisava mover "Visibility =" Hidden "para o acionador de dados. Em seguida, funcionou conforme o esperado. Definitivamente, a abordagem mais direta que já vi. Para reutilização, movi o estilo para um recurso
Mitch

A resposta de @Mitch IceForce não está funcionando para mim, o que você mudou para fazê-la funcionar?
Chris

1
@Chris Acho que ele quis dizer adicionar <Setter Property="Visibility" Value="Hidden"/>fora do gatilho (dentro do estilo) e remover Visibility="Hidden"do elemento de bloco de texto real
Reintegrar Monica, por favor

@Mitch, como você move o estilo Textblock em um recurso para reutilização se você tem o ElementName no DataTrigger apontando para um objeto específico (mybox)? Existe alguma maneira de especificar esse nome de forma genérica?
CrApHeR


16

Não sei se é compatível diretamente, mas você pode sobrepor o combo com um rótulo e defini-lo como oculto se a seleção não for nula.

por exemplo.

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

Então, na seleção mudou manipulador ...

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

1
Em vez de fazer um manipulador SelectionChanged, a visibilidade do TextBlock pode ser definida em XAML.
aliceraunsbaek

6

Com base na resposta do IceForge, preparei uma solução reutilizável:

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>

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

Agradável. Eu o exterminei vinculando o DataContext do TextBlock usando uma fonte relativa para evitar ter que definir o nome. Veja o próximo comentário para marcação (o código nos comentários do SO parece feio)
Sascha

<TextBlock DataContext = "{Binding Path = Children [0] .SelectedItem, RelativeSource = {RelativeSource AncestorType = Grid}}" Text = "- Selecione o projeto -" Style = "{StaticResource ComboBoxSelectOverlay}" />
Sascha

4

Não tentei com caixas de combinação, mas isso funcionou para mim com outros controles ...

postagem de blog bloqueada por idade

Ele usa a camada de adorno aqui para exibir uma marca d'água.


Acabei de baixar e tentei este código. Parece funcionar conforme anunciado. Permite decorar seu combo com uma propriedade anexada simples contendo sua marca d'água. Ele também funciona para outros controles. É uma abordagem muito melhor do que qualquer uma das outras respostas a esta pergunta.
Ian Oakes

Boa coisa, não só resolve o problema do ComboBox, mas agora posso me livrar do conjunto WPF Tools e apenas usar isso nos meus TextBoxes em vez do controle WatermarkedTextBox também, tão cheio de vitórias :) - ah, não é um Geek Trapped Armadilha aprovada!
5 de

2

A solução de HappyNomad foi muito boa e me ajudou a chegar a essa solução ligeiramente 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>

2

A maneira mais fácil é usar CompositeCollection para mesclar texto e dados padrão do banco de dados diretamente no ComboBox, por exemplo

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

E em Resources, defina StaticResource para vincular as opções ComboBox ao seu DataContext, porque a vinculação direta em CollectionContainer não funciona corretamente.

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

Desta forma, você pode definir as opções do ComboBox apenas em xaml, por exemplo

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

1

Eu recomendaria o seguinte:

Defina um comportamento

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

Use o comportamento

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

Isso funciona como um encanto para uma única caixa de combinação. Mas quando o utilizo com mais de 1 combo, dá-me um erro (mas compila e funciona bem) "Propriedade 'DefaultText' já registada por 'ComboBox'". Eu mencionei a correção no meu blog.
Romesh D. Niriella

Obrigado por apontar isso. Não consegui produzir este erro no meu computador. No entanto, concordo que typeof (ComboBoxBehaviors) deve ser passado no 3º parâmetro de RegisterAttached em vez de typeof (ComboBox).
Usman Zafar

Embora este post seja um pouco antigo, não vejo como pode funcionar. O bg do combo é definido por meio de gatilhos com várias condições. Tente colocar um combo sozinho em uma grade e definir manualmente o bg como 'vermelho'. Não tem efeito na área onde você deseja que a marca d'água apareça. Isso pode afetar apenas o bg atrás do painel suspenso. Uma solução melhor é copiar o modelo de controle da caixa de combinação e adicionar alguns gatilhos e estilos para pintar um pincel visual que consiste em um bloco de texto no fundo da borda.
Newclique

1

A resposta do IceForge foi bem próxima e é AFAIK a solução mais fácil para esse problema. Mas faltou algo, pois não estava funcionando (pelo menos para mim, ele nunca realmente exibe o texto).

No final, você não pode simplesmente definir a propriedade "Visibility" do TextBlock como "Hidden" para que fique oculto quando o item selecionado da caixa de combinação não for nulo; você tem que DEFINIR dessa forma por padrão (uma vez que você não pode verificar não nulo em gatilhos , usando um Setter em XAML no mesmo lugar que os gatilhos.

Aqui está a solução real baseada na dele, o Setter ausente sendo colocado logo antes dos Ativadores:

<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

EDIT: Por comentários abaixo, esta não é uma solução. Não tenho certeza de como estava funcionando e não posso verificar esse projeto.

É hora de atualizar esta resposta para o XAML mais recente.

Encontrando essa questão do SO em busca de uma solução para essa questão, descobri que a especificação XAML atualizada tem uma solução simples.

Um atributo chamado "Espaço reservado" agora está disponível para realizar essa tarefa. É tão simples quanto isto (no 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>

Usei esta solução - o eleitor negativo se preocupa em elaborar? Concedido, não sou um especialista em XAML, mas funcionou.
Robb Sadler

1
Embora eu não seja o votante negativo, imagino que você tenha votado contra porque não há PlaceholderTextpropriedades na System.Windows.ComboBoxclasse . Esta é uma pergunta sobre WPF, não WinForms.
Sheridan

Cara, isso é estranho - eu sei que estava trabalhando em um aplicativo XAML e sei que tinha acabado de descobrir isso e vi funcionar. Talvez uma extensão tenha sido incluída no projeto? IDK - Desde então, tenho olhado e com certeza não há nenhum espaço reservado no ComboBox. Não consigo voltar ao projeto em que estava trabalhando - cliente antigo. ugh.
Robb Sadler

2
Você não está errado, mas isso não é para WPF. UWP ComboBox tem isso, consulte esta página: msdn.microsoft.com/en-us/library/windows/apps/…
laishiekai

0

Não é a melhor prática ... mas funciona bem ...

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

Código por trá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;
            }           
    }
}

0

Acredito que uma marca d'água conforme mencionada nesta postagem funcionaria bem neste caso

É necessário um pouco de código, mas você pode reutilizá-lo para qualquer combobox ou caixa de texto (e até mesmo caixas de senha), então eu prefiro assim


0

Estou usando uma classe IsNullConverter em meu projeto e funcionou para mim. aqui está o código para ele em c #, crie uma pasta chamada Converter e adicione esta classe nessa pasta, já que o gatilho usado não suporta valor para em vez de nulo, e IsNullConverter apenas faz isso

 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.");
    }
}

adicione o namespace no arquivo xaml assim.

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

significa

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

use esta linha abaixo dos recursos para torná-lo disponível através do código xaml

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

aqui está o código xaml, usei aqui o gatilho, então sempre que um item é selecionado na combobox a visibilidade do seu texto se torna falsa.

<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

// Código XAML

// código 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

Um pouco tarde mas ..

Uma maneira mais simples seria adicionar um item de dados fictício à lista com o parâmetro IsDummy = true e ter certeza de que não é HitTestVisable e sua altura é de 1 pixel (usando um conversor) para que não seja visto.

Do que apenas registrar em SelectionChanged e nele, definir o índice para o índice de item fictício.

Funciona muito bem e assim você não bagunça o estilo e as cores do ComboBox ou do tema do seu aplicativo.


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

O código acima demonstra a maneira mais simples de conseguir isso. Após o carregamento da janela, declare o texto da combobox, usando a propriedade .Text da combobox. Isso pode ser estendido ao DatePicker, Textbox e outros controles também.


0

Fiz isso antes de vincular o combobox aos dados do banco de dados no codebehind como este -

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

1
Isso apenas adiciona o texto como uma opção na lista suspensa. Não era isso que o OP estava pedindo.
Dean Friedland

trata-se de adicionar texto padrão e eu fiz desta forma
Atiq Baqi

0
  1. Coloque uma etiqueta no topo da combobox.

  2. Vincule o conteúdo do rótulo à propriedade Texto da combobox.

  3. Defina a opacidade do combobox para zero, Opacity = 0.

  4. Escreva o texto padrão na propriedade Texto combobox

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

-2

Defina apenas o atributo IsEditable como verdadeiro

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

-3

Eu sei que isso é meio velho, mas e desta forma:

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

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

2
ComboBoxnão tem EmptySelectionBoxTemplatepropriedade.
Novitchi S
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.