共用方式為


Writing a Silverlight Content Control

Note: The zip archive below has been updated after Silverlight 2 released to the web. For more details on the changes I made, see this post.

Note: I updated the article and attached code after samcov pointed out some issues with the earlier code.  

This article illustrates the writing of a Silverlight content control. Click here to see it in action. It is part of multi-post series. You can find the next post, which is about Visual Transitions here. If you’d rather just dive into the code and explore it yourself then it is available for download here:

Here's what we are going to build:

Preview

All the content in the panel (including the text, the textbox and the radio button) is defined by the consumer of the control (the page where the panel is embedded). This is the sort of thing that content controls enable in Silverlight. Now lets get on with figuring out how it's done.

For my first Silverlight project recently, I needed to do a panel based interface. I decided to do collapsible panels so that I could present as much information as possible while still giving users a choice to only see what they wanted to. Originally, I started out with a whole bunch of user controls but as you can imagine, updating and maintaining each of those user controls got very tedious very quickly.

The only thing different between these user controls was the actual content in them. This got me thinking. Is there a better way to abstract out the container while still keeping the content different? I found my answer in content controls. The Silverlight button, for instance, is a content control. The wonderful thing about a content control is that you can leave the decision of what is going to go into the control up to the consumer of the control and just concentrate on how the overall control behaves. The content can be anything ranging from simple text to elaborate graphics.

I found some great blog posts and walkthroughs on writing basic content controls for Silverlight 2 Beta 1, but nothing that used the new Visual State Manager that shipped with Silverlight 2 Beta 2. I decided there might be other people interested in doing what I have done so I wrote this post.

The first thing to do when writing a content control is to inherit from the System.Windows.Controls.ContentControl. This class is in the System.Windows.dll assembly. Start with a Silverlight class library project and add your content control class:

public class CollapsiblePanel : ContentControl
{
public CollapsiblePanel()
{
DefaultStyleKey = typeof(CollapsiblePanel);
}
}

We will call our content control CollapsiblePanel because it is a panel that the user can expand or contract at runtime. Notice the DefaultStyleKey property that I set in the constructor. This property identifies the default style for the control and is usually set to the type of the control. This is useful if for instance you want to inherit a control (such as Button) but do not want to create a whole new default style template for it. In such a case, you would just set the DefaultStyleKey to point to the type of the base class.

The next thing to do is set up some template parts and template visual states. Template parts are used identify the types of the named parts that are used to apply control templates. If that line makes little sense, you can blame it on inability to express the concept  It will become clearer exactly what purpose template parts serve as we move forward. Template visual states on the other hand are a way for the template to identify transitions from one state of the control to the other. A button for example will have visual states for events like “mouse over”, “mouse down”, “disabled” etc. Template visual states are a way for the template writer to specify how each of these states should look visually.

For our control we will need a template part that identifies the control that can be clicked to expand or collapse the panel. We will also need the template to identify a container control (such as a Grid or another Panel) that contains the content. Finally, we will need a control that contains the content container. We will see why this is needed a little later.

We only need two visual states, one to represent the expanded state and the other to represent the collapsed state. Visual states can also be grouped. For example, you might want to have one set of visual states that specify what a control’s “mouse over” and “mouse down” events look like when it is enabled. On the other hand, you might want another set of visual states that specify what “mouse over” and “mouse down” look like when the control is disabled. This is where visual state groups come in handy. For our purposes, we’ll just stick with one group we’ll call CommonStates. Enough talk, now let’s look at some code:

[TemplatePart(Name=CollapsiblePanel.ExpandCollapseButton, Type=typeof(FrameworkElement))]
[TemplatePart(Name = CollapsiblePanel.ContentContainer, Type = typeof(Panel))]
[TemplatePart(Name = CollapsiblePanel.PanelContent, Type = typeof(FrameworkElement))]
[TemplateVisualState(GroupName=CollapsiblePanel.CommonStates, Name=CollapsiblePanel.Expand)]
[TemplateVisualState(GroupName = CollapsiblePanel.CommonStates, Name = CollapsiblePanel.Collapse)]
public class CollapsiblePanel : ContentControl
{
private const string ExpandCollapseButton = "ExpandCollapseButton";
private const string ContentContainer = "ContentContainer";
private const string PanelContent = "PanelContent";
private const string CommonStates = "CommonStates";
private const string Collapse = "Collapse";
private const string Expand = "Expand";

private string _RollUpStoryboardName = null;
private string _RollDownStoryboardName = null;
private FrameworkElement _expandCollapseButton;
private Panel _contentContainer;
private FrameworkElement _content;

public CollapsiblePanel()
{
DefaultStyleKey = typeof(CollapsiblePanel);
}
}

