WPF combobox with image in case of null value - wpf

I would like to have a combobox in WPF whose items are of type string (like the default). The only difference is that I want to display an image (error) if the string is x:null. Anyone an idea how I can create an itemtemplate or style to display an image in the case of a null (or empty) string and the string in all other cases in the combobox.
Thanks!
-- Carsten

Here is an example that uses an ItemTemplate and a DataTrigger to only show an image if the item is null:
<Grid>
<Grid.Resources>
<x:Array x:Key="sampleData" Type="sys:String">
<sys:String>abc</sys:String>
<sys:String>def</sys:String>
<sys:String>ghi</sys:String>
<x:Null/>
</x:Array>
</Grid.Resources>
<StackPanel>
<ComboBox ItemsSource="{StaticResource sampleData}" Text="abc">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding}"/>
<Image x:Name="errorImage" Source="http://thecybershadow.net/misc/stackoverflow.png" Visibility="Collapsed"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="{x:Null}">
<Setter TargetName="errorImage" Property="Visibility" Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Grid>
and it looks like this where the fourth null item is displayed as a stack overflow image:

Ok, here is the remaining piece in the style combobox (edit a copy of it and look for ContenPresenter and replace with this):
<Grid>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" IsHitTestVisible="false" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"/>
<Image x:Name="errorImage" Source="/Autodesk.UtilityDesign.Electric.UI;component/Themes/Images/Error.ico" Visibility="Collapsed" Width="16"/>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="SelectedItem" Value="{x:Null}">
<Setter TargetName="errorImage" Property="Visibility" Value="Visible"/>
</Trigger>

Related

RenderSize of Grid control have difference in Windows 10 [.NET Framework 4.6.2] and Windows 7[.NET Framework 4]

I have a custom combo box style as below
<Style x:Key="SmallArrowComboBoxStyle" TargetType="{x:Type ComboBox}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
<Setter Property="Background" Value="#FF1E1E1E"/>
<Setter Property="BorderBrush" Value="#454545"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="FontFamily" Value="Segoe UI,Meiryo UI" />
<Setter Property="Background" Value="#1C1C1C" />
<Setter Property="Foreground" Value="#FFFFFF" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid x:Name="MainGrid1" SnapsToDevicePixels="true" Margin="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup1" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" Margin="1,0,1,1" Placement="Bottom" >
<Border x:Name="Shdw1" BorderBrush ="#FF4289C4" Background="#FF4289C4" BorderThickness="1" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=MainGrid1}">
<Border x:Name="DropDownBorder" BorderThickness="0" Background="#1E1E1E">
<Grid>
<ScrollViewer x:Name="DropDownScrollViewer1" Template="{DynamicResource ScrollViewerControlTemplate1}">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect1" Fill="{Binding Background, ElementName=DropDownBorder}"
Height="{Binding ActualHeight, ElementName=DropDownBorder}" Width="{Binding ActualWidth, ElementName=DropDownBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter1" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ScrollViewer>
<Slider x:Name="CustomSliderVertical" Margin="0,0,-12,0" Maximum="100" SmallChange="1" LargeChange="10" TickPlacement="BottomRight" TickFrequency="10"
Style="{StaticResource CustomSliderStyle}" Foreground="{DynamicResource LimeBrush}" HorizontalAlignment="Right" Delay="1"
Orientation="Vertical" Focusable="False" Grid.Column="0" Grid.Row="1" Panel.ZIndex="1"/>
</Grid>
</Border>
</Border>
</Popup>
<ToggleButton BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}" Grid.ColumnSpan="2"
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Style="{StaticResource SmallArrowComboBoxToggleButton}" BorderThickness="{TemplateBinding BorderThickness}"/>
<ContentPresenter ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" Name="ContentSite"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Content="{TemplateBinding SelectionBoxItem}"
ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
IsHitTestVisible="false" Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
<TextBox x:Name="PART_EditableTextBoxSmall" Width="150" Height="40"
Style="{x:Null}"
Template="{StaticResource ComboBoxTextBoxSmall}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3,3,23,3"
Focusable="True"
Background="Transparent"
Visibility="Hidden"
IsReadOnly="{TemplateBinding IsReadOnly}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter TargetName="PART_EditableTextBoxSmall" Property="Visibility" Value="Visible"/>
<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>
<Setter Property="Foreground" TargetName="PART_EditableTextBoxSmall" Value="#FFFFFF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I prepared a test application with a combo box and applied this style to the combo box.
Prepared the test application with
.NET Framework 4 and 4.6.2
Tested the appearance of combo box using both
Windows-10(.NET Framework 4.6.2) and Windows-7(.NET Framework 4)
But there is an appearance difference between Windows 10 and Windows 7.
That is the alignment of combo box text is different.
That is padding of combo box is different in Windows 10 and Windows 7. But these padding values are not explicitly set in the code.
In Windows 7, the padding value is ‘4,3,4,3’
In Windows 10, the padding value is ‘6,3,5,3’
While checking, it is observed that the RenderSize of "MainGrid1" (grid layout control from the above style code) is different in Windows 10 and Windows 7.
From MSDN, RenderSize is the final render size of an element.
My doubt is why this RenderSize have different values in Windows 10 and Windows 7 ?
Windows 10 and Windows 7 use different default themes for WPF controls. Things like buttons and ComboBoxes will look slightly different unless you override the default template and/or change the necessary properties. You can also use Expression blend to "dump" out the default template to see details.
There are a number of other questions about themes if you do a little searching on StackOverflow. ie:
where-to-find-wpf-classic-theme-as-xaml

