Welcome to RioterDeckers' HeadQuarter Sign in | Join | Help

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)


Published Friday, May 19, 2006 3:14 AM by Chaz
Filed Under:

Comments

No Comments
Anonymous comments are disabled