Notice how the types of each of the template parts is specified alongside the part’s name. This makes it easy for someone who writes controls to guide the template writer. Be careful not to make the type too restrictive lest you limit the creativity of template writers. Notice for instance how I chose to make the ExpandCollapseButton a FrameworkElement instead of a button. This is because in Silverlight practically all controls and shapes have events for detecting mouse clicks and I did not want to be limited to using buttons for this template part.
Before we go any further I thought I’d discuss how the collapse/expand mechanism would work. When collapsing the panel we want the contents of the panel to disappear. However, rather than scaling the content to a zero size, I wanted to give the impression that it was rolling up behind a title bar. My solution for this was to put all the content inside a container control and set the clipping rectangle of the container control to be the same width/height as the content. This part of the template will be represented by the ContentContainer TemplatePart defined above.

Another container control inside ContentContainer would contain the actual content. This is the TemplatePart called PanelContent. When I want the content to roll up, I simply translate PanelContent up on the Y axis so that all the content ends up outside of the clipping region of ContentContainer. The following illustration represents how this works.

How it works

The gray dashed lines above represent the content when it moves out of the clipping region of the ContentContainer.
Now lets take a look at the OnApplyTemplate method implementation for CollapsiblePanel. The OnApplyTemplate method is a virtual method of ContentControl that is called as soon as the template is applied. This is a good place to get references to the template parts and set up any events that we might want to listen for. Here’s what the code looks like:

public override void OnApplyTemplate()
{
base.OnApplyTemplate();

_expandCollapseButton = GetTemplateChild(ExpandCollapseButton) as FrameworkElement;
_contentContainer = GetTemplateChild(ContentContainer) as Panel;
_content = GetTemplateChild(PanelContent) as FrameworkElement;

if (_contentContainer != null)
{
_contentContainer.SizeChanged += new SizeChangedEventHandler(_contentContainer_SizeChanged);
}

if (_content != null)
{
_content.SizeChanged += new SizeChangedEventHandler(_content_SizeChanged);
}

if (_expandCollapseButton != null)
{
if (_expandCollapseButton is ButtonBase)
{
(_expandCollapseButton as ButtonBase).Click += new RoutedEventHandler(_expandCollapseButton_Click);
}
else
{
_expandCollapseButton.MouseLeftButtonUp += new MouseButtonEventHandler(_expandCollapseButton_MouseLeftButtonUp);
}
}
}

First, we call the base implementation of OnApplyTemplate. Then, we get references to all the template parts using the GetTemplateChild method. We set size changed event handlers for both the content container and the content itself. This is needed so that we can set up the clipping rectangle (on the content container) and the translation animation for the content that I talked about earlier. Finally, we set up mouse events for the expand/collapse button. Notice that we use the Click event if the expand/collapse control inherits from ButtonBase, otherwise we just use the MouseLeftButtonUp event. This is important because controls that inherit from ButtonBase do not let the MouseLeftButtonUp event bubble up. The Click event is where you listen for mouse clicks in this case.

Lets take a look at the implementation of the events defined in OnApplyTemplate to get a better idea of what goes on when each of these events is triggered. The _contentcontainer_SizeChanged event handler simply sets up a clipping rectangle that is the same width/height as the content container:

void _contentContainer_SizeChanged(object sender, SizeChangedEventArgs e)
{
Panel container = sender as Panel;
if (container != null)
{
RectangleGeometry rg = new RectangleGeometry();
Rect r = new Rect(0, 0, container.ActualWidth, container.ActualHeight);
RectangleGeometry clip = new RectangleGeometry();
rg.Rect = r;
container.Clip = rg;
}
}

The _content_SizeChanged event handler is used to set up storyboards that actually show the content rolling up and down (depending upon whether it is expanding or collapsing). Here’s the code:

void _content_SizeChanged(object sender, SizeChangedEventArgs e)
{
FrameworkElement content = sender as FrameworkElement;
if (content != null)
{
TransformGroup tGroup = new TransformGroup();
TranslateTransform translate = new TranslateTransform();
translate.SetValue(FrameworkElement.NameProperty, "RollTransform" + Guid.NewGuid().ToString());
translate.Y = 0;
tGroup.Children.Add(translate);
content.RenderTransform = tGroup;

_RollUpStoryboardName = "RollUp" + Guid.NewGuid().ToString();
_RollDownStoryboardName = "RollDown" + Guid.NewGuid().ToString();

_SetupYTranslationStoryboard(translate, _RollUpStoryboardName, -content.ActualHeight);
_SetupYTranslationStoryboard(translate, _RollDownStoryboardName, 0); }
}

