How to display default text "--Select Team --" in combo box on pageload in WPF?

In a WPF app, in MVP app, I have a combo box,for which I display the data fetched from Database. Before the items added to the Combo box, I want to display the default text such as

" -- Select Team --"

so that on pageload it displays and on selecting it the text should be cleared and the items should be displayed.

Selecting data from DB is happening. I need to display the default text until the user selects an item from combo box.

Please guide me

179484 次浏览

I dont know if it's directly supported but you could overlay the combo with a label and set it to hidden if the selection isn't null.

eg.

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

Then in the selection changed handler ...

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

Not best practice..but works fine...

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

Code behind

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

Not tried it with combo boxes but this has worked for me with other controls...

ageektrapped blogpost

He uses the adorner layer here to display a watermark.

Set IsEditable="True" on the ComboBox element. This will display the Text property of the ComboBox.

You can do this without any code behind by using a IValueConverter.

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

Here you have the converter class that you can re-use.

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
}

And finally, you need to declare your converter in a resource section.

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

Where Converters is the place you have placed the converter class. An example is:

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

The very nice thing about this approach is no repetition of code in your code behind.

I like Tri Q's answer, but those value converters are a pain to use. PaulB did it with an event handler, but that's also unnecessary. Here's a pure XAML solution:

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

The easiest way I've found to do this is:

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

You'll obviously need to add your other options, but this is probably the simplest way to do it.

There is however one downside to this method which is while the text inside your combo box will not be editable, it is still selectable. However, given the poor quality and complexity of every alternative I've found to date, this is probably the best option out there.

No one said a pure xaml solution has to be complicated. Here's a simple one, with 1 data trigger on the text box. Margin and position as desired

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

I believe a watermark as mentioned in this post would work well in this case

There's a bit of code needed but you can reuse it for any combobox or textbox (and even passwordboxes) so I prefer this way

Only set the IsEditable attribute to true

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

I would recommend the following:

Define a behavior

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

User the behavior

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

I am using an IsNullConverter class in my project and it worked for me. here is the code for it in c#,create a folder named Converter and add this class in that folder,as the trigger used doesnt supports value for rather than null,and IsNullConverter just do that

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

add the namespace in xaml file like this.

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

means

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

use this line below the resources to make it availabe through xaml code

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

here is the xaml code,i used here the trigger so whenever an item is selected in the combobox the visibilty of your text becomes false.

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

I know this is semi old but what about this way:

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


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

IceForge's answer was pretty close, and is AFAIK the easiest solution to this problem. But it missed something, as it wasn't working (at least for me, it never actually displays the text).

In the end, you can't just set the "Visibility" property of the TextBlock to "Hidden" in order for it to be hidden when the combo box's selected item isn't null; you have to SET it that way by default (since you can't check not null in triggers, by using a Setter in XAML at the same place as the Triggers.

Here's the actual solution based on his, the missing Setter being placed just before the Triggers:

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

HappyNomad's solution was very good and helped me eventually arrive at this slightly different solution.

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

Easiest way is to use CompositeCollection to merge default text and data from database directly in ComboBox e.g.

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

And in Resources define StaticResource to bind ComboBox options to your DataContext, because direct binding in CollectionContainer doesn't work correctly.

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

This way you can define your ComboBox options only in xaml e.g.

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

//XAML Code

// ViewModel code

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


}
}

Based on IceForge's answer I prepared a reusable solution:

xaml style:

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

example of use:

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

A little late but..

A more simple way would be to add a dummy data item to the list with parameter IsDummy=true and make sure it is not HitTestVisable and its hight is 1 pixel (using a Converter) so it wont be seen.

Than just register to SelectionChanged and in it, set the index to the dummy item index.

It works like a charm and this way you don't mess with the style and colors of the ComboBox or your application theme.

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

The above code demonstrates the simplest way to achieve it. After window load, declare the text of the combobox, using the .Text property of the combobox. This can be extended to the DatePicker, Textbox and other controls as well.

EDIT: Per comments below, this is not a solution. Not sure how I had it working, and can't check that project.

It's time to update this answer for the latest XAML.

Finding this SO question searching for a solution to this question, I then found that the updated XAML spec has a simple solution.

An attribute called "Placeholder" is now available to accomplish this task. It is as simple as this (in 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>

I did it before binding the combobox with data from database in codebehind like this -

Combobox.Items.Add("-- Select Team --");
Combobox.SelectedIndex = 0;
  1. Put a label on top of the combobox.

  2. Bind the content of the label to to the combobox Text property.

  3. Set the opacity of the combobox to zero , Opacity=0.

  4. Write default text in the combobox Text property

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