Drop in the Bucket
One coder in the sea of s/w knowledge

Positioning Dynamically Created User Controls in Silverlight

Tuesday, July 29, 2008 12:52 AM

Note: This app was created using Silverlight 2.0 Beta 2

Help Me. I See Spots

Something you'll hear over and over again about Avalon (WPF) and Silverlight is how different it is when compared with more established UI platforms: like WinForms, HTML, ASP.NET, etc... I came face to face with this when I wanted to write a little spike that placed a dynamically generated user control wherever I clicked the mouse. Here's a screenshot of the application in all it's splendor:

screenshot

Wait a minute! Go ahead and try it out in the little blue area below.

How This Works

The page is really simple.

page

Notice that it doesn't contain any elements other than the TextBlock with the instructions. The Canvas element ties the MouseLeftButtonUp event to an appropriately named event handler which will serve as our click event.

Next is the XAML markup for the "ball". This is the user control that we'll create dynamically.

ball

The Width and Height are "intentionally left blank" because we're going to dynamically set the size of the ball as well as it's position.

Positioning the user control is the tricky part. If you were declaring the user control in the page's XAML, you could simply use the attached properties of Canvas.Top and Canvas.Left. The trouble with the dynamic control, is that in the code behind there are no positioning properties like you might expect (X, Y, Left, and Top), and the attached properties are nowhere to be found.

My solution was to supply the control with a render transformation. Specifically, I tie a TranslateTransform at construction time. I explose an X and Y property on the control itself, and alter the transform in the setters.

   15 public partial class Ball : UserControl
   16 {
   17 
   18     private const double DefaultRadius = 25.0;
   19     private const double DefaultX = DefaultRadius;
   20     private const double DefaultY = DefaultRadius;
   21 
   22     private double _x;
   23     private double _y;
   24 
   25     private TranslateTransform _translation = 
   26         new TranslateTransform();
   27 
   28     public double X
   29     {
   30         get
   31         {
   32             return _x;
   33         }
   34         set
   35         {
   36             _x = value;
   37             _translation.X = _x - Radius;
   38         }
   39     }
   40 
   41     public double Y
   42     {
   43         get
   44         {
   45             return _y;
   46         }
   47         set
   48         {
   49             _y = value;
   50             _translation.Y = value - Radius;
   51         }
   52     }
   53 
   54     public double Radius { get; private set; }
   55 
   56     public Ball() : this(DefaultRadius)
   57     {
   58     }
   59 
   60     public Ball(double r)
   61     {
   62         validateConstructor(r);
   63         Radius = r;
   64         Width = 2 * r;
   65         Height = 2 * r;
   66         RenderTransform = _translation;
   67         InitializeComponent();
   68     }
   69 
   70     private static void validateConstructor(double r)
   71     {
   72         if(r <= 0.0)
   73         {
   74             throw new ArgumentException("Please provide...
   75         }
   76     }
   77 }

The page simply news up a Ball and sets the x and y according to the mouse click.

   21 
   22 private void LayoutRoot_MouseLeftButtonUp(object sender, ...
   23 {
   24     Point clickPoint = e.GetPosition(LayoutRoot);
   25     Ball b = new Ball( );
   26     b.X = clickPoint.X;
   27     b.Y = clickPoint.Y;
   28     LayoutRoot.Children.Add(b);
   29 }
   30 
Download the Code
Comments have been closed on this topic.