The method first attaches a translate transform to the content’s RenderTransform. The _SetupYTranslationStoryboard method is used to set up the actual storyboard that will translate the content up or down. The method takes a reference to the translate transform, a string containing the name of the storyboard and the value of Y to which the storyboard must animate the transform. To roll up, we simply translate up by the content’s height (negative of the content.ActualHeight) and to roll down we translate Y back to 0. Lets take a look at the _SetupYTranslationStoryboard method:

void _SetupYTranslationStoryboard(TranslateTransform transform, string sbName, double translation)
{
if (Resources.Contains(sbName))
{
Storyboard sb = Resources[sbName] as Storyboard;
DoubleAnimationUsingKeyFrames anim = sb.Children[0] as DoubleAnimationUsingKeyFrames;
SplineDoubleKeyFrame keyFrame = anim.KeyFrames[0] as SplineDoubleKeyFrame;
keyFrame.Value = translation;
}
else
{
Storyboard sb = new Storyboard();
sb.SetValue(NameProperty, sbName);
DoubleAnimationUsingKeyFrames anim = new DoubleAnimationUsingKeyFrames();
sb.Children.Add(anim);
Storyboard.SetTarget(anim, transform);
Storyboard.SetTargetProperty(anim, new PropertyPath("Y"));
anim.BeginTime = new TimeSpan(0, 0, 0);
SplineDoubleKeyFrame keyFrame = new SplineDoubleKeyFrame();
KeySpline spline = new KeySpline();
spline.ControlPoint1 = new Point(0, 1);
spline.ControlPoint2 = new Point(1, 1);
keyFrame.KeySpline = spline;
keyFrame.KeyTime = new TimeSpan(0, 0, 1);
keyFrame.Value = translation;
anim.KeyFrames.Add(keyFrame);
Resources.Add(sbName, sb);
}
}

I like smooth animations, so I use a key spline to slow down the animation towards the end. Now let’s take a look at what the expand/collapse button’s event handlers look like:

void _expandCollapseButton_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
IsExpanded = !IsExpanded;
}

void _expandCollapseButton_Click(object sender, RoutedEventArgs e)
{
IsExpanded = !IsExpanded;
}

These event handlers simply flip the value of the IsExpanded dependancy property (which we will look at in a moment). All the real work of animating between the expanded and contracted states goes on in the property changed callback of the IsExpanded property. Let’s take a look at how IsExpanded is defined:

public bool IsExpanded
{
get { return (bool)GetValue(IsExpandedProperty); }
set { SetValue(IsExpandedProperty, value); }
}

// Using a DependencyProperty as the backing store for IsExpanded. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsExpandedProperty =
DependencyProperty.Register("IsExpanded", typeof(bool), typeof(CollapsiblePanel), new PropertyMetadata(new PropertyChangedCallback(IsExpandedChanged)));

private static void IsExpandedChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
bool expanded = (bool)e.NewValue;
CollapsiblePanel panel = o as CollapsiblePanel;

if (expanded)
{
VisualStateManager.GoToState((o as Control), Expand, true);
if (panel != null && panel._RollDownStoryboardName != null)
{
Storyboard sb = (o as FrameworkElement).Resources[panel._RollDownStoryboardName] as Storyboard;
if (sb != null)
{
sb.Begin();
}
}
}
else
{
VisualStateManager.GoToState((o as Control), Collapse, true);
if (panel != null && panel._RollUpStoryboardName != null)
{
Storyboard sb = (o as FrameworkElement).Resources[panel._RollUpStoryboardName] as Storyboard;
if (sb != null)
{
sb.Begin();
}
}
}
}

Well, I did say “real work” but our new friend the VisualStateManager makes going from one state to the other a snap! As you can see, the IsExpanded property is a boilerplate dependancy property. If the new value is true, the property changed callback simply calls the VisualStateManager to go to the Expand state and runs the RollDown storyboard defined by _content_SizeChanged earlier. If the new value is false, the property changed callback does the opposite. Easy! :)

We will also be needing another dependancy property to show a title in the panel’s title bar. Here’s the code for the Title property:

public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}

// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(CollapsiblePanel), null);

The last (and some might say, most significant) bit is setting up a default template for our CollapsiblePanel control. Here’s what the XAML looks like:

<ResourceDictionary
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Knowledgecast.Controls"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
>

<vsm:Style TargetType="local:CollapsiblePanel">
<vsm:Setter Property="Template">
<vsm:Setter.Value>
<ControlTemplate TargetType="local:CollapsiblePanel">
<Grid>
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="CommonStates">
<vsm:VisualState x:Name="Collapse">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ArrowAngleTransform" Storyboard.TargetProperty="Angle" BeginTime="00:00:00">
<SplineDoubleKeyFrame KeyTime="00:00:01" Value="0">
<SplineDoubleKeyFrame.KeySpline>
<KeySpline ControlPoint1="0,1" ControlPoint2="1,1"/>
</SplineDoubleKeyFrame.KeySpline>
</SplineDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Expand">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ArrowAngleTransform" Storyboard.TargetProperty="Angle" BeginTime="00:00:00">
<SplineDoubleKeyFrame KeyTime="00:00:01" Value="90">
<SplineDoubleKeyFrame.KeySpline>
<KeySpline ControlPoint1="0,1" ControlPoint2="1,1"/>
</SplineDoubleKeyFrame.KeySpline>
</SplineDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid x:Name="ExpandCollapseButton" Height="20" Grid.Row="0">
<Path x:Name="TitleBack" Opacity="0.8" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch" Stretch="Fill" StrokeThickness="0.5" Data="M12.5,7 C47.333332,7 115.85664,7 117,7 C118.14336,7 122.1255,6.7291665 122.25,12 C122.3745,17.270834 122.25,18.333334 122.25,21.5 L12.5,21.5 z">
<Path.Fill>
<RadialGradientBrush GradientOrigin="0.699000000953674,0.792999982833862">
<RadialGradientBrush.RelativeTransform>
<TransformGroup>
<ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="1.4" ScaleY="2.188"/>
<SkewTransform CenterX="0.5" CenterY="0.5"/>
<RotateTransform CenterX="0.5" CenterY="0.5"/>
<TranslateTransform X="0.017" Y="0.009"/>
</TransformGroup>
</RadialGradientBrush.RelativeTransform>
<GradientStop Color="#FF00008B" Offset="1"/>
<GradientStop Color="#FFADD8E6" Offset="0"/>
</RadialGradientBrush>
</Path.Fill>
</Path>
<TextBlock Cursor="Arrow" HorizontalAlignment="Stretch" Margin="27.75,2.75,-5,1.75" VerticalAlignment="Stretch" FontFamily="Verdana" FontSize="11" FontStyle='Normal' FontWeight='Normal' Foreground='#FFFFFFFF' Text='{TemplateBinding Title}' Opacity='1' x:Name='Title'/>
<Path x:Name="Arrow" HorizontalAlignment="Left" Margin="5.85300016403198,2.81200003623962,0,3.29800009727478" VerticalAlignment="Stretch" Width="10.685" Fill="#FFFFFFFF" Stretch="Fill" Stroke="#FF000000" StrokeThickness="0" Data="M182.75038,211.50015 L216.5,234.50017 L182.81238,257.87216 z" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<TransformGroup>
<RotateTransform x:Name="ArrowAngleTransform" Angle="90"/>
</TransformGroup>
</Path.RenderTransform>
</Path>
</Grid>
<Grid x:Name="ContentContainer" Grid.Row="1">
<Border x:Name="PanelContent" BorderThickness="0">
<Grid>
<Rectangle Opacity="0.6" Stroke="Gray" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Rectangle Opacity="0.6" Stroke="Black" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Rectangle.RenderTransform>
<TranslateTransform X="-1" Y="-1" />
</Rectangle.RenderTransform>
</Rectangle>
<ContentPresenter />
</Grid>
</Border>
</Grid>
</Grid>
</ControlTemplate>
</vsm:Setter.Value>
</vsm:Setter>
</vsm:Style>
</ResourceDictionary>

Pay close attention to where each of the template parts and visual states appear. Let’s take this from the top. The expand and contract visual states define storyboards that manipulate the angle of a rotate transform. If you scroll down and look at a Path element called Arrow defined in the ExpandCollapseButton you will see that this angle transform belongs to that path. The Path is simply an arrow that is rendered inside the expand/collapse title bar.

