Introduction
This is a very simple custom expander control. The control is called "GroupExpander
".
Background
I don't like the built-in expander control in WPF. Although I can change the control template to give a new look and feel, I thought it will be quicker to create a custom one.
Using the Code
I have a default WPF project "WpfApplication1
". This will create a MainWindow.xaml and cs by default. Add a custom control "GroupExpander
". This will give us a GroupExpander.cs and a Generic.xaml. Create a Header
property in GroupExpander
control and rest of the stuff is done in XAML in Generic.xaml file. I have used "WpfApplication1
" as namespace all across. I have coded the GroupExpander
control to match my existing UI. But the XAML for template is available. Feel free to change.
The code for GroupExpander
control is as follows:
public class GroupExpander : ContentControl
{
public string Header
{
get { return (string)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string), typeof(GroupExpander),
new PropertyMetadata(string.Empty));
static GroupExpander()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(GroupExpander),
new FrameworkPropertyMetadata(typeof(GroupExpander)));
}
}
In the generic.xaml file, we are defining the control template for the GroupExpander
control. We have a Toggle
button to control the visibility of the content presenter. The template of the toggle button has been changed. This has a textblock
to show the header, a polyline to draw angle indicator and a line separator. The XAML in Generic.xaml file to format the control:
<Style TargetType="{x:Type local:GroupExpander}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:GroupExpander}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ToggleButton Grid.Row="0"
Name="PART_ToggleButton" IsChecked="True"
OverridesDefaultStyle="True">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Margin="1,0,1,0">
<Grid.Triggers>
<EventTrigger RoutedEvent="UIElement.MouseEnter">
<BeginStoryboard>
<BeginStoryboard.Storyboard>
<Storyboard Storyboard.TargetName="PART_Rectangle"
Storyboard.TargetProperty="Opacity">
<DoubleAnimation To="1" Duration="0:0:0.01" />
</Storyboard>
</BeginStoryboard.Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.MouseLeave">
<BeginStoryboard>
<BeginStoryboard.Storyboard>
<Storyboard Storyboard.TargetName="PART_Rectangle"
Storyboard.TargetProperty="Opacity">
<DoubleAnimation To="0" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard.Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Rectangle x:Name="PART_Rectangle"
Fill="#EEE" Opacity="0" />
<DockPanel Background="Transparent" Margin="0,5,0,10">
<TextBlock DockPanel.Dock="Left"
FontWeight="Bold" Margin="0,4,0,0"
Text="{Binding Path=Header,
RelativeSource={RelativeSource AncestorType=
{x:Type local:GroupExpander}}}"/>
<Grid DockPanel.Dock="Right" Margin="0,4,0,0"
VerticalAlignment="Center">
<Polyline Points="0,0 4,4 8,0"
Stroke="Black" StrokeThickness="1">
<Polyline.Style>
<Style TargetType="{x:Type Polyline}">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=PART_ToggleButton,
Path=IsChecked }" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Polyline.Style>
</Polyline>
<Polyline Points="0,4 4,0 8,4"
Stroke="Black" StrokeThickness="1">
<Polyline.Style>
<Style TargetType="{x:Type Polyline}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=PART_ToggleButton,
Path=IsChecked }" Value="True">
<Setter Property="Grid.Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Polyline.Style>
</Polyline>
</Grid>
<Separator Margin="4,4,4,0" />
</DockPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>
<Grid Grid.Row="1" x:Name="PART_Grid" Visibility="Collapsed"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
<ContentPresenter Content="{Binding RelativeSource=
{RelativeSource TemplatedParent},Path=Content}" />
</Grid>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=PART_ToggleButton,Path=IsChecked}"
Value="True">
<Setter TargetName="PART_Grid"
Property="Visibility" Value="Visible"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Once you have compiled the project, you will be able to use the control in MainWindow
. I have put some dummy control inside, just to give you an idea. Insert the following XAML inside the <window>
tag. The XAML for MainWindow
is as given below:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<local:GroupExpander Header="Single Customer" Grid.Row="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="ID"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="101"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Name"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="Bob Wang"/>
<Button Grid.Row="2" Content="Save"/>
</Grid>
</local:GroupExpander>
<local:GroupExpander Grid.Row="1" Header="All Customers">
<ListBox>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<TextBlock Text="101"/>
<TextBlock Text="Bob Wang"/>
</StackPanel>
</ListBoxItem>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<TextBlock Text="102"/>
<TextBlock Text="Tim Bret"/>
</StackPanel>
</ListBoxItem>
</ListBox>
</local:GroupExpander>
</Grid>