How to have buttons with the same width inside a ListView/WrapPanel?

I have a ListView that shows a list of buttons inside a WrapPanel. The text inside the buttons may vary so the width of the buttons may also vary.
I'd like to have all the buttons to have same width, meaning I want all the buttons to have the width of the larger button inside the WrapPanel. Is there a way to do that?
Here's my ListView:
<ListView Margin="8 10 8 8"
VerticalAlignment="Top"
ScrollViewer.CanContentScroll="True"
Background="Transparent"
x:Name="ResourcesList"
BorderThickness="0"
ItemContainerStyle="{DynamicResource LvItemStyle}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding CadResource.Values}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel HorizontalAlignment="Left" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<ItemContainerTemplate>
<Button Content="{Binding ResourceAlias.Value}"
x:Name="ResourceButton"
Command="{Binding ElementName=ResourcesList, Path=DataContext.SelectResourceCommand}"
CommandParameter="{Binding}"
VerticalContentAlignment="Stretch"
BorderThickness="0"
Margin="2"
MinWidth="75"
Template="{DynamicResource ResourceButtonTemplate}"
Foreground="{DynamicResource ResouceAliasText}"
Background="{Binding ResourceAlias.BackColor, Mode=TwoWay, Converter={converter:StringFormatConverter}}">
<Button.ContextMenu>
<ContextMenu Name="ContextMenu"
ItemsSource="{Binding ContextMenu}"
ItemContainerStyle="{StaticResource MenuItemStyle}" />
</Button.ContextMenu>
</Button>
</ItemContainerTemplate>
</ListView.ItemTemplate>
</ListView>
And here's the template for the button that I'm using:
<ControlTemplate x:Key="ResourceButtonTemplate"
TargetType="{x:Type ButtonBase}">
<Border x:Name="border"
Background="{DynamicResource ResourceButtonBackground}"
BorderThickness="1"
Height="20"
BorderBrush="{DynamicResource ResourceButtonBorder}"
SnapsToDevicePixels="True">
<Border BorderThickness="0 1 0 0"
Background="Transparent"
BorderBrush="{DynamicResource ResourceHighlight}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<images:ResourceSquare Fill="{TemplateBinding Background}"
Grid.Column="0"
Style="{StaticResource ViewBoxStyleChildWindow13X13}"
x:Name="ResourceSquare"
HorizontalAlignment="Left"
Margin="3 2 0 2" />
<images:ResourceTriangle Fill="{TemplateBinding Background}"
Grid.Column="0"
Style="{StaticResource ViewBoxStyleChildWindow13X13}"
x:Name="ResourceTriangle"
Visibility="Collapsed"
HorizontalAlignment="Left"
Margin="3 2 0 2" />
<ContentPresenter x:Name="contentPresenter"
Grid.Column="1"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
ContentStringFormat="{TemplateBinding ContentStringFormat}"
Focusable="False"
TextBlock.FontSize="{Binding FontSize}"
Margin="7 0 7 0"
RecognizesAccessKey="True"
HorizontalAlignment="Center"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
<images:ResourceConnected Fill="{DynamicResource IconToolbar}"
Grid.Column="2"
Style="{StaticResource ViewBoxStyleChildWindow10X10}"
HorizontalAlignment="Right"
VerticalAlignment="Top" />
</Grid>
</Border>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected.Value}"
Value="True">
<Setter TargetName="border"
Property="Background"
Value="{DynamicResource ListSelectedLine}" />
<Setter TargetName="border"
Property="BorderBrush"
Value="{DynamicResource BorderHighLight}" />
</DataTrigger>
<Trigger Property="Button.IsDefaulted"
Value="True">
<Setter Property="BorderBrush"
TargetName="border"
Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
</Trigger>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
TargetName="border"
Value="{DynamicResource CadMouseOver}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
So here's what I did:
I added an event handler for SizeChanged on the WrapPanel
<WrapPanel HorizontalAlignment="Left" SizeChanged="WrapPanelSizeChanged" />
And the WrapPanelSizeChanged is as follows:
private void WrapPanelSizeChanged(object sender, EventArgs e)
{
var panel = sender as WrapPanel;
if(panel == null)
return;
if( panel.Children.Count == 0 )
return;
var itemWidth = panel.Children.OfType<FrameworkElement>().Max(x => x.ActualWidth);
panel.ItemWidth = itemWidth;
}
Well, the control you need is called WrapGrid. It is present in Windows Store apps, but not in WPF. Somebody calls it UniformWrapPanel. See this please:
Is there a WPF "WrapGrid" control available or an easy way to create one?
Please note, that WrapPanel has ItemWidth property. If you set it, all child elements will have this width. It would be very easy, if you could set it to fixed value.
You can try following. Add items to wrappanel, then measure theirs sizes, select widest element and set it's ActualWidth to WrapPanel.ItemWidth property. Then all elements should have the same width