The whole layout is defined inside a Grid container. The ExpandCollapseButton is in fact a Grid which sits in the first row of the container grid. The ContentContainer (below the ExpandCollapseButton) is another Grid that sits in the second row. At runtime, we will be applying a clipping rectangle to the ContentContainer grid. The PanelContent is a Border container. Aside from a couple of rectangles for visual effect, it also contains a ContentPresenter control. The ContentPresenter is the heart of a ContentControl. This is where all the content that the user provides for this control goes.

To set this up as the default template for our control we are going to put the XAML in a file called generic.xaml that is part of the project that contains the control class. We will also set the Build Action on this file to "Page" and Custom Tool to "MSBuild:MarkupCompilePass1". This should tell Silverlight to render the control using this template when none is specified by the consumer.

To close, lets take a look at a very simple example of the CollapsiblePanel’s use:

<UserControl x:Class="Knowledgecast.DemoApplication.Page"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:knowledgecast="clr-namespace:Knowledgecast.Controls;assembly=Knowledgecast.Controls"
Width="220" Height="200">
<Grid x:Name="LayoutRoot" Background="White">
<knowledgecast:CollapsiblePanel Margin="10,10,10,10" Title="Collapse/Expand" FontSize="10">
<Grid>
<StackPanel Margin="4,2,2,2" Orientation="Vertical">
<TextBlock Text="This is a content control" />
<TextBlock Text="It can contain anything" />
<TextBox Width="100" Margin="0,10,0,10" Text="a textbox" />
<RadioButton Width="100" Content="a radio button" />
<TextBlock Text="Or that graphic in the background" />
<TextBlock Margin="0,20,0,0" Text="Click the title bar to expand/collapse" />
</StackPanel>
</Grid>
</knowledgecast:CollapsiblePanel>
</Grid>
</UserControl>

All the code for this post along with examples is available here:

The next post in this series which explains the Visual Transitions used in the collapsible panel is here.

CollapsiblePanel.zip

