Freigeben über


Silverlight Toolbar

I wanted to build a toolbar where toolbar button expands when you hover over it.

 I will not only admit that I am not a designer, but also admit that I really suck at it when it comes to picking colors :) but that is why I wanted to build toolbar in such a way so that somebody can pick up this code and make it look better by just changing the XAML.

So to build a Toolbar, I needed to build two custom controls...

  1. Toolbar: This is just a container class. This class contains buttons that are added to toolbar.
  2. ToolbarButton: This is actual button class that implements the behavior for the button part as well as the visual animation part.

Toolbar code is straight forward. Since it just a container its XAML just contains a canvas.

 <Canvas xmlns="https://schemas.microsoft.com/client/2007"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        Width="640"
        Height="480" />

The only thing interesting about this class is actual children Collection. Since in Silverlight there is no collection class that actually fires event when an item is added (something like ObservableCollection), you need to implement one. Code here is courtasy Peter Blois. Link to code that Peter gave me are here.  

Only interesting part of the rest of the code is where layout is updated when new item is added to toolbarcollection.

 public Toolbar()
{
    System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream("Toolbar.Toolbar.xaml");
    _rootCanvas = this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd()) as Canvas;
    _toobarcollection = new ObservableCollection<ToolbarButton>();
    _toobarcollection.CollectionChanged += new NotifyCollectionChangedEventHandler<ToolbarButton>(_toobarcollection_CollectionChanged);
    this.Loaded += new EventHandler(Toolbar_Loaded);
}

void _toobarcollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs<ToolbarButton> e)
{
    UpdateLayout();
}

public ObservableCollection<ToolbarButton> ToolBarCollection
{
    get { return _toobarcollection; }
}

 

Now, the ToolbarButton control...

This one has some specific behavior...

  1. From OM perspective Button has three APIs...
    1. Button has tooltip (of type string) that shows up when mouse enters the button
    2. Button has Image property (of type Uri) where consumer of the control can specify the image to be displayed in the button
    3. Button has a Click event which user can hook into
  2. I wanted to Popup animation when mouse enters the button
  3. I wanted to Popdown animation when mouse leaves the button
  4. I also wanted default look of the button to change when the button is pressed.

I wanted to make sure somebody can change the Popup and Popdown as well as pressed/unpressed look of the button later.

Xaml for ToolbarButton contains its default look. It also contains two storyboards called (popup and popdown) in the code, I only refer to those, that means somebody can change the contents of those storyboard and make them look good.

 

 <Canvas.Resources>
        
        
                <Storyboard x:Name="Popup">
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
                        <SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="2"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
                        <SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="2"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
                        <SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="2"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
                        <SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="2"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.Opacity)">
                        <SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="1"/>
                    </DoubleAnimationUsingKeyFrames>
                </Storyboard>
                <Storyboard x:Name="PopDown">
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
                        <SplineDoubleKeyFrame KeyTime="00:00:00" Value="2"/>
                        <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
                        <SplineDoubleKeyFrame KeyTime="00:00:00" Value="2"/>
                        <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
                        <SplineDoubleKeyFrame KeyTime="00:00:00" Value="2"/>
                        <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
                        <SplineDoubleKeyFrame KeyTime="00:00:00" Value="2"/>
                        <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.Opacity)">
                        <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
                        <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.7"/>
                    </DoubleAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.OpacityMask).(SolidColorBrush.Color)">
                        <SplineColorKeyFrame KeyTime="00:00:00" Value="#FF000000"/>
                        <SplineColorKeyFrame KeyTime="00:00:00.3000000" Value="#FFEFF4FA"/>
                    </ColorAnimationUsingKeyFrames>
                </Storyboard>
        
    </Canvas.Resources>

 

 I calls the Begin on those storyboard when mouse enters....

 

 void _rootCanvas_MouseLeave(object sender, EventArgs e)
        {
            _popdown.Begin();
            this.SetValue(Canvas.ZIndexProperty, 0);
            _mousedown = false;
            UnPressedLook();
            _tooltip.Visibility = Visibility.Hidden;
        }

        void _rootCanvas_MouseEnter(object sender, MouseEventArgs e)
        {
            _popup.Begin();
            this.SetValue(Canvas.ZIndexProperty, 5);
            _tooltip.Visibility = Visibility.Visible;
        }

For the pressed look, i create a seperate xaml that contains the brush that defines the pressed look.

 

 <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
  <GradientStop Color="#FF6392C7" Offset="0.125"/>
  <GradientStop Color="#FF6392C7" Offset="0.88"/>
  <GradientStop Color="#FFF8F2F2" Offset="0"/>
  <GradientStop Color="#FFF8F2F2" Offset="1"/>
</LinearGradientBrush>

In the code, this xaml can be read using XamlReader.Load and you can swap the looks when mouse button is down or up.

 

 void _rootCanvas_MouseLeftButtonDown(object sender, MouseEventArgs e)
        {
            _mousedown = true;
            PressedLook();
        }

        void _rootCanvas_MouseLeftButtonUp(object sender, MouseEventArgs e)
        {
            if (this.Click != null && _mousedown == true)
            {
                this.Click(this, EventArgs.Empty);
                _mousedown = false;
            }
            UnPressedLook();

        }

The complete code for ToolBar is here.

 

 

Comments

  • Anonymous
    May 17, 2007
    Silverlight Toolbar [Via: vivekd ] Paste XML as Serializable Type [Via: mwinkle ] Reflector Addins and...

  • Anonymous
    May 20, 2007
    Another week has shot by. Didn&#39;t have any time to look at Silverlight although I have managed to

  • Anonymous
    May 20, 2007
    The comment has been removed

  • Anonymous
    May 22, 2007
    One of the things I'm going to try and start doing is to do a weekly post of useful links on .NET related

  • Anonymous
    May 22, 2007
    One of the things I'm going to try and start doing is a weekly blog post of useful/interesting links

  • Anonymous
    June 03, 2007
    During the weekend I spent some minutes to collect some of the greatest Silverlight examples. Most of

  • Anonymous
    July 19, 2007
    分享几个国外的silverlight例子和blog GreatVisualStudioQuickStarts