Stats
Categories
  • Trying to download Blend 4 for WP7 Beta, but everything I've installed 2 month ago is incompatible :|...ufff, let's reinstall :D 2 weeks ago
  • Ç Testing LINQ to Twitter update status on 7/8/2010 2:15:10 AM #linqtotwitter 3 weeks ago
  • Writing stuff 4 fun for tweeter with wpf 3 weeks ago
  • read & write excel xlsx files with silverlight ok, sync with our webservice ok, update DB almost complete :) 3 weeks ago
  • building tours silverlight app 3 weeks ago

Posts Tagged ‘Blend 3’

One of the most important objectives in every program is to try to have reusable code. In Expression Blend it’s a little tricky to create a Panel that holds components and arrange them visually in a custom way: so what we want to create in this tutorial is a custom Panel that we can reuse in Expression Blend 3.

Let’s start with the main idea, which can be anything: we…we…well…let me think (….10 mins later), uh right as shown in the blend example, we want to put all elements in circle around the center.

What we need is to define a new class and derive it from Panel. (I’m inserting line numbers so it’s easy to read, all source code it’s @ the end ;) )

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using System.Windows.Controls;
   6:   
   7:  namespace uidev.Panels
   8:  {
   9:      public class CircularMenuPanel : Panel
  10:      {
  11:   
  12:      }
  13:  }

 

Then we need a method to refresh the view when we insert the elments, and use it with a little trick: a call from Dependency Properties, so when a parameter has been changed the property calls the method.

  1. Add a private Refresh() method
  2. Add a static void method that calls the Refresh() method
  3. Add a Dependency Property that calls the static void method
  4. Add a variable that the Dependency Property refers to

 

   1:   public class CircularMenuPanel : Panel
   2:      {
   3:          //step 3
   4:          public static readonly DependencyProperty RadiusProperty =
   5:              DependencyProperty.Register("Radius", typeof(double), 
   6:              typeof(CircularMenuPanel),
   7:              new PropertyMetadata(CircularMenuPanel.RadiusChanged));
   8:   
   9:          //step 2
  10:          private static void RadiusChanged(DependencyObject sender, 
  11:              DependencyPropertyChangedEventArgs e)
  12:          {
  13:              ((CircularMenuPanel)sender).Refresh();
  14:          }
  15:   
  16:          //step 4
  17:          public double Radius
  18:          {
  19:              get { return (double)GetValue(RadiusProperty); }
  20:              set { SetValue(RadiusProperty, value); }
  21:          }
  22:   
  23:          // step 1
  24:          private void Refresh()
  25:          { 
  26:          
  27:          }
  28:      }

 

Now when you change the Radius variable, the DependencyProperty “RadiusProperty” listens the changes and updates RadiusChanged that calls Refresh().

Now we want that each time a element is added we ensure enough space, so we have to override MeasureOverride and ArrangeOverride. In addiction we can, it’s up to us, animate each element added…just to make thing more interesting.

 

   1:          protected override Size MeasureOverride(Size availableSize)
   2:          {
   3:              Size resultSize = new Size(0, 0);
   4:   
   5:              foreach (UIElement child in this.Children)
   6:              {
   7:                  child.Measure(availableSize);
   8:                  resultSize.Width = Math.Max(resultSize.Width, 
   9:                      child.DesiredSize.Width);
  10:                  resultSize.Height = Math.Max(resultSize.Height, 
  11:                      child.DesiredSize.Height);
  12:              }
  13:   
  14:              resultSize.Width =
  15:                  double.IsPositiveInfinity(availableSize.Width) ?
  16:                  resultSize.Width : availableSize.Width;
  17:   
  18:              resultSize.Height =
  19:                  double.IsPositiveInfinity(availableSize.Height) ?
  20:                  resultSize.Height : availableSize.Height;
  21:   
  22:              this.Animate();
  23:   
  24:              return resultSize;
  25:          }
  26:   
  27:          protected override Size ArrangeOverride(Size finalSize)
  28:          {
  29:              this.Refresh();
  30:              return base.ArrangeOverride(finalSize);
  31:          }
  32:   
  33:          private void Animate()
  34:          {
  35:              int AnimationDuration = 200;
  36:              int index = 0;
  37:              foreach (FrameworkElement element in this.Children)
  38:              {
  39:                  element.Opacity = 0;
  40:                  int time = AnimationDuration * (index + 1);
  41:   
  42:                  DoubleAnimation opacityAnimation = new DoubleAnimation();
  43:                  opacityAnimation.From = 0;
  44:                  opacityAnimation.To = 1;
  45:                  opacityAnimation.BeginTime = new TimeSpan(0, 0, 0, 0, time);
  46:                  opacityAnimation.Duration = 
  47:                      new Duration(new TimeSpan(0, 0, 0, 0, AnimationDuration));
  48:   
  49:                  element.BeginAnimation(FrameworkElement.OpacityProperty, 
  50:                      opacityAnimation);
  51:                  index++;
  52:              }
  53:          }

