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:
Wait a minute! Go ahead and try it out in the little blue area below.
How This Works
The page is really simple.
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.
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