Wednesday, December 29, 2010

Silverlight Pivot Viewer

For those that have not seen/used the Silverlight Pivot viewer, its definitely worth a look. It is an interesting control which allows you to navigate large amounts of data in very interesting ways. It gives the user the ability to view different aspects of the data and do their own filtering. The Pivot Viewer shows Deep Zoom Images as the output format, which allows the user to drill down into the required information.

Probably easier to view by example:
NetFlix has a working example of Pivot Viewer

Keeping this in mind, its probably best to use the Pivot Viewer with information which has large amounts of attributes. This will then give the user more options to 'slice and dice' the information. The main challenge will be to design your data correctly. This is a common problem with any business intelligence tool. You will find that you will keep refactoring your data because few people get it right on the first approach.

The basic output of the Pivot Viewer is a special XML schema (CXML). This data (XML) needs to be hosted on a web server with the necessary crossdomain policy files setup. There are two main ways you can generate the data needed for the Pivot viewer.

The first (and easiest) method is to use the provided Excel add-in. This method allows you the enter your necessary information and images. It then allows you to export it in the necessary format which can be hosted on your web server.

The second method requires you to build up the CXML yourself. This can be tricky, but if you use their provided SDK then it should be rather straight forward. The most examples show how to use static deep zoom images. While this is nice when working with images (like the NetFlix example above), using dynamic data will be more tricky. The approach I like to use is to use their SDK and implement my own PivotSource. This is basically a class that implements the CollectionFactoryBase abstract class in their SDK example. You will need to provide implementation for the MakeCollection method.

The next step is to create custom Http Handler to generate the deep zoom image on the fly. They provide a PivotTools library which you can use as a starting point. An alternative method is to create a template image in Blend or PhotoShop which you can inject your values into at run time. There are various APIs you can use to do this.

All of the examples use the local Visual Studio web server to host the solution. There were some interesting problems when trying to host in IIS6 on my Windows 7 machine. A couple of things to keep in mind:
  • Make sure ASP.NET 4 is registered in IIS - use aspnet_regiis -i in command line
  • Make sure MIME types are setup in IIS for text/xml - .cxml, .dzc, .dzi
  • Because you are using custom Http Handlers please make sure that you have Module Mappings setup for the above extensions for IsapiModule. This will tell IIS to execute your registered handlers in the web.config
The rest I will leave up to you to experiment. If you find any other cool features please let me know.

Wednesday, December 1, 2010

Silverlight and WPF Best practices

I have worked with a bit of Silverlight and thought I would share some best practices I have experienced and seen online. It is not a well discussed topic so this is a start. I will modify as I go on and would appreciate feedback

  • Use the Visibility property rather than Opacity to hide objects - Opacity is associated with higher costs because the object is still hit tested and technically rendered. Setting Visibility to Collapsed avoids these costs.
  • Don't use the Width and Height properties of MediaElement and Path objects - If you need to change the display size of the element, it is best to re-encode the media to the desired size by using a media encoding tool.
  • Detach programmatically registered event handlers after use.
  • Use transparent control backgrounds sparingly - using a transparent background for a Silverlight plug-in has a significant effect on performance.
  • Set EnableFrameRateCounter to true During Development - This setting will display the frames-per-second (fps) of the rendered Silverlight content in the browser's status bar, so you can fine-tune your application.
  • Use Silverlight Animations Instead of Creating Your Own Per-Frame Animations - This approach is platform-independent and browser-independent and is the most efficient method.
  • Avoid Animating the Size of Text - Animating the size of text can potentially use a lot of system resources. This is because Silverlight uses hinting to smooth each text glyph when it renders text. If you animate the text size (by using the Transform object or the FontSize property), Silverlight will hint the glyphs for each frame, which is costly and could result in frame-dropping. If your application requires dynamic scale changes of large text, it may be better to use vector graphics to represent the text.
  • Avoid Using Windowless Mode - Set the Windowless property to true only when necessary. Windowless mode is expensive and can cause tearing during animations.
  • In Full-Screen Mode, Hide Unused Objects - When your application goes into full-screen mode, hide all objects that are not being rendered in full-screen mode, or disconnect them from the tree.
  • When Downloader Finishes, Detach Events and Set to Null - When you use the Downloader object, after the Completed event fires, detach all event handlers from the Downloader, and then set Downloader to null. If you reference any content that the Downloader obtained again at a later time, you typically get that content faster because of caching. The performance cost of initializing another Downloader will be more than offset when the Downloader request is able to use cached content, so there is no advantage in keeping the same Downloader instance around to service the same request again later.
  • Break Up CPU-Intensive Work into Smaller Tasks - When JavaScript is running, the plug-in will stop drawing. Typically, this is not an issue if minimal work is done in the event handlers. However, if your application requires substantial, CPU-intensive work on the JavaScript thread, we recommend that you split that work into smaller tasks. This will allow rendering to keep up with the desired frame rate.