The last thing to do is to implement Refresh() method.

We can subdivide the actions taken in this method:

  1. Check if the width/height are NaN (Not a Number = not set)
  2. Iterate through each element in the Panel
  3. Calculate a transform (in this case a Rotation) for each element
  4. Check if the desired width/desired height are NaN
  5. Apply Arrange method to the element
   1:          private void Refresh()
   2:          {
   3:              int count = 0;
   4:              int initialAngle = 0;
   5:              int angleItem = 30;
   6:              if (double.IsNaN(this.Width))
   7:              {
   8:                  this.Width = 200;
   9:              }
  10:              if (double.IsNaN(this.Height))
  11:              {
  12:                  this.Height = 200;
  13:              }
  14:   
  15:              foreach (FrameworkElement element in this.Children)
  16:              {
  17:                  RotateTransform r = new RotateTransform();
  18:                  r.CenterX = 0;
  19:                  r.CenterY = 0;
  20:                  r.Angle = (angleItem * count++) - initialAngle;
  21:                  element.RenderTransform = r;
  22:                  double x = this.Radius * Math.Cos(Math.PI * r.Angle / 180);
  23:                  double y = this.Radius * Math.Sin(Math.PI * r.Angle / 180);
  24:   
  25:                  if (!(double.IsNaN(this.Width)) && !(double.IsNaN(this.Height)) 
  26:                      && !(double.IsNaN(element.DesiredSize.Width)) 
  27:                      && !(double.IsNaN(element.DesiredSize.Height)))
  28:                  {
  29:                      element.Arrange(new Rect(x + this.Width / 2 - r.CenterX, 
  30:                          y + this.Height / 2 - r.CenterY, element.DesiredSize.Width, 
  31:                          element.DesiredSize.Height));
  32:                  }
  33:              }
  34:          }

Note that element.Arrange is what let the visual element refresh the Blend view! So it’s basically the core method.

The final step is to make Blendize it, so we can manipulate its properties as  we do with all others components/panels. To do this we add a custom category, just up the Radius variable, and all other variables we are going to create.

   1:          [Category("Circular Menu")]
   2:          public double Radius
   3:          {
   4:              get { return (double)GetValue(RadiusProperty); }
   5:              set { SetValue(RadiusProperty, value); }
   6:          }

We enable the Category option adding a using to our statements:

   1:  using System.ComponentModel;

So, how do I use it in Expression Blend?

Before you can use this component in Blend, you should build your solution, then open Expression Blend 3, and look into Assets and click on Project. There you’ll find all your custom Panels, as shown below:

image

Then click on CircularMenuPanel, the component we just created, and drag as usual

image

Now let’s add some components…which ones? Anything will work fine :) we can add buttons:

image  image

We also notice that the buttons are stretched into a really small circle and each element added gains a 30° degrees rotation. That’s what’s we specified at the Refresh’s beginning method

   1:          private void Refresh()
   2:          {
   3:              int count = 0;
   4:              int initialAngle = 0;
   5:              int angleItem = 30;//specifies the angle
   6:              .....
   7:          }

and we did not touch the Radius variable we created.

To modify the Radius, and make the circle a little bit larger we have to select the CirclularMenuPanel from “Objects and Timeline” and then go to the properties panel on the right and look for Circular Menu category:

image

Then modify the radius and watch what happens to your view!

The view refreshes in real time!!!!!!

Not only, each element added is animated, in this case its opacity goes from zero to 1.

image

That’s all folks, I really hope that you enjoyed this tutorial. Comments really appreciated, and suggestions more than everything are welcome!

As final thing the link @ the code:

 

-dave

Code Zero is proud to present the first tutorial made with the new Design.

Here we talk about how to create a state in SketchFlow and play with it. Basically the easy part is to activate the state, and the tricky part is how to deactivate it!

So let me show you how to do it. The video is in english, and since I’m not english native and I don’t get many chances to talk in english, please forgive my errors :)

And as usual the video:

Get Microsoft Silverlight

And the code and the slides in a zip used in this demo ;)

 

Comments and suggestions are welcome :)

-dave