Rants Tagged with “Silverlight 2”
As I've been neck-deep in Silverlight 2 for a couple of months now I noticed that there are some bugs/inconsistencies that aren't necessarily known to everyone. Here's a list of some of the issues (with workarounds if possible):
Custom Control Data Binding
If you are writing User/Custom Controls that want to be used in data binding (i.e. DataTemplate), you must use the assembly name in the namespace declaration, even if it is in the main assembly:
Bad:
xmlns:my="clr-namespace:MyAssembly.Controls"
Good:
xmlns:my="clr-namespace:MyAssembly.Controls;assembly=MyAssembly"
This is a known bug that will be fixed in future builds.
Uri Issues
In Silverlight 2 (in contrast to previous versions) the assets are packaged up in a .xap file. This .xap file is simply a ZIP file that contains the assemblies and other assets. In this file you can have images, fonts and other assets required. This new facility has made Uri's a bit confusing. When you specify a relative Uri in your Silverlight 2 project it may mean inside the .xap file or it may mean from the server.
The key is that relative Uri's will first look in the .xap file then look on the server but this isn't universal. For example if you have a foo.jpg in the root of your .xap file and specify this Image tag, it will find it in the .xap file:
<Image Source="foo.jpg" />
If the foo.jpg doesn't exist in the .xap, Silverlight 2 will look at the server for the file (most of the time)...but where is it looking? Unlike what you might expect, it actually looks on the file server relative to the ClientBin directory (where the .xap file is loaded from). If you want to have an image file loaded from the /image folder of the web server you would specify:
<Image Source="../images/foo.jpg" />
This works because it is relative to the ClientBin directory. If you specify "/images/foo.jpg" it won't look on the server. This is the bug. Using a full path to the server fixes this but that is fragile (you need to change it when you deploy an application).
This is especially an issue with MediaElement as it doesn't seem to work at all with relative paths. Interestingly if you look at the Uri usage with Reflector, the Source property in the MediaElement strip off the Uri and just send it the plain string (again, this is what I consider a bug). So if you're using a MediaElement, use non-relative paths for the Source.
In general I have found different/buggy behavior with different controls so if you are having trouble with Uri's, go to non-relative Uri's (e.g. full paths).
Using the Silverlight Control
If you want to use the ASP.NET Silverlight Control it requires ASP.NET AJAX 3.5. It requires a ScriptManager on the page and only works with the 3.5 version of AJAX. This means that if you are not using AJAX 3.5 you will need to use one of the other methods:
I tend to like the Silverlight.js for deployed projects anyway because of the additional functionality of Silverlight.isInstalled and other small features. If you used Silverlight 1.0 you'll already be familiar with Silverlight.js. The version that ships with Silverlight 2 has been updated to support Silvelright 2 deployment.
ImageBrush Can't Be Data Bound
This is a small but pesky bug that if you try to use data binding with the Image brush it just fails (or locks up the browser). So if you want to set the ImageBrush, you'll need to wrap a UserControl and data bind a DependencyProperty that will set the ImageBrush manually. Not hard but just a workaround.
LINQ Projections or Anonymous Types Fail on Data Binding
If you are using LINQ to do projections or using Anonymous types, data binding gets confused and tends to lock up the browser. For example:
var qry = from x in customers
select new
{
Name = x.CustomerName,
Phone = x.ContactPhone
};
theList.ItemsSource = qry.ToList();
The alternative is to just select the entire object (or use static types instead of anonymous types).
Popup.DataContext Doesn't Work
If you are using the cool Popup element and you want to use Data Binding to fill in data, the DataContext of the Popup fails to propogate its container. Therefore you need to manually set the first child's DataContext instead:
<Popup DataContext="{Binding}" /> <!-- Doesn't Work -->
That's all for the issues that I am working around these days. If you have more, feel free to drop them in the comments!
This weekend I will be at the Atlanta Code Camp this Saturday. I have four talks and they follow the predictable Silverlight and Data topics. Here are the talks:
- Using Blend 2.0 for Silverlight
- Consuming Data with Silverlight 2
- Digging into Deep Zoom
- Using C# 3.0 Features for Clean Code
In addition, we giving away a seat at the upcoming Atlanta stop of the Silverlight Tour (on May 12-14th, 2008). Com early and state late to be there for the drawing. The code camp is giving away a number of prizes, this is just one of them.
Hopefully, i'll see you there!
If you are interested in attending the upcoming Silverlight Tour stop in Seattle on April 9-11th, there are only a handful of seats left. Be sure and sign up soon. We only accomodate sixteen students per class to make sure everyone can get the attention they deserve.
In my weekend attempt to upgrade some of my older examples, the Silverlight SeeqPod Player is now all Silverlight 2.
Converting the Silverligth 1.1 application to a 2.0 was remarkably harder than the Silverlight 1.0 to 2.0. It may be because of use of user controls versus real controls (I changed from using two nested user controls to using a ListBox with Control Templates). In addition, switching the format of the web service usage from consuming XML directly to using DataContracts confused it a bit but overall it wasn't difficult. Lastly, I was using a layered HTML input control for a TextBox, and changing it over to a built-in one, took a little time too. Some code just simply disappeared (as I was moving some text around to make it appear like it was centered, but now its just a matter of marking it as TextAlignment = Center.
I was also able to polish some features I wanted to add:
- The Play Panel how slides in and out like I originally wanted.
- When the URL from SeeqPod is a dead link, I now change the UI to show its a bad song so you don't continue to click on the song.
- I also added Tooltips to help make sense of some of the non-text buttons.
Here's the source if you want to play with it: Source Code
UPDATE: I've updated the code (and the app) as I found some odd behavior with random or shuffle play. One of the things I noticed was that the selected item was not getting updated all the time. I realized that during some operations (MediaElement specific events and callbacks in Web Service Proxy's), you're not actually running on the main UI thread. This is not a big surprise but since it fails silently it can cause odd issues. To get around it I used the Dispatcher to fire an event on the UI thread:
Dispatcher.BeginInvoke(() => { songList.SelectedIndex = someValue; } );
You could use an anonymous delegate, but I prefer this shorthand Lambda function. It fixed the issue with the wierdness of showing hte selected index.
Hope this helps some of you struggling with issues.
Its not a dramatic change, but I've ported my Silverlight 1.0 Silverlight page to use Silverlight 2. There were some interesting lessons learned:
- If I want to use the ASP.NET Silverlight control, I have to upgrade to ASP.NET AJAX 3.5. Not a big deal but annoying.
- If you used class-style JavaScript, it came over to C# pretty easily. (Can't same the same for VB though).
- I was able to switch from Dynamic XAML to Data Templates (with a ListBox) very easily.
- There are some font rendering changes as the old XAML that used to fit on the template now overlaps. Had to do some editorial changes to accomodate the change.
- There is a bug in ListBox that does not reset the position of the ListBox's Scrollbar if the number of items changes (e.g. changed the ItemsSource). Doing hacks like ScrollIntoView() doesn't seem to fix it. If you move it with the mouse it heals itself.
I've decided to release the code this time. You can download it here: Source Code
Overall it was a fun little task. Tomorrow I am going to port my SeeqPod player from Silverlight 1.1 to Silverlight 2 to see what can be learned there.
Quick fix for a problem that was haunting me today:
If you upgrade an ASP.NET 2.0 app to 3.5 and have .xaml files in your project that are part of a Silverlight 1.0 or 1.1 project, the conversion wizard converters them to Build-type: "Page" and adds a custom build for building the WPF files. If this happens you'll get a cryptic error:
"error MC6000: Project file must include the .NET Framework assembly 'WindowsBase, PresentationCore, PresentationFramework' in the reference list."
Go through your xaml files and mark them as "Content" and remove any custom tools and it will fix it.
Jon Galloway has a new blog entry where he discussed and dissects DeepZoom. He explains it better than most and is really worth a read. Of particular interest is the comparison with other technologies (e.g. Google Maps, Zoomorama, etc.). He really makes some key points to explain why this is important to the Silverlight platform. If you care about Silverlight, read it now.
I've been teaching my students to be careful not to create Silverlight projects that alienate users and break the basic working of the web. One of the things I've explained is that you need to make it so that the back button and links work (where applicable) in your applications. I haven't had a good example to show them until now.
I've created a quick and dirty example of how to change the address bar (and respond to this change when you return). The trick to getting this to work is to use the HtmlWindow's CurrentBookmark and NavigateToBookmark members. For example, in the example I have a ListBox that changes some state in my app. When the SelectedIndex changes, I use NavigateToBookmark to change the Url:
theList.SelectionChanged += (s, e) =>
{
if (theList.SelectedItems.Count > 0)
{
siteInfo.Visibility = Visibility.Visible;
siteInfo.DataContext = theList.SelectedItem;
Site site = (Site) theList.SelectedItem;
HtmlPage.Window.NavigateToBookmark(string.Concat("id=", site.Id));
// HACK to fix non-binding of NaviateUri
theLink.NavigateUri = new Uri(site.Url);
}
else
{
siteInfo.Visibility = Visibility.Collapsed;
}
};
Then in my app, on Load I look at the CurrentBookmark so that if I've returned to my application with the Bookmark specified, I can change the state of my app back:
this.Loaded += (s, x) =>
{
// Show current item if included in bookmark
if (!string.IsNullOrEmpty(HtmlPage.Window.CurrentBookmark))
{
string[] parts = HtmlPage.Window.CurrentBookmark.Split('=');
if (parts.Length == 2)
{
int id;
if (int.TryParse(parts[1], out id))
{
Site selected = sites.FirstOrDefault(e => e.Id == id);
theList.SelectedItem = selected;
}
}
}
};
This is enabling both the Back button as well as Linking to my app. You can download the source here:
http://wildermuth.com/downloads/backenabledsilverlight.zip
Or see a live version of it here:
http://www.silverlightdata.com/backenabledsilverlight.aspx
One caveats on the example:
-
When you return to the page with a bookmark, the ListBox selection isn't showing. This is a ListBox bug that has been communicated to Microsoft. It has nothing to do with this code.
This is an excellent looking Silverlight 1 ad that I found on Microsoft's site today. Clear, consise and a bit sexy.
Made me think about more Silverlight based ads for non-Microsoft sites. Too bad SL isn't ubiquitous yet...but it feels like its coming...
Brad Abrams announced the availablility (as promised at MIX 08) of the source code for the Silverlight controls and all 2000+ unit tests for the controls...including a unit test framework for Silvelright 2. I will have a look once I get home from teaching the Silverlight Tour. I might have to add Unit Testing to the class ;)
Give it a look if you're looking for a good way to unit test your own Silverlight 2 applications!