Markup Best Practices

  • Don’t use unnecessary “xmlns” namespaces in the XAML file. This overburdens the load time of the Silverlight page.
  • Don’t add same namespaces multiple times in a single XAML page. It screws up the XAML code at the time of maintenance and also loads the assembly namespace multiple times causing various memory issues at runtime.
  • Use proper name for your “xmlns” namespace prefix. For example: xmlns:commonControls is more meaningful than xmlns:cctrl. This avoids multiple declarations of namespaces in future.
  • Try avoiding “xmlns” namespace prefix name as “local”. Instead use “localControls” or “localConverters” etc. as per your namespace name. Using “local” will not give you proper meaning. In the same assembly there may be two or more namespaces (e.g. Controls, Converters etc.). In such case, it will be helpful for you to use proper prefix name to distinguish between them in proper way.
  • When adding a control that has no elements inside it, better to close it by self-closing tag “/>” instead of the hard closing tag (). This gives more cleaner XAML code.
  • Remove all unnecessary resource keys if they are not in use. These increases the memory uses and you may sometime encounter some animation issues due to this. If you need it at later point of time, you are always welcome to add it.
  • Don’t use extra panels (e.g. Grid, StackPanel, Canvas etc.) unless it is required.
  • Always try to use Grid as your panel first and if you require other panels, use them. Grid has the flexible UI layout and thus resizing your application will have a great effect.
  • Never try to give a name to all of your controls inside your Silverlight page as it takes unnecessary object creation at the time of load. Name only those elements which you want to use from your code behind and/or from your xaml. If you are using MVVM pattern, you can remove the naming of your controls in almost all the cases.
  • Use the Visibility property of the controls instead of the Opacity property to hide the content. Opacity to zero makes the control to hide but takes space in both memory and the UI. Other side, the Visibility property collapses the control from the UI, thus making spaces for the other controls in the same place.
  • Use proper formatting of your XAML code. This gives better look of code and also easy to maintain in future.
  • Use comments in XAML whenever require. This is useful when you revisit the code after a long time or some other person comes to work with your XAML file.
  • Try to use StaticResource instead of DynamicResource as it increases the performance and also it throws exceptions at development time. Hence, easier to identify the root cause.
  • Remove unnecessary styles & storyboard animations if they are not require at all.
  • Try to add your styles in a separate file if you want to share them across your application. If they are specific to a single page then add them in the page resource.

Formatting Best Practices

  • Attributes should be on a separate line.
  • x:Name should come first
  • Related attributes (such as HorizontalAlignment and VerticalAlignment) should be grouped.

Project Organization Best Practices

The obvious practice here is to create subdirectories to keep items organized.

