Enter the expander
In waiting for the next post about the
WinFX wizard framework (stay calm, it's in progress), I decided to post a simple entry about a piece of my work on the Massaï project: a custom templated
Expander control. But it would be too easy to explain how to customize a lonely poor expander. No! I cannot do that, you know ;)
So here is a custom expander in a Matrix theme.
[Figure 1. Expander everywhere]
First, the goal is to template an expander control to apply particular visual appearance but in keeping the control's features. It's not so easy because once templated, the expand/collapse functionality is not directly available anymore. Just have a look on a piece of the template:
<ControlTemplate x:Key="RDExpander_Template" TargetType="{x:Type l:RDExpander}">
<Grid x:Name="EnterTheMatrix" Width="168" Height="109">
<ContentPresenter x:Name="_expanderContent" Content="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}">
<ContentPresenter.RenderTransform>
<TranslateTransform X="0" Y="0" />
</ContentPresenter.RenderTransform>
</ContentPresenter>
<Image Source="Images/MatrixScreen.png" Width="168" Height="109" />
<Grid Margin="4,2,0,0">
<ContentPresenter x:Name="_header" Content="{Binding Path=Header, RelativeSource={RelativeSource TemplatedParent}}" />
...
</Grid>
</Grid>
...
</ControlTemplate>
Until now, no problem...
I used two ContentPresenter to display the header and the content of the expander.
Just have a look on the final used expander:
<l:RDExpander Margin="2" MouseEnter="_ExpanderEnter">
<l:RDExpander.Header>
<l:MatrixTextBlock Text="Enter my matrix" ImmediateWrite="False" Width="128" Height="Auto" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center" TextWrapping="WrapWithOverflow" />
</l:RDExpander.Header>
<Grid>
<Image Source="Images/MatrixFrame.png" Width="160" Height="101" />
<StackPanel Width="130" Height="70">
<TextBlock Margin="2">
<Hyperlink x:Name="_chazHL" NavigateUri="http://blog.rioterdecker.net/blogs/chaz">Chaz's blog</Hyperlink>
</TextBlock>
</StackPanel>
</Grid>
</l:RDExpander>
If you would try this code, the expander neither expands nor collapsed its content by clicking on the header. So I decided to add a simple Button control in the template. There is another problem. How to bound the click event on the expand/collapse functionality ? One solution consists in adding a RoutedCommand to my custom expander and bound it with the Button:
public partial class RDExpander : Expander
{
public static readonly RoutedCommand ExpandOrCollapseCommand;
static RDExpander()
{
RDExpander.ExpandOrCollapseCommand = new RoutedCommand("ExpandOrCollapse", typeof(RDExpander));
CommandManager.RegisterClassCommandBinding(typeof(RDExpander), new CommandBinding(ExpandOrCollapseCommand, new ExecutedRoutedEventHandler(OnExecuteCommand), new CanExecuteRoutedEventHandler(OnQueryExecuteCommand)));
}
private static void OnExecuteCommand(object target, ExecutedRoutedEventArgs e)
{
RDExpander __expander = (RDExpander)target;
if (e.Command == RDExpander.ExpandOrCollapseCommand)
{
__expander.IsExpanded = !__expander.IsExpanded;
}
}
private static void OnQueryExecuteCommand(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
}
...
<ContentPresenter x:Name="_header" Content="{Binding Path=Header, RelativeSource={RelativeSource TemplatedParent}}" />
<Button x:Name="_matrix" Style="{StaticResource MediaButtonStyle}" Command="l:RDExpander.ExpandOrCollapseCommand" />
...
The expander provides two routed events, Expanded and Collapsed which are raised when the IsExpanded property has changed. So I just need to add triggers on these events to animate the content. To allow the content to be expanded up, I add another RoutedEvent which is raised when the ExpandDirection property is set to Up:
protected override void OnExpanded()
{
switch(base.ExpandDirection)
{
case ExpandDirection.Down:
base.OnExpanded();
break;
case ExpandDirection.Up:
RoutedEventArgs __args = new RoutedEventArgs(ExpandedUpEvent);
base.RaiseEvent(__args);
break;
}
}
For this version, I decided that the control would not support left and right directions. But you can easily add these functionalities...
In the codes above, you certainly noticed that I used another custom control: MatrixTextBlock. This control derives from a simple TextBlock and display text as a typewriter. In the next post, I just extract this control for you. After that, I will add scroll bars in the content of the expander and will change their styles too. As I used to say, download it and eat your ice cream :) !!
Download the sources
Download the binaries
If you have not the right fonts (Agency FB and OCR Extended), download them now.
(WinFX Feb. CTP)