Display same content twice in WPF Button

We want to style a wpf button so that the content set in the button is displayed twice. The reason for this is that we want to achieve a drop shadow effect of the button's content. Out thought was to have two ContentControls in the Button style like below:
<ContentControl x:Name="ContentControl" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
<ContentControl Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Foreground="White" Margin="0,1,0,0" />
So, one ContentControl is for displaying the real content and one ContentControl is for displaying the same content with a little margin so that it gives the effect of being the drop shadow. The problem is that it doesn't show content in both content controls. Only one of them shows content. How can I successfully show content in both content controls?
Also, the dropshadow effect is not an option since the button's content becomes blurry.
Thanks for help!
Buttons with Nested Content:
<Style x:Key="ShadowButton"
TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Rectangle Width="{Binding ActualWidth,
ElementName=presenter}"
Height="{Binding ActualHeight,
ElementName=presenter}">
<Rectangle.Fill>
<VisualBrush AlignmentX="Left"
Stretch="None"
Visual="{Binding ElementName=presenter}" />
</Rectangle.Fill>
<Rectangle.RenderTransform>
<TranslateTransform X="3"
Y="3" />
</Rectangle.RenderTransform>
</Rectangle>
<!-- You can replace the following line to a ContentControl if you absolutely have to -->
<ContentPresenter x:Name="presenter"
ContentSource="Content" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Usage can then be dynamic like:
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="36"
Style="{StaticResource ShadowButton}">
<StackPanel>
<Button Margin="2"
Content="A" />
<Button Margin="2"
Content="B" />
<TextBox Margin="2"
Text="Blah" />
</StackPanel>
</Button>
By using a VisualBrush your not having 2 ContentControl / ContentPresenter in your Style and are just rendering one into a Brush to fill a rectangle and get your effect.
Duplicating Visual Tree with a Template
Try to have a UserControl do this than a Button in the first place. You need to use a Template if you want to duplicate the Visual Tree in your style.
<Style x:Key="ShadowButton"
TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<ContentControl x:Name="shadow"
ContentTemplate="{TemplateBinding ContentTemplate}"
Foreground="SpringGreen">
<ContentControl.RenderTransform>
<TranslateTransform X="50"
Y="50" />
</ContentControl.RenderTransform>
</ContentControl>
<ContentControl x:Name="presenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Foreground="SlateBlue" />
</Grid>
<ControlTemplate.Triggers>
<Trigger SourceName="presenter"
Property="IsMouseOver"
Value="True">
<Setter TargetName="shadow"
Property="Foreground"
Value="Teal" />
<Setter TargetName="presenter"
Property="Foreground"
Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and usage:
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Style="{StaticResource ShadowButton}">
<Button.ContentTemplate>
<DataTemplate>
<StackPanel>
<Button Margin="2"
Content="A"
Foreground="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContentControl}},
Path=Foreground}" />
<TextBox Margin="2"
Foreground="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContentControl}},
Path=Foreground}"
Text="Blah" />
</StackPanel>
</DataTemplate>
</Button.ContentTemplate>
</Button>
I tried this in a small dummy application and it works just fine. See if this is what you want.
<Window.Resources>
<Style x:Key="test" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<ContentControl Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"/>
<ContentControl Foreground="DarkGray"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}">
<ContentControl.RenderTransform>
<TranslateTransform Y="2" X="2"/>
</ContentControl.RenderTransform>
</ContentControl>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button Style="{StaticResource test}">
Test
</Button>
</Grid>