A typical structure follows:

  • Resources is the directory for most of your resources. This directory should include only XAML files and subdirectories.
  • Resources-Images or Images should contain the icons and other images that are included as resources in the assembly. These items should be included as type “Resource,” not “Embedded resource”.
  • Resources-Fonts or Fonts, include the fonts embedded as resources in the assembly.
  • Converters is for value converters. This directory includes only class files. One converter per file is preferred.
  • Commands is the directory for your commands - classes that implement ICommand or custom routed commands-. This directory should include only class files.
  • Other is my catch-all to avoid too many extra directories. I usually group other user interface logic or helpers into this directory. For example, I use the other subdirectory to contain freezables that carry DataContext; or I use it for template selectors, or dependency objects thatoverride existing UI elements’ metadata.
  • Themes includes generic.xaml, other theme-specific resource dictionaries, and theme specific subdirectories. Use the following guidelines:
    • When working on projects that are not themed, leave the default structure generated by the tools – this means including default styles for custom controls in generic.xaml.
    • If working on a themed project, include a resource dictionary for each theme. Use the ..xaml convention (e.g. luna.normalcolor.xaml)
    • When working on a skinned project, follow same convention than themes and include a resource dictionary for each skin in the \Themes directory.
    • If a theme or skin is not self-contained and needs other resource dictionaries or other resources, create a subdirectory in \Themes and put the extra resources files there. Each subdirectory is named after the theme or skin. These “theme-specific”subdirectories can have subdirectories such as \Fonts and \Images.
    • Note that any resources shared among multiple themes still goes under the non-theme specific directories (e.g. \Resources\Fonts or \Fonts).

Organization of Resources

  • If there are dependencies between resources, the resources should be consistently ordered. Reusable resources should be at the top of the resource dictionary. A common structure usually follows the order shown in the following example:

- #Constants
- #Colors
- #Brushes
- #Converters
- #Objects (such as Data) or commands (for ribbon),etc.
- #Styles
- #DataTemplates

  • Each section for each type should be labeled with a comment that indicates the type of the resources it includes. I also like to ‘prefix’ each section name with # or ##, this helps you quickly find these section headers by using CTRL+F.
  • Resources that are not shared should be declared in line within the scope of the element that contains them.
  • If a resource is shared among just a few elements, it can be moved closer to the elements that use it.
  • All resources should have an x:Key attribute.
  • For resource keys, use Pascal casing and follow the same meaningful postfix convention we used for x:Names.
  • When creating implicit styles, use the x:Key attribute.
  • This reads consistently, and makes it easy to search for x:Key="{x: if you want to go find implicit styles.
  • Keep resources files small (but not too small) Generic.xaml and other theme resource dictionaries can get large. If you don’t want to manage large files, you can partition them into smaller resource dictionaries and use the MergedResourceDictionary feature to pull them all together. For App.xaml, using MergedResourceDictionaries is a must. It really helps navigate the logical organization.
  • When merging resource dictionaries, follow the order of reuse, with the most-reusable items at the top.
  • When merging a resource dictionary and local (scene) resources, always include your local resources last.
  • In your application, merge your resource dictionaries in App.xaml.
  • For projects where you do not have access to App.xaml (for example, a control library) or for scenarios where a scene includes resource dictionaries that are not in XAML, merge resource dictionaries by using code. Alternatively, create a subclass for ResourceDictionary that merges resources dictionaries by picking resource dictionaries from a singleton that caches the ResourceDictionary instance.

Naming Conventions

There are also two scenarios for naming: elements within a scene and elements within a template, in particular a ControlTemplate.

Naming Elements within a Scene

  • For scenes that will be designed by a human designer (not auto-generated and not just by a developer), all logical elements in the scene should be named.
  • When assigning a name, use the x:Name attribute for consistency.
  • Be consistent in your naming.
  • Use Pascal casing. It is more readable, it involves less typing of unnecessary prefixes, and almost truly represents the behavior, because the generated members are internal.
  • Provide meaningful names.
  • Make the x:Name attribute the first attribute after a type instance.
  • Postfix names with the type of the element.

Naming Elements within a Template

  • Within a template, name only the elements that absolutely need names. This includes the template parts that the control expects, the elements used in triggers, animations, named bindings, and so on.
  • Another difference between this scenario and naming elements in the scenes is the use of the PART_ prefix for template parts that are expected by the control. All the parts that the control expects and cannot work without should have the PART_ prefix. Nothing else should have the prefix.