Silverlight Loaded Events
A common scenario in the development of GUI applications is the presence of different events fired by the engine as the visual tree is constructed and rendered. And Silverlight, of course, is no exception.
While WPF provides Initialized and Loaded events for all framework elements, Silverlight has only the latter. There is also a LayoutUpdated method, but (as we will see in a few moments) is not very useful.
To check the order of the events fired by Silverlight when rendering the visual tree, I created a small test: it consisted in a grid containing a border containing a textbox. I wrote handlers for the Loaded, SizeChanged and Layout events, and outputted them to the console.
Note that MSDN recommends NOT to use javascript’s alert method to check the order of the events fired, since it could not match the real one. Don’t ask how this might happen.
Also, if you are dealing with a User Control you built yourself, there is also the possibility to override the Measure and Arrange methods, which correspond to the two passes the Silverlight engine makes when deciding the layout of the visual tree. Here I will limit the test to events visible from the outside.
Here is the output of the first test run. I’m writing the control’s name (level means the level within the tree), its render size, and margin set. The last one will serve as a witness to know when it is actually set, being specified in XAML.
Loaded LevelThreeTextbox render size 0×0 margin 1
Loaded LevelTwoBorder render size 0×0 margin 1
Loaded LevelOneGrid render size 0×0 margin 1
Size LevelOneGrid render size 300×300 margin 1
Size LevelTwoBorder render size 98×98 margin 1
Size LevelThreeTextbox render size 20×96 margin 1
Layout Sender Null
Layout Sender Null
Layout Sender Null
As expected, the Loaded event gets fired first, since (according to the documentation) it is fired when all properties are set (note that margin’s value is 1). Also notice that it is fired in the leaves first, since there is no point in setting the "Children" property of a control and raising its loaded event, having unloaded children.
Then, SizeChanged is fired, which makes sense if you consider that size has gone to a valid value from no size at all. The problem here is that the root fires first. You will soon see why this is a problem.
Finally, Layout methods are fired, without any information of the sender at all (just a null reference), which renders it completely unusable.
Another test I decided to undertake was adding more controls to the grid, and check in which order they were loaded/drawn: position in grid or order of creation in XAML.
Here is the XAML I used:
<Grid x:Name="LevelOneGrid" Height="300" Width="300" Loaded="Element_Loaded" SizeChanged="Element_SizeChanged" LayoutUpdated="Element_LayoutUpdated" Margin="1"> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Border x:Name="LevelTwoBorderAlpha" Grid.Column="1" Grid.Row="1" Background="Beige" Loaded="Element_Loaded" SizeChanged="Element_SizeChanged" LayoutUpdated="Element_LayoutUpdated" Margin="1"> <TextBox x:Name="LevelThreeTextboxAlpha" Text="Test" TextAlignment="Center" HorizontalAlignment="Stretch" VerticalAlignment="Center" Loaded="Element_Loaded" SizeChanged="Element_SizeChanged" LayoutUpdated="Element_LayoutUpdated" Margin="1"/> </Border> <Border x:Name="LevelTwoBorderBeta" Grid.Column="0" Grid.Row="0" Background="Beige" Loaded="Element_Loaded" SizeChanged="Element_SizeChanged" LayoutUpdated="Element_LayoutUpdated" Margin="1"> <TextBox x:Name="LevelThreeTextboxBeta" Text="Test" TextAlignment="Center" HorizontalAlignment="Stretch" VerticalAlignment="Center" Loaded="Element_Loaded" SizeChanged="Element_SizeChanged" LayoutUpdated="Element_LayoutUpdated" Margin="1"/> </Border> <Border x:Name="LevelTwoBorderGamma" Grid.Column="2" Grid.Row="2" Background="Beige" Loaded="Element_Loaded" SizeChanged="Element_SizeChanged" LayoutUpdated="Element_LayoutUpdated" Margin="1"> <TextBox x:Name="LevelThreeTextboxGamma" Text="Test" TextAlignment="Center" HorizontalAlignment="Stretch" VerticalAlignment="Center" Loaded="Element_Loaded" SizeChanged="Element_SizeChanged" LayoutUpdated="Element_LayoutUpdated" Margin="1"/> </Border> <Border x:Name="LevelTwoBorderDelta" Grid.Column="0" Grid.Row="2" Background="Beige" Loaded="Element_Loaded" SizeChanged="Element_SizeChanged" LayoutUpdated="Element_LayoutUpdated" Margin="1"> <TextBox x:Name="LevelThreeTextboxDelta" Text="Test" TextAlignment="Center" HorizontalAlignment="Stretch" VerticalAlignment="Center" Loaded="Element_Loaded" SizeChanged="Element_SizeChanged" LayoutUpdated="Element_LayoutUpdated" Margin="1"/> </Border> </Grid>
And here the output:
Loaded LevelThreeTextboxAlpha render size 0x0 margin 1 Loaded LevelTwoBorderAlpha render size 0x0 margin 1 (1,1) Loaded LevelThreeTextboxBeta render size 0x0 margin 1 Loaded LevelTwoBorderBeta render size 0x0 margin 1 (0,0) Loaded LevelThreeTextboxGamma render size 0x0 margin 1 Loaded LevelTwoBorderGamma render size 0x0 margin 1 (2,2) Loaded LevelThreeTextboxDelta render size 0x0 margin 1 Loaded LevelTwoBorderDelta render size 0x0 margin 1 (2,0) Loaded LevelOneGrid render size 0x0 margin 1 Size LevelOneGrid render size 300x300 margin 1 Size LevelTwoBorderDelta render size 98x98 margin 1 (2,0) Size LevelThreeTextboxDelta render size 20x96 margin 1 Size LevelTwoBorderGamma render size 98x98 margin 1 (2,2) Size LevelThreeTextboxGamma render size 20x96 margin 1 Size LevelTwoBorderBeta render size 98x98 margin 1 (0,0) Size LevelThreeTextboxBeta render size 20x96 margin 1 Size LevelTwoBorderAlpha render size 98x98 margin 1 (1,1) Size LevelThreeTextboxAlpha render size 20x96 margin 1 Layout Sender Null Layout Sender Null Layout Sender Null Layout Sender Null Layout Sender Null Layout Sender Null Layout Sender Null Layout Sender Null Layout Sender Null
Loaded events are fired in order of creation in XAML, which makes sense. The big surprise is that size changed events are fired in exactly the opposite order. And Layout keeps the same behaviour as usual: none.
Now, to the motivation of this post. I had a relatively complex user control, which contained several children, and I needed to position a selection window over it, based on the position of the children.
The problem is that there was no event I could attach to in the user control that could notify that all children were fully rendered, since the control’s SizeChanged fired before its children’s. And I definitively wanted to avoid LayoutUpdated.
So I wrote a small class, the RenderNotifier, that attaches itself to a control and receives a list of children to monitor. When all of them raise their size changed events, the RenderNotifier raises its own RenderComplete event.
It contains a list of children and attaches itself to each SizerChanged event, and detaches on remove.
public RenderNotifier(FrameworkElement targetElement) { this.TargetElement = targetElement; TargetElement.SizeChanged += new SizeChangedEventHandler(TargetElement_SizeChanged); } public void AddObservedChild(FrameworkElement child) { if (!observedChildren.Contains(child)) { child.SizeChanged += Child_SizeChanged; observedChildren.Add(child); } } public void RemoveObservedChild(FrameworkElement child) { if (observedChildren.Remove(child)) { child.SizeChanged -= Child_SizeChanged; } } public void ClearObservedChildren() { foreach (FrameworkElement elem in observedChildren) { elem.SizeChanged -= Child_SizeChanged; } observedChildren.Clear(); }
And whenever the parent control fires its size changed, the Notifier counts the children’s notifications and raises its own event after all have been fired.
int notificationsPending = 0; void Child_SizeChanged(object sender, SizeChangedEventArgs e) { if (–notificationsPending == 0) { OnRenderComplete(); } } void TargetElement_SizeChanged(object sender, SizeChangedEventArgs e) { notificationsPending = observedChildren.Count; }
Quite a lot of work just to know when the tree is completely rendered. But at least it works.
And returns a non-null sender in its callbacks.
Paging in Data-Bound ListBox in Silverlight
A useful feature for most data-bound items presenting controls is the ability to page their data, by showing only the first n elements and requesting the rest on demand as the users scrolls down the view. This is specially useful when downloading each item is costly, and waiting until the whole collection is obtained to show the user the result demands too much patience from him.
So I decided to implement this on Silverlight, as a way of continuing my old post on data binding, which ended with an unfulfilled promise of a sequel.
First thing to do was check how controls (DataGrid and ListBox) behave when bound to a collection. I created a mock class to populate the controls with:
public class Client { public String Name { get; set; } public int Age { get; set; } public bool Vip { get; set; } public override string ToString() { return String.Format(“Client {0} {1}”, Name, Age); } }
I won’t be creating a DataTemplate for this example, so I just override the ToString() method to display something human-friendly.
Next thing to do was creating a read-only wrapper for a Client collection, that implemented ICollection<Client>. The point was returning a custom iterator that just wrapped the underlying collection but also logged every action performed by the control.
public class ClientsEnumerator : IEnumerator<Client> { public string Name { get; private set; } public int Count { get; private set; } public int Index { get; private set; } private Client[] clients; public ClientsEnumerator(string name, Client[] clients) { Debug.WriteLine(“Enumerator {0} created.”, name); this.Name = name; this.Count = clients.Length; this.clients = clients; } #region IEnumerator<Client> Members public Client Current { get { Debug.WriteLine(“Enumerator {0} requested item {1}.”, Name, Index); return clients[Index]; } } #endregion #region IDisposable Members public void Dispose() { Debug.WriteLine(“Enumerator {0} disposed.”, Name); } #endregion #region IEnumerator Members object System.Collections.IEnumerator.Current { get { Debug.WriteLine(“Enumerator {0} requested item {1}.”, Name, Index); return clients[Index]; } } public bool MoveNext() { Index++; Debug.WriteLine(“Enumerator {0} moved to {1}.”, Name, Index); return (Index < Count); } public void Reset() { Index = 0; Debug.WriteLine(“Enumerator {0} reset to {1}.”, Name, Index); } #endregion }
Nothing too fancy as you see. The test consisted in binding both a DataGrid and a ListBox to this collection, and check which items were requested initially, and which ones as I scrolled.
The results with the ListBox were predictable. It contains a panel that handles the layout of its items, and a ScrollViewer that controls which part of the panel is visible. This panel “believes” that it has all the space it needs to render, and the ScrollViewer draws a rectangular window (the viewport) that moves through it. Therefore, the ListBox makes a single initial call requesting all elements and displaying them. Scrolling makes no change to the already loaded items collection.
However, the results for the DataGrid were most surprising. It did implement some sort of paging, since requests to the collection were made as I scrolled down (although many repeated requests were made). Anyway, even if with repetitions it would be possible to make it work, there was also the initial sweep through the entire collection.
Considering that both controls did the same initial sweep which I had to avoid, and the ListBox (being much more lightweight than the DataGrid) contained all the functionality I needed, I decided to go and try to implement paging on listboxes.
Step one was detecting when the user scrolled down to the bottom of the ListBox, to fire an event requesting for another page. This proved much more difficult than I expected.
The ListBox itself doesn’t expose any property regarding its visible items, it is therefore necessary to obtain its internal ScrollViewer. As I was extending the ListBox control (remember to set DefaultStyleKey in the constructor when you extend from a control!), I had access to the protected GetTemplateChild(name) method, which searches for a control with the specified name inside the control’s current template. And thanks to the Silverlight convention on elements’ names in a styled control’s visual tree, we know that the ScrollViewer is (or should be) always named “ScrollViewer” (remember that it was “ScrollViewer Element” in beta 1). The LayoutUpdated event of the control is a nice spot to obtain the reference.
Now there is full access to the VerticalOffset property of the ScrollViewer, which returns the position of the viewport. The problem is Silverlight offers no way to listen to changes in a dependency property you have not declared. You can only specify the OnChange callback when you register it. Note that if you are working in WPF, it is possible to do this with the DependencyPropertyInfo class.
There are some ugly things you can do. There is always the possibility to attach with a two-way binding to the chosen property (actually, you would only need one-way-to-source, but it is only available in WPF) and listen to the setter of a certain property in a mock data context. But this doesn’t work if the dependency property you are trying to listen to is read-only, as the Binding object will try to assign to it the value in the DataContext when it is first attached, and throw an exception. Needless to say, VerticalOffset is read only.
Going with reflector inside the ScrollViewer, I found there is a useful ScrollChanged event, but it is internal to the System.Windows.Controls assembly. Extending ScrollViewer and re-writing most of its functionality to gain access to this event is not an option, since it is a sealed class, and ListBox requires that it the control used as a ScrollViewer must be a, well, ScrollViewer.
Luckily, this issue had already been discussed in the Silverlight forums (which I strongly recommend to read whenever you find a problem) and was solved: it is not the ListBox, neither the ScrollViewer you have to attach to to get the Scroll event. It is the ScrollBar.
So, I had to do something like this to finally attach to the Scroll event:
void PagingListBox_LayoutUpdated(object sender, EventArgs e) { ScrollViewer viewer = base.GetTemplateChild(“ScrollViewer”) as ScrollViewer; FrameworkElement viewerRoot = (FrameworkElement)VisualTreeHelper.GetChild(viewer, 0); vertical = ((ScrollBar)viewerRoot.FindName(“VerticalScrollBar”)); if (vertical != null) vertical.ValueChanged += verticalHandler; }
Since the ScrollBar exposes a Maximum property, it is easy to attach to its ValueChanged event and fire an event requesting for an extra page if the new value and the maximum value match.
The only trick here is that the ValueChanged event fires multiple times whenever you scroll (reason unknown), and it is important to ensure that only one page is requested when the user scrolls to the bottom. This is easily solved by manually keeping track of the last value received.
void ScrollBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { ScrollBar bar = sender as ScrollBar; if (e.NewValue == bar.Maximum && e.NewValue != lastValue) { // Fire an event requesting an extra page here! } lastValue = e.NewValue; }
The final step here is actually appending the new page to the list box wherever you handle the event. The easiest way of doing this is by using an ObservableCollection<T> so the ListBox automatically adds any items added to the underlying data source. Just add the items to the collection and the scroll will shrink and move slightly up, indicating that new elements have been added.
That is the only issue I have found so far with this implementation: it looks too much like SQL’s view of a table, where it shows a limited amount of rows, and when you scroll down new rows are added and the scrollbar’s thumb shrinks. It is a wise idea to show the user the total amount of items somewhere, so it knows whether the scrolling will eventually end or he may spend his whole life requesting additional pages.
Silverlight Dependency Properties
WPF introduced a new concept for managing properties which is dependency properties. Dependency properties can act as standard properties, but also offer a lot more functionality, such as data binding, assignment through resources, setters in styles, and a lot more. They also allow for easy extensibility since they can be declared in different classes than the types they affect, and can be set directly from XAML. Silverlight, of course, has inherited this, yet it has a subtle difference.
In order to define a dependency property for a user control in Silverlight, suppose TestNumber of type int in the control TestControl (those are original names, aren’t they?), you must do the following:
public int TestNumber { get { return (int)GetValue(TestNumberProperty); } set { SetValue(TestNumberProperty, value); } } // Using a DependencyProperty as the backing store for TestNumber. // This enables animation, styling, binding, etc… public static readonly DependencyProperty TestNumberProperty = DependencyProperty.Register("TestNumber", typeof(int), typeof(TestControl), new PropertyMetadata( new PropertyChangedCallback(TestNumberChanged)));
Looks a bit scary at first, specially if you have to remember all that whenever you want to make a new dependency property. Luckily Visual Studio ships with the propdp code snippet that automatically creates this.
The public property ensures easy access from code to the property, so that you can treat dependency and standard properties exactly the same way. Getters and setters forward the call to the FrameworkElement functions GetValue and SetValue, which receive the property to manage.
As for the property, it is registered statically, by declaring its name, type, owner and metadata. In this last piece is where the difference with WPF relies. While WPF expects an instance of UIPropertyMetadata, which allows you to specify an on-change callback, a default value, forbid animations and a few more things, Silverlight dependency properties receive a PropertyMetadata, which allows you to specify only the callback, not default values.
Nevertheless, there is an easy workaround to supply a default value for these properties as long they are defined for a user/custom control. When you set a value for a control in XAML, the value is set after its constructor is invoked (which makes sense if you think about it). All you have to do then is set the property value in the constructor, and it doesn’t matter whether it is before or after the InitializeComponent call.
In case you are wondering when the control’s constructor is invoked, it is when its parent’s InitializeComponent call is made. What this method does is process the XAML and create all objects specified in it, and set all fields named after the named controls with their correct values.
Refactoring to WebClient
Since Silverlight 2 Beta 2 introduced the WebClient class, I decided to refactor an application we had by replacing all references to HttpWebRequest with the new client. Its methods seemed much more simple and clear, and I didn’t even need to write a wrapper helper class to make calls to the server. But, as it happens with every refactor, I ran across a few problems.
The first thing I noticed was the fact that I the only method provided by the WebClient to send a stream of data in a POST to the server didn’t return the server’s response. The result parameter in the OpenWriteCompleted event is the opened writable stream, and I didn’t find any way to obtain the server’s response.
So, I ended up using the much simpler UploadString, which does return the response. The only problem is that I needed to serialize some objects, so I built an XmlWriter on top of a StringBuilder, serialized into it, and sent the ToString() of the writer:
XmlSerializer serializer = new XmlSerializer(typeof(T)); StringBuilder builder = new StringBuilder(); XmlWriter writer = XmlWriter.Create(builder); serializer.Serialize(writer, data); client.UploadStringAsync(url, builder.ToString());
But this threw an exception on the Java server, stating that it couldn’t deserialize the POSTed object, because "Content is not allowed in prolog". Googling a few minutes it came up that encoding could be the cause, and seeing that the serializer generated UTF-16 code, I decided to go for UTF-8 and see what happened.
serializer.Serialize(writer, data, null, Encoding.UTF8.WebName);
Now came the big surprise. When I ran this code, I got this beautiful exception stating that I shouldn’t set the encoding:
In other words, do not use the serialize method that sets encoding. The full text of the exception is The encoding style ‘utf-8′ is not valid for this call because this XmlSerializer instance does not support encoding. Use the SoapReflectionImporter to initialize an XmlSerializer that supports encoding.
Before you try to use the SoapReflectionImporter, let me tell you that it isn’t included in the Silverlight 2 Beta 2 NET framework.
As a final attempt, I tried simply removing the xml document prolog. Since there isn’t a XmlDocument class either in the framework, I had to do it the old way:
string written = builder.ToString(); written = written.Substring(written.IndexOf(‘>’) + 1, written.Length - written.IndexOf(‘>’) - 1);
And it worked. It seems that the WebClient was adding an extra header to the xml string I was sending, and therefore the Java web service threw the exception. Whatever the reason, it now works beautifully.
Update: Juan Wajerman pointed out that it is possible to pass XmlWriterSettings to its constructor, and specify there both the encoding and a very useful property named OmitXmlDeclaration, which does exactly what I wanted without needing to rely on the horrible string operations.
XmlSerializer serializer = new XmlSerializer(typeof(T)); StringBuilder builder = new StringBuilder(); using (XmlWriter writer = XmlWriter.Create(builder, new XmlWriterSettings { OmitXmlDeclaration = true })) { serializer.Serialize(writer, data); } string written = builder.ToString();
Binding Lights
For almost any developer who has worked in GUIs the concept of data binding should be at least familiar. If you have an Article and want to show its code and price, you just place two textboxes bound to those properties and place some invisible magical control who ensures that the controls and the data objects are kept synchronized. As long as you didn’t push it too far, it worked just fine, and allowed you to forget about some tedious GUI programming.
Silverlight, of course, has its own flavor of data binding, partially inherited from its big brother. This time, however, things have been made much easier.
Any Silverlight page or control has a visual tree, defined in XAML, which specifies a root container, its controls, their controls, and so on. Any Silverlight control has, as well, a property of type Object named DataContext. This property, when set to a control, is inherited by all of its children. Therefore, if you have a control C inside B inside A, and you set:
C.DataContext = myName;
Then A and B will share myArticle as their DataContext, and C will have myName since it was manually set. To make C inherit its parent DataContext later, just set its property to null. Note that if you want to clear an object’s DataContext, then you can’t set it to null, you should set it to an empty object that has all the properties that the control expects.
What are these properties expected by the control? Silverlight has a special syntax to specify that a control’s property binds to one of it’s DataContext’s properties. Nothing better to begin with than the classic TextBox example:
<TextBlock Text=”{Binding Name}” />
The first textblock will always display the text “Name”, whereas the second one will search for a property named “Name” in its DataContext and display its value. You can even set it to bind two-ways, as an easy way to allow user input:
Note that if the property does not exist then the binding will fail silently (warning you by a small notice in the output console if you happen to be debugging). Nevertheless, Beta 2 introduced an event to which you can attach and get these kind of errors.
Because it fails silently, it is not mandatory that a control’s DataContext always provides all of the binded properties, so an easy and dirty way of clearing a data context is setting it to String.Empty.
Silverlight 2 in fact allows some interesting property paths, that allows you to bind to a property of a property of a property … of the data bound object, traverse through a collection’s hierarchy or refer to attached properties.
When you look for how to ensure that the GUI stays tuned with the data bound object’s properties whenever they change, you find an old interface from 2.0 filling the gap: INotifyPropertyChanged. If you are wondering if its cousin, INotifyCollectionChanged, has a role to play here, just consider the following scenario:
<ListBox ItemsSource=”{Binding Students}”/>
Consider this class model, with the class Course set as the visual tree’s datacontext:
{
public ObservableCollection
public String CourseName { get; set; }
}
class Student
{
public String Name { get; set; }
public int RegistrationNumber { get; set; }
public int CourseYear { get; set; }
public override string ToString() { return Name; }
}
What you will get is a TextBox displaying the course name, and a ListBox with the list of students in the course. Since the type of the Students collection is ObservableCollection, the most common INotifyCollectionChanged implementor, whenever you add a student to the course, the listbox is automatically updated.
If you want the same thing to happen with the name of the course, then you will have to manually implement INotifyPropertyChanged.
Note that you have a more complex property path for your bindings, suppose {Binding Course.Teacher.Address.Street}, then all objects in the path must implement INotifyPropertyChanged for it to refresh properly.
Going back to the example, you will notice that what is displayed as items in the ListBox are the ToString executions of all the Students contained in the items source collection. Though it is the easiest representation, it will hardly ever be the representation you want to show to the end user.
To solve this issue, Silverlight has included the concept of DataTemplates, which allow you to customize the way an object is rendered when contained in a presenter (such as a ListBoxItem).
But that is more than enough to fill a new post, which will be coming real soon.
Silverlight 2 Beta 2 Breaking Changes
Silverlight 2 Beta 2 has introduced a number of fixes, additions and changes to the previous Beta 1 version. As I said in a previous post, I believe the improved error handling and detailed exceptions are among the best improvements, and also the less commented.
There have also been several improvements in Expression Blend, most of them regarding the Visual State Manager (which I’ll describe in a few moments), but Scott Guthrie has already described them flawlessly.
As for a detailed list of changes, you can always refer to MSDN. What I will be focusing in here are all breaking changes that you will encounter when migrating your Beta 1 application to Beta 2, since most of them won’t be noticeable in compile time.
What you will first surely notice after updating to Beta 2 is -obviously- that your code no longer compiles. These are by far the easiests errors to fix. Most of them will be regarding namespaces, or definitions of Dependency Properties, since the signature of the Register method has changed slightly to be more like its WPF equivalent. The DialogResult enum has been replaced by a bool? as the return type of OpenFileDialog, behaviour also shared with WPF.
Fairly easy, isn’t it? Migrating a whole Silverlight application from Beta 1 to Beta 2 hasn’t taken more than a few minutes. However, when you run the application, you will realize that it isn’t that easy.
The first problem you might encounter, if you are using a Web Site Project to run your Silverlight application in an HTML page, is seeing this logo instead of your welcome screen:
This will redirect you to the Beta 1 download page, stating that the application you are trying to open has been developed in an earlier version, even though Visual Studio has already updated the project.
This just requires a correction in the HTML code of the page. Look for a line similar to this one:
type=”application/x-silverlight-2-b1″
width=”100%” height=”100%”>
Noticed anything? Just replace x-silverlight-2-b1 with x-silverlight-2-b2 and voila. Actually, you should re-generate the whole page, since the javascript function onSilverlightError has changed as well and, more important, the links to download the plugin in case it is missing:
href=”http://go.microsoft.com/fwlink/?LinkID=115261″>
<img style=”border-style: none”
src=”http://go.microsoft.com/fwlink/?LinkId=108181″
alt=”Get Microsoft Silverlight” />
</a>
Now, the application finally runs. However, it is most likely that if you have been playing with control templates (and you should have, since it is there where lies most of Silverlight’s power), some things may appear broken.
Silverlight allows you to completely redefine a control’s visual tree, thus making a button render as a textbox with a calendar floating around it. It is extremely flexible, and worth a whole post in the blog.
Some controls expect certain elements in their visual tree for correct behaviour. For example, a ScrollViewer expects two ScrollBars, one vertical and one horizontal, to handle the scrolling of its contents.
Now, how do you specify inside the control template (the mechanism to override the control’s visual tree) which scrollbar is the horizontal one and which one is the vertical one? You may even have more scrollbars, just for decoration, or to handle some other content. Well, you use a specific x:Name for it. If a ScrollViewer finds a ScrollBar named HorizontalScrollBar in its template, then it assumes it is the one that handles the horizontal scrolling of its content. If the name is something else, then it is just ignored and rendered without any special considerations. After all, it is just a name.
The problem here (and we finally get back to the differences between beta 1 and beta 2) is that those reserved names have changed in beta 2. HorizontalScrollBar in beta 2 was HorizontalScrollBarElement in beta 1. This means you will have to rename most of them everywhere you have used them (luckily, the change is nothing more than removing the ending Element in most cases). And the big problem, is that Visual Studio will not warn you against this, since you are just setting a name. The only clue you will have is that controls you have templated won’t behave correctly.
Last but not least, the introduction of the Visual State Manager has also changed quite a lot of things. Let’s take as an example the check box. Clearly, you want it to render differently if it is checked or not, if it is grayed, if it has focus, if it is right under the cursor, etc. In beta 1, the way to do this was by addind Storyboards to the visual tree’s root with -again- reserved names. These are the storyboards you had to add to control all the different renderings:
<Storyboard x:Key=”Checked State”>
<Storyboard x:Key=”Indeterminate State”>
<Storyboard x:Key=”MouseOver Unchecked State”>
<Storyboard x:Key=”MouseOver Checked State”>
<Storyboard x:Key=”MouseOver Indeterminate State”>
<Storyboard x:Key=”Pressed Unchecked State”>
<Storyboard x:Key=”Pressed Checked State”>
<Storyboard x:Key=”Pressed Indeterminate State”>
<Storyboard x:Key=”Disabled Unchecked State”>
<Storyboard x:Key=”Disabled Checked State”>
<Storyboard x:Key=”Disabled Indeterminate State”>
Quite a lot of states. If you are wondering why they are storyboards (since they handle animations), it is because the specified animation is executed whenever you enter the specified state. A bit verbose, but gives you a lot of control.
Now in beta 2 the syntax has changed. Visual States are organized into orthogonal State Groups, and a control is always in one and only one state of each group. Returning to the CheckBox example:
| State Name | VisualStateGroup | Description |
|---|---|---|
| Normal | CommonStates | The default state. |
| MouseOver | CommonStates | The mouse pointer is positioned over the control. |
| Pressed | CommonStates | The control is pressed. |
| Disabled | CommonStates | The control is disabled. |
| Focused | FocusStates | The control has focus. |
| Unfocused | FocusStates | The control does not have focus. |
| ContentFocused | FocusStates | The content of the control has focus. |
| Checked | CheckStates | IsChecked is true. |
| Unchecked | CheckStates | IsChecked is false. |
| Indeterminate | CheckStates | IsThreeState is true and IsChecked is null. |
You can now define storyboards for both states and transitions between states of the same group. For example, you may want the MouseOver animation to be different if the control was previously Pressed or Disabled.
This means, however, that you have no control over the transitions between states of different groups, making it less verbose but less powerful at the same time. Suppose you want the checkbox to turn red if it is Disabled and Pressed. In beta 1, you just created a ColorAnimation inside the Disabled Checked State. Now, you have no communication between states of different groups, and can’t determine when both Disabled and Checked states are present.
The idea is to use different ways to identify groups. Focused is usually represented by a dashed border around the control. Checked states by drawing a check (d’oh) inside the box. And common states, such as mouse over or pressed, by changing its color or shade.
This means that there is no way to directly translate what you have done in beta 1 into beta 2 in terms of visual states. Nevertheless, it is far less verbose, and the introduction of the Visual State Manager in Expression Blend allows designers to work easily on this.
As for the syntax, note that you now need to add the System.Windows namespace to add a reference to the VisualStateManager:
And instead of keeping storyboards loosely as resources, you add them inside VisualStateGroups, which are inside the VisualStateManager, which is inside the control’s resources (yeah, everything end up as a resource).
<vsm:VisualStateGroup x:Name=”CommonStates”>
<vsm:VisualStateGroup.Transitions>
<vsm:VisualTransition To=”MouseOver” Duration=”0:0:0.1″ />
<vsm:VisualTransition To=”Pressed” Duration=”0:0:0.1″ />
</vsm:VisualStateGroup.Transitions>
<vsm:VisualState x:Name=”Normal” />
<vsm:VisualState x:Name=”MouseOver”>
<Storyboard>…</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
…
</vsm:VisualStateManager.VisualStateGroups>
Keep in mind that Visual Studio won’t like the VisualStateManager.VisualStateGroups tag, but it will compile anyway and the application will run smoothly.
As a final issue to take into account, is the handling of mouse events, which has changed favourably to be more like WPF. Jesse Liberty has an excellent post explaining this change, but it sums up to the fact that the Handled property in the arguments of mouse events is taken into account when bubbling (or not) the mouse event; altough the reflector shows that the ListBoxItem never bubbles it.
To sum up, some of beta 2 breaking changes are not shown by Visual Studio after the upgrade, and they have to be searched for manually, which may be a bit annoying. Nevertheless, the huge leap between the two betas in both support, controls and stability makes it worth the effort.
Beta-Lighting the web
One of the current most interesting technologies around is Microsoft Silverlight, whose intention is to provide a programmer and designer friendly way to develop web UIs. It is basically a subset of Windows Presentation Foundation, although it has a few minor differences that you will surely note along the way.
If you aren’t familiar to WPF either (as I was when I started trying Silverlight), it is a whole new paradigm that intends to replace WinForms. It defines each control through two files: the old .cs file with the code supporting the front-end and a XAML file. The latter is an XML which should be modified by the designer (after the initial square and black-and-white presentation coded by the programmer) to apply all the necessary styles to make it more human-friendly. Needless to say, WPF is much more than this, and it can’t be explained in a mere paragraph.
Back to Silverlight, you may consider it as the little brother of WPF, designed for web applications. A Silverlight plugin in the browser contains the runtime, and it downloads and executes a .xap file with the assemblies of the application being stored in the server. This allows C# code to be executed directly on the client, without needing to translate it to a massive amount of javascript.
However, note that most user-interaction events are restricted to the browser, therefore some very simple events such as double click are not present in Silverlight. Also, the whole NET framework has been stripped of some of its functionality to ensure it can be packaged in the small plugin; and only a few WPF controls are present without their full functionality (although there are plans to release up to a hundred controls in the release version).
The most recent available version is Silverlight 2.0 Beta 2, which offers some improvements from Beta 1. If you are interested in the details, I won’t be listing them here, since there is already an excellent post by Scott Guthrie containing all changes and even an explanation on how to use the new Visual State Manager in Blend.
In my opinion, the best improvement between the two betas is the fact that exceptions are now much more detailed, giving you a clue on where to find the error in the XAML, instead of just getting an “AG_BAD_PARSER_VALUE”. Note, however, that there are still several XAML errors that could be caught on compile-time that won’t show themselves until you run the application.
To sum up, WPF can be regarded as a new UI design paradigm, with Silverlight being a very powerful web based equivalent, and it is worth giving it a shot. But bear in mind that it is still a beta, containing some annoying bugs still waiting to be corrected, and some very useful features that are missing for no apparent reason. I have the intention to do most of my blogging around them.
Yet another blog
Greetings! I’m starting a new blog to add even more stuff to the already huge blogosphere. If you wonder what this blog will be about, initially it is intended to share programming thoughts, issues and -hopefully- solutions. Since I’m currently working on C# and Java, most of my posts will be related to these languages.
I hope you’ll enjoy reading this blog as much as I’ll enjoy torturing you with its contents!