ItemsControl where entire item is expander for sub-items

So it's kind of hard to describe the behavior I want, so I drew a nice picture with my amateur paint skills.
So basically I want the headings of the ItemsControl to operate like an expander, without the ugly expander icon that comes along (so just clicking anywhere in the box would expand to see the sub items). I already have the data-hierarchy in place, but I cannot for the life of me get the expander to behave as I want, anybody had any success overriding expander styles to accomplish something like this, or is there another control that can accomplish something like this easier? Here's some simple code, but has the ugly expander button, overriding the headertemplate and style of the expander has only made it look much worse.
<ItemsControl ItemsSource="{Binding Collection}"
HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="Heading">
<ItemsControl ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="Sub-Heading">
<ListBox ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch"/>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You just have to use a custom style for your expander if you don't like the built in style :)
Here is a start :
<Style x:Key="customExpander">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="Expander">
<DockPanel>
<ToggleButton DockPanel.Dock="Top"
IsChecked="{Binding Path=IsExpanded,Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Background="{TemplateBinding Background}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
Foreground="{TemplateBinding Foreground}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontStretch="{TemplateBinding FontStretch}"
FontStyle="{TemplateBinding FontStyle}"
FontWeight="{TemplateBinding FontWeight}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Padding="{TemplateBinding Padding}"
Name="HeaderSite"
MinWidth="0"
MinHeight="0"
Margin="1,1,1,1">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<TextBlock Text="{TemplateBinding Content}" Background="{TemplateBinding Background}" />
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
<ContentPresenter Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentStringFormat="{TemplateBinding ContentStringFormat}"
Name="ExpandSite" Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Visibility="Collapsed"
Focusable="False"
DockPanel.Dock="Bottom" />
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="HeaderSite" Property="Background" Value="Gold" />
<Setter TargetName="ExpandSite" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" Value="{DynamicResource DisabledTextBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then change your xaml to use this style :
<ItemsControl ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="Heading" Style="{StaticResource customExpander}" Background="LightSteelBlue" >
<ItemsControl ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="Sub-Heading" Style="{StaticResource customExpander}" Background="LightSalmon">
<ListBox ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch"/>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
EDIT The important part is the
<ControlTemplate TargetType="ToggleButton">
<TextBlock Text="{TemplateBinding Content}" Background="{TemplateBinding Background}" />
</ControlTemplate>
if you want to customize. This is a very raw solution so you have plenty of room to enhance it ;)

'Default' text for templated combo box