Comments

  • Anonymous
    July 16, 2008
    Very good! Is there a way to change the orientation of your control, so that the content can come out from the side?  Or would we have to do a rotate transform to get that effect?

  • Anonymous
    July 16, 2008
    Just thought of another couple of items.

  1.  How do you start this control out in collapsed state.
  2.  Do you have to create a seperate control for each "TitleBack", or should there be a property to set the GradientStop's? Thanks.
  • Anonymous
    July 16, 2008
    Oops, major problem. I placed two controls into a stackpanel, and boom, it blows up in _content_SizeChanged.

  • Anonymous
    July 16, 2008
    I tried out placing two panels inside a stackpanel and sure enough, it threw an exception. I add a translation and a couple of storyboards at runtime and because all the added elements become part of the page's visual tree I realised I'd have to make their names unique. I'm working on that. As for horizontal display and starting out collapsed I consciously left them out to keep things simple. Don't worry though. I intend to make another post adding those features soon as I'm done testing them. Thanks for pointing these things out. Expect a resolution on the stackpanel issue real soon (probably today).

  • Anonymous
    July 17, 2008
    Thanks arunjeetsingh, I'll be interested in how you do that since the name property seems to be read only. I haven't found a way to do that in code, though it's easy in XAML(I admit I haven't looked hard either). Sam...

  • Anonymous
    July 17, 2008
    Hi samcov. The Name property is a dependancy property implemented by the FrameworkElement abstract class. Most Silverlight elements inherit from this class directly/indirectly. To set it's value from code: object.SetValue(FrameworkElement.NameProperty, "objectName"); Enjoy!

  • Anonymous
    July 21, 2008
    Thanks for the fix, and special thanks for clearing how to set the name property. I never understood two things about that.

  1.  Why the name was read only
  2.  Why you need it at all when you have an object instance. However, I'm sure that under the covers, there is a reason that has to do with staying in sync with the XAML.
  • Anonymous
    July 21, 2008
    This post is part of series that demonstrates how to write a Silverlight 2 content control. The first

  • Anonymous
    July 21, 2008
    Hi samcov. To answer your questions:

  1. Name is a dependancy property. It would be nice if the property definition provided a setter that invoked SetValue like we do. However, it does not at the moment, so this is how we do it :)
  2. I don't really need a name for the translate transform but I like to name things I add at runtime so I can dig them out later if I have to. I do need names for the storyboards because I don't want to hold on to object references unnecessarily. By the way, I fixed the starting out in collapsed state problem. I set it up on purpose so I could cover Visual Transitions. Let me know what you think.
  • Anonymous
    July 24, 2008
    Thanks Arunjeet for the great sample! Couple questions -
  1. the IsExpanded property doesn't seem to change the expanded / collapsed state - how do I start out in the collapsed state?
  2. When placing multiple CollapsiblePanel controls in a stack panel, I was expecting it to work like an accordian control. When a panel is collapsed, the control beneath it doesn't slide up - is this not the way it was designed? Can you offer any suggestions on how to change this?
  • Anonymous
    July 24, 2008
    Arunjeet, I don't understand what you mean when you said you fixed the collapsed state problem, I still can't start out collapsed. Also, one thing I noticed, and I'm not quite sure how to fix yet, is this.
  1.  I start out with 2 controls in a StackPanel.
  2.  If I expand and collapse each, they seem to hold on to their original size, i.e., there is a big space where you would want the entire structure to collapse.
  3.  I tried to dynamically add additional content, and it works well, but again, it holds on to the full size required to display the content. It would be nice to use this in a portal type situation where the content could be dynamically sized down when the content isn't visible.
  • Anonymous
    July 24, 2008
    @J Williamson
  1. This is a bug in the code that accompanies this post. I did another post fixing that issue and also explained the concept of Visual Transition in the process. That post is available here: http://blogs.msdn.com/knowledgecast/archive/2008/07/22/using-visualtransition-with-a-silverlight-content-control.aspx. I updated the code to fix the issue. Go ahead and download that zip file again. It should have the latest code now.
  2. If you set the Visibility property of the content container to Collapsed at the end of the collapse storyboard the panel should give way to whatever is below it. However, this will be abrupt and not a smooth sliding animation which is what you're probably looking for. To get the accordion effect to happen, I ended up writing my own layout container (like StackPanel). I'm working on cleaning up that code and it should be up here soon. Patience, my young padawan! :D Arun
  • Anonymous
    July 24, 2008
    The comment has been removed

  • Anonymous
    July 24, 2008
    Arunjeet, it looks like we're hitting you all at once, lol. Please take your time, I understand having a lot on your plate.  The good news is that your work is getting popular, the bad news is... well you know. Thanks, Sam...

  • Anonymous
    October 22, 2008
    The comment has been removed

  • Anonymous
    October 24, 2008
    Hi Humberto. Are you using Silverlight 2 RTW? This control was built for Silverlight 2 Beta 2. Things might have changed in RTW and that is probably why you're seeing this behaviour. I'm working on porting the control to RTW and that should be up soon. In the meantime if you want to send me a sample project where this happens I'll be happy to look into it. The address is arunjeet.singh at hotmail.com.

  • Anonymous
    November 27, 2008
    This was something that had been bothering me for a while. Silverlight 2 came out almost two months ago

  • Anonymous
    December 09, 2008
    Hi, i wanted to use your panel as a navigation bar. The goal is that, one clicked, it stays on top of the html page and disappears when collapsed. I think the key is in using zindex for the containing divs like this:


<div style='z-index:2; position: absolute; left:0px; top:0px'>      <asp:Silverlight ID="Xaml1" runat="server"              Source="~/ClientBin/Knowledgecast.DemoApplication.xap"MinimumVersion="2.0.30523" Width="100%" PluginBackground="White" Windowless="True"                  /> </div> <div style='z-index:1; position: absolute; left:0px; top:30px'>            // HTML page here  </div>

Problem is: how do i dynamically set zindex? I can't access the div containers from the user control code behind. Also if you have any suggestion on how to make the html page fade when the menu is expanded and get back to normal when collapsed that would be great.

  • Anonymous
    February 09, 2009
    The comment has been removed

  • Anonymous
    April 15, 2009
    Hai Arunjeet, i am beginner to silverlight controls. I need a collapsable panlel in my application. I found your post on the net. i used the  example of the CollapsiblePanel’s u hv provided at the end of your post to try it out. But, only the text and the controls are displayed. How can i collapse and expand. Am i missing anythin

  • Anonymous
    October 09, 2010
    Hi Arunjeet, Did anyone try to embed your collapsiblePanel  control as content inside youre collapsiblePanel. When the content of the collapsiblePanel changes in size it will not update its parents and/or children resulting in area's not visible or other visual flaws. I looked everywhere, but I cannot seem to find a way to access the content inside the contentpresenter to be able to update the parent(s) about their content size changes. I hope you could help me on this one. Thanks in advance. With kind regards, Magiel