I wanted to touch on this briefly as I ran into a namescoping issue reading on animations and thought I'd share a little bit of what I learned.
A namescope is the relationship between the name of an object defined in XAML and its instance. This is particularly relevant when interacting with the object in the code behind. Framework level items (FrameworkElement / FrameworkContentElement can be named using the Name property in code or XAML attribute. However, some some animatable objects such as those that derive from the Freezable class requires using the x:Name XAML attribute value in order for code behind interaction with that instance of the object. The name assigned to an object is unique and this is enforced by the namescope of the containing object.
The INameScope interface provides the contract for enforcing name uniqueness and how names are accessed in a XAML namescope. The NameScope class implements base support for the INameScope methods that store or retrieve name-object mappings into a particular namescope, and adds attached property support to make it simpler to get or set identifier namescope information dynamically at the element level (from documentation).
Some things have been added and shifted around from .NET 3.5 to 4.0 Beta 1 for NameScope.
.NET 3.5
- NameScope only implements INameScope interface
- NameScope is under the System.Windows namespace in the PresentationFramework.dll assembly.
.NET 4.0 Beta 1
- NameScope implements several interfaces and provides a host of additional members
- moved to the System.Windows namespace in the WindowsBase.dll assembly.
While namescopes can be nested, because of the enforcement of uniqueness, two elements that falls within the same namescope cannot have the same name. For example, when you fire up Visual Studio and create a new WPF project (page or window), a namescope is automatically created for you since these are root elements of an application. Even if you do not assign names to any of the elements in XAML, the namescope is still there and waiting for you. But if you try to give two buttons on the same window the same name, an exception will be thrown. However, things may get a little weird when you jump to C# and try to work with objects from the code behind, especially when dealing with animation. Thus far, you can pretty much do a whole lot in XAML to satisfy your animation needs without sweating over the whole namescope issue.
This is where I ran into my first namescoping problems - writing animation code in C#. Here, I will look at simple snippets of code in two scenarios:
[1] A empty WPF project was created with Visual Studio
[2] A WPF app is written without any initial XAML at all - like in Notepad
Case 1:
As soon as you create a new WPF project in VS, remember that some initial housekeeping is automatically taken care for you, such as creating a root element, be is a Page or a Window. At this point, since this is root element, a namescope is created for it. Now if you add elements using VS either using XAML or the designer, then add the Name or x:Name attribute, all is well in the world and you can freely use the named elements in code behind with no issues.
But here, let's forget the XAML part and do it all in C# in the code behind. Therefore, when you created your WPF project (let's say window), all you have is the basic structure of a window and a grid as its content. Remove the grid and go to the code behind.
The code will create a rectangle object, name it, register its name in the containing namescope, create an animation to change the opacity, create a storyboard to run the animation.
1 using System;
2 using System.Windows;
3 using System.Windows.Controls;
4 using System.Windows.Media;
5 using System.Windows.Media.Animation;
6 using System.Windows.Shapes;
7
8 namespace WpfApplication_Namescope
9 {
10 public partial class Window1 : Window
11 {
12 Storyboard mySB;
13
14 public Window1()
15 {
16 InitializeComponent();
17
18 // STACKPANEL
19 // create a stack panel to put my
20 // objects in
21 StackPanel sp = new StackPanel();
22
23 // RECTANGLE
24 // create rectangle and set some
25 // properties
26 Rectangle myRect = new Rectangle();
27 myRect.Width = 100;
28 myRect.Height = 100;
29 myRect.Fill = Brushes.Black;
30
31 // name the rectangle
32 myRect.Name = "myRect";
33
34 // register the name of the rectangle
35 // with the namescope of the containing
36 // element
37 // in this case this is the root element,
38 // the window
39 NameScope.GetNameScope(this).
40 RegisterName(myRect.Name, myRect);
41
42 // add the rectangle to the stack panel
43 sp.Children.Add(myRect);
44
45 // ANIMATION
46 // create a DoubleAnimation to animate the
47 // opacity of the rectangle from 100%
48 // to 50% over 2 seconds
49 DoubleAnimation da = new DoubleAnimation();
50 da.From = 1.0;
51 da.To = 0.5;
52 da.Duration =
53 new Duration(TimeSpan.FromSeconds(2));
54 da.AutoReverse = true;
55
56 // STORYBOARD
57 mySB = new Storyboard();
58 mySB.Children.Add(da);
59 Storyboard.SetTargetName(da, myRect.Name);
60 Storyboard.SetTargetProperty(da,
61 new PropertyPath(Rectangle.OpacityProperty));
62
63 // make the stack panel the main content of
64 // the window
65 this.Content = sp;
66
67 // start the storyboard when the window
68 // is loaded
69 this.Loaded +=
70 new RoutedEventHandler(Window1_Loaded);
71 }
72
73 void Window1_Loaded(object sender,
74 RoutedEventArgs e)
75 {
76 this.mySB.Begin(this);
77 }
78 }
79 }
Lines 32, 39 and 40 is where the namescoping comes in. We needed to set the name of the rectangle and register it in the namescope of the window (since its the root element, but you could have registered it to another containing elements' namescope). This is necessary for Line 59, setting the Storyboard.SetTargetName attached property. When the storyboard starts, it will look of the element with that particular name. It will first search any locally defined namescopes to find the name-object mapping then walk up the tree all the way to the root. If you did not register the name of the rectangle (you can comment out Lines 39,40 to see) then when you run the storyboards, guess what will happen. Nothing! That was because the storyboard could not resolve the name of the element it is supposed to use to animate a property on.
Case 2:
This is an instance if you do not use any XAML at all, probably not even using VS to create an initial skeleton project. Let's say you want to do exactly the stuff in case 1, but all in notepad. The main changes will be as shown:
1 // create a namescope for this
2 // window
3 NameScope.SetNameScope(this, new NameScope());
4
5 // RECTANGLE
6 // create rectangle and set some
7 // properties
8 Rectangle myRect = new Rectangle();
9 myRect.Width = 100;
10 myRect.Height = 100;
11 myRect.Fill = Brushes.Black;
12
13 // name the rectangle
14 myRect.Name = "myRect";
15
16 // after naming the rectangle,
17 // register it with the namescope
18 // created above
19 this.RegisterName(myRect.Name, myRect);
The main points are Lines 3 and 19. Because we did not have an initial XAML file, no namescope was automatically created for us to use. However, in Line 3, we created our own and in Line 19 we registered the rectangle to be animated with the namescope. Make note of the slight syntax differences.
No comments:
Post a Comment