I have a combo box that is based on a data template the includes check boxes like such:
<ComboBox x:Name="cboComplex" Text="Select days...">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsSelected}" Width="20"/>
<TextBlock Text="{Binding DayOfWeek}" Width="100" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The problem I'm having is that I'd like the combobox to display "Select days..." and then show the list when clicked. Unfortunately setting the Text property seems to have no effect. Any ideas or help would be greatly appreciated.
Thanks in advance!
Sieg
The Text property is used when the ComboBox is editable. You can set a default "Select" type message by adding an element to the ControlTemplate that shows up only when there is no selection and then disappears. Using this method you don't need to worry about modifying your collection or having a user try to pick the "Select" message from the list because it isn't part of the list. I'd recommend using the Tag to set your message on each instance or in a Style and then adding a new TextBlock to display it into the default template:
<TextBlock x:Name="SelectMessage" HorizontalAlignment="Left" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Tag}" VerticalAlignment="Center" Margin="{TemplateBinding Padding}" Visibility="Collapsed"/>
Then you could use it like this:
<ComboBox ItemsSource="{Binding MyList}" Template="{StaticResource ComboBoxMessageTemplate}" Tag="Select days..."/>
Here's a complete Blend generated copy of the default Aero ComboBox template with the changes. You'll also need the theme namespace (xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero") and a reference to the PresentationFramework.Aero assembly:
<Geometry x:Key="DownArrowGeometry">M 0 0 L 3.5 4 L 7 0 Z</Geometry>
<Style x:Key="ComboBoxReadonlyToggleButton" TargetType="{x:Type ToggleButton}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Focusable" Value="false"/>
<Setter Property="ClickMode" Value="Press"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Microsoft_Windows_Themes:ButtonChrome x:Name="Chrome" SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}">
<Grid HorizontalAlignment="Right" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}">
<Path x:Name="Arrow" Fill="Black" HorizontalAlignment="Center" Margin="3,1,0,0" VerticalAlignment="Center" Data="{StaticResource DownArrowGeometry}"/>
</Grid>
</Microsoft_Windows_Themes:ButtonChrome>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="RenderPressed" TargetName="Chrome" Value="true"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Fill" TargetName="Arrow" Value="#AFAFAF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="ComboBoxMessageTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="MainGrid" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" Margin="1" AllowsTransparency="true" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" Placement="Bottom" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Grid.ColumnSpan="2">
<Microsoft_Windows_Themes:SystemDropShadowChrome x:Name="Shdw" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=MainGrid}" Color="Transparent">
<Border x:Name="DropDownBorder" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1">
<ScrollViewer CanContentScroll="true">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.DirectionalNavigation="Contained"/>
</ScrollViewer>
</Border>
</Microsoft_Windows_Themes:SystemDropShadowChrome>
</Popup>
<ToggleButton Style="{StaticResource ComboBoxReadonlyToggleButton}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"/>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" IsHitTestVisible="false" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"/>
<TextBlock x:Name="SelectMessage" HorizontalAlignment="Left" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Tag}" VerticalAlignment="Center" Margin="{TemplateBinding Padding}" Visibility="Collapsed"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="SelectedItem" Value="{x:Null}">
<Setter Property="Visibility" TargetName="SelectMessage" Value="Visible"/>
</Trigger>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
<Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/>
<Setter Property="Color" TargetName="Shdw" Value="#71000000"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Height" TargetName="DropDownBorder" Value="95"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
<Setter Property="Background" Value="#FFF4F4F4"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Show default text in combo box
<ComboBox Height="23" HorizontalAlignment="Left" Margin="180,18,0,0" Name="cmbExportData" VerticalAlignment="Top" Width="148" ItemsSource="{Binding}" Text="-- Select Value --" AllowDrop="False" IsEditable="True" IsManipulationEnabled="False" IsReadOnly="True" />
Set the text property of Combo Box
Mark it as IsEditable = true
Mark it as IsReadOnly = true
You will have to create a new Item in your underlying collection class with the value "Select Days...", with index[0] and change the selected index to 0.
<ComboBox x:Name="cboComplex" SelectedIndex="0">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsSelected}" Width="20"/>
<TextBlock Text="{Binding DayOfWeek}" Width="100" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Or the other option is to put a label on top of it with the text "Select Days...", and then listen to OnSelectionChanged event, and if the SelectedIndex is not -1, change the labels visibility to false, otherwise true. e.g.
private void MyListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (MyListBox.SelectedIndex >= 0)
{
MyListBoxInitialLabel.Visibility = Visibility.Hidden;
}
else
{
MyListBoxInitialLabel.Visibility = Visibility.Visible;
}
}

Resources