May 11, 2009

Editing Model & Default Initializer for Silverlight Controls

Introduction

This is part of the series on design time feature implementation in Silverlight Toolkit. This post uses the Chart default initializer to illustrate how to implement default initializer for Silverlight controls, and explain the underlying editing model architecture.

Experience

If you install Silverlight 3 Toolkit in March 2009 Release, you can drag the Chart control from Blend Asset Library and drop it onto designer surface, and you have a chart nicely initialized and rendered:

Chart default initializer

You can read more about it in prior post Silverlight Toolkit Design Time Features: March 2009 Release Update.

Architecture

DefaultInitializer

It is actually pretty simple to provide a default initializer for a Silverlight control:

Below screenshot shows the implementation of DefaultInitializer abstract base class, and other classes in Microsoft.Windows.Design.Model namespace that will be discussed shortly:

DefaultInitializer

Initialization is done in InitializeDefaults override, and result is serialized into xaml. So the first step in implementing a default initializer is to define the desired xaml to be produced.

Editing Model

Once the result xaml is defined, I wish there is a higher level abstraction/method that would just take the xaml. But unfortunately we have to use a much lower level (thus more flexible and powerful) imperative API called editing model that consists of classes like ModelItem, ModelProperty, ModelEvent, ModelFactory and ModelService. The MSDN page Editing Model Architecture gives an basic overview:

Your design-time implementation interacts with run-time controls though a programming interface called the editing model. The objects being designed are called editable objects.

Your controls are defined in Extensible Application Markup Language (XAML). You update the XAML for your controls programmatically by using the editing model.

Model, Wrapper, and View

The editing model consists of three functional subunits: a model, a public wrapper that abstracts the model, and a view that represents the user interface (UI) of the model. The model and the view are separate, but the wrapper and model are closely related. The following illustration shows the relationship among the three subunits.

Model, ModelItem, and View relationships

The design environment uses the ModelItem type to communicate with the underlying model. All changes are made to the ModelItem wrappers, which affect the underlying model. This allows the model to be simple. The ModelItem wrappers handle complex designer features, such as transaction support, undo tracking, and change notifications.

Below class diagram may help explain interactions among core classes of the editing model:

Editing Model Class Diagram

Use below xaml as an example:

<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ModelTest.Page" Width="640" Height="480">

<Grid x:Name="LayoutRoot" Background="White">
<Button x:Name="Button">
<Rectangle x:Name="Rectangle" />
</Button>
</Grid>
</UserControl>
  • ModelItem: assume variable item is the ModelItem for the Button control above,
    • Name is persisted as x:Name attribute in xaml: item.Name = “Button” is persisted in xaml as x:Name = “Button”.
    • ItemType is the Type object of the underlying control, and decides the tag used in xaml: item.PropertyType == tyepof(Button), and the xaml tag is <Button />.
    • Properties is the collection of properties, wrapped in ModelProperty type, of the underlying control. For example: item[“ClickMode”] = ClickMode.Release will be persisted in xaml as ClickMode="Release".
    • Content represents the Content property defined in ContentControl (and inherited by its subclasses): item.Content is the ModelItem representing the Rectangle object.
    • Source: when a ModelItem represents an element that’s a property of another element, Source is the ModelProperty wrapper of that property. For example, item.Content.Value.Source == item.Content // the Rectangle.
    • Events represents the collection of events of the underlying control, wrapped in ModelEvent type. From my debugging experience, it is always null, so it seems Events property is not yet supported by Blend3 for Silverlight3 yet.
    • Parent is the the logic parent of the underlying control, wrapped in ModelItem type. ex: item.Parent is the ModelItem for Grid.
    • Root is the ModelItem wrapper for root visual, ex: item.Root represents the UserControl.
  • ModelProperty: assume variable prop is the ModelProperty for Button.Content, i.e. prop = item.Content,
    • Name is the name of the property of the underlying control. It is persisted in xaml as attribute name. ex: prop.Name == “Content”.
    • PropertyType is the Type object of the property of the underlying control this ModelProperty object represents. ex: prop.PropertyType = typeof(object).
    • AttachedOwnerType is the Type object of the control that first defines the property this ModelProperty object represents. ex: prop.AttachedOwnerType == typeof(ContentControl) // note: not Button.
    • Value is the value of the property of the underlying control, wrapped in ModelItem type. ex: prop.Value is the ModelItem representing the Rectangle object.
    • Parent is the control, wrapped in ModelItem type, of which the property belongs to. ex: prop.Parent == item.
  • ModelEvent: as mentioned above, item.Events is always null, so it doesn’t seem that ModelEvent is supported in Blend3 for Silverlight3 yet. But assume it works, and variable evt is the ModelEvent for Button.Click, i.e., evt = item.Events[“Click”],
    • Name is the name of the event. ex: evt.Name == “Click”.
    • EventType is the type of the event delegate, ex: evt.EventType == typeof(RoutedEventHandler).
    • Handlers is the string collection of event handlers, ex: evt.Handlers = new string[] { “Button_Click” } will be persisted as Click = “Button_Click”. Note: this is pure speculation, since ModelEvent isn’t yet supported, and I don’t know whether/when/how it will be supported for Silverlight.
    • Parent is the control, wrapped in ModelItem type, of which this event belongs to. ex: evt.Parent == item.

Implementation

The Chart default initializer class ChartDefaultInitializer is implemented in ChartDefaultInitializer.cs, and registered in ChartMetadata.cs. Both files are in Controls.DataVisualization.Toolkit.Design.csproj of Silverlight.Controls.Design.sln. Please read prior post Design Time Feature Implementation in Silverlight Toolkit for more information.

Result XAML

As the first step, define the result xaml to be produced by ChartDefaultInitializer:

   1: <Charting:Chart Title="Chart Title">
   2:     <Charting:Chart.DataContext>
   3:         <PointCollection>
   4:             <Point X="1" Y="10" />
   5:             <Point X="2" Y="20" />
   6:             <Point X="3" Y="30" />
   7:             <Point X="4" Y="40" />
   8:         </PointCollection>
   9:         <Charting:Chart.Series>
  10:             <Charting:ColumnSeries ItemsSource="{Binding}"
  11:                 DependentValuePath="X"
  12:                 IndependentValuePath="Y" />
  13:         </Charting:Chart.Series>
  14:     </Charting:Chart.DataContext>
  15: </Charting:Chart>

Registration

Done in ChartMetadata.cs with following code:

b.AddCustomAttributes(new FeatureAttribute(typeof(ChartDefaultInitializer)));

References

See below screenshot:

References

Even though DefaultInitializer is defined in version 3.5 of Microsoft.Windows.Design.Interaction.dll (under %devenvdir%\PublicAssemblies, i.e. c:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies on 32bit Windows), it is not supported by Blend2 or Visual Studio 2008 for Silverlight. It is supported by Blend3 and Visual Studio 2010, but they both switch to newer version of Microsoft.Windows.Design*.dll (3.7 for Blend 3 Preview, 4.0 for Blend 3 RTM and Visual Studio 2010), so we need to link against the newer version of MWDs. Please see prior post How to Write Silverlight Design Time for All Designers: Visual Studio 2008, Blend 2; Blend 3, and Visual Studio 2010 for more information.

Please also notice that reference to Silverlight’s System.Windows.dll is aliased, and the PointCollection is specifically called out to be the Silverlight one, not WPF’s. Since WPF and Silverlight share a lot of classes in same namespaces, it is important that Silverlight types, not WPF’s, are used in creating ModelItem and ModelProperty in default initializer for Silverlight controls.

Collections

Please note in below screenshot that while all other property value can be set via ModelProperty.SetValue:

ChartDefaultInitializer

Chart.Series is of collection type (Collection<Series>), so its value has to be set by first creating a ModelItem via ModelFactory.CreateItem for the value, and then add the created ModelItem via ModelProperty.Collection.Add. Otherwise, even though the correct xaml may be generated, Blend won’t refresh to render the Chart control correctly, you have to reload the page to have it rendered correctly.

ChartDefaultInitializer.cs

// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

extern alias Silverlight;
using System.Windows.Controls.DataVisualization.Charting;
using System.Windows.Controls.Design.Common;
using Microsoft.Windows.Design.Metadata;
using Microsoft.Windows.Design.Model;
using SSW = Silverlight::System.Windows;
using SSWD = Silverlight::System.Windows.Data;
using SSWM = Silverlight::System.Windows.Media;

namespace System.Windows.Controls.DataVisualization.Design
{
/// <summary>
/// Default initializer for chart.
/// </summary>
internal class ChartDefaultInitializer : DefaultInitializer
{
/// <summary>
/// Sets the default property values for chart.
/// </summary>
/// <param name="item">Chart ModelItem.</param>
public override void InitializeDefaults(ModelItem item)
{
string propertyName;

// <Charting:Chart Title="Chart Title">
propertyName = Extensions.GetMemberName<Chart>(x => x.Title);
item.Properties[propertyName].SetValue(Properties.Resources.ChartTitle);

// <Charting:Chart.DataContext>
// <PointCollection>
// <Point X="1" Y="10" />
// <Point X="2" Y="20" />
// <Point X="3" Y="30" />
// <Point X="4" Y="40" />
// </PointCollection>
// </Charting:Chart.DataContext>

SSWM::PointCollection defaultItemsSource = new SSWM::PointCollection();
for (int i = 1; i <= 4; i++)
{
defaultItemsSource.Add(new SSW::Point(i, 10 * i));
}

propertyName = Extensions.GetMemberName<Chart>(x => x.DataContext);
item.Properties[propertyName].SetValue(defaultItemsSource);

// <Charting:Chart.Series>
// <Charting:ColumnSeries ItemsSource="{Binding}"
// DependentValuePath="X"
// IndependentValuePath="Y" />
// </Charting:Chart.Series>

ModelItem columnSeries = ModelFactory.CreateItem(item.Context, typeof(ColumnSeries));
propertyName = Extensions.GetMemberName<ColumnSeries>(x => x.ItemsSource);
columnSeries.Properties[propertyName].SetValue(ModelFactory.CreateItem(columnSeries.Context, typeof(SSWD::Binding)));
propertyName = Extensions.GetMemberName<ColumnSeries>(x => x.DependentValuePath);
columnSeries.Properties[propertyName].SetValue("X");
propertyName = Extensions.GetMemberName<ColumnSeries>(x => x.IndependentValuePath);
columnSeries.Properties[propertyName].SetValue("Y");

propertyName = Extensions.GetMemberName<Chart>(x => x.Series);
item.Properties[propertyName].Collection.Add(columnSeries);
}
}
}

 

 

May 8, 2009

Migrate Windows Live Writer Data

I am re-imaging my laptop with Windows 7 RC: it is so much fun and productivity boost to spend a day or two re-installing and re-configuring OS and apps :-) One of my favorite apps is Windows Live Writer, so I need to migrate all my Windows Live Writer settings and data. This is what I did:

  • robocopy posts under “%userprofile%\Documents\My Weblog Posts”
  • copy auto linking entries in “%appdata%\Windows Live Writer\LinkGlossary\linkglossary.xml”

For those curious, this is where Windows Live Writer settings are stored:

  • posts:
    WLW posts directory
  • User dictionaries, keywords and auto links:
    WLW config directory
  • other settings in registry:
    WLW settings in registry

For those even more curious, this is how I found out where those settings are, besides web search:

  • configuration is usually persisted in registry and/or files, and those registry and files usually follow well known path convention. Registry is usually a good start. For files, try %appdata% and/or "%userprofile%.
  • use sysinternalsprocmon to see what registry and files a process is accessing:
    procmon on linkglossary.xml
  • for managed code, load all their assemblies (usually under %programfiles%) into reflector, and see how it is implemented:
    reflector on LinkGlossary
    Happy blogging, and hacking :-)

Technorati Tags:

May 4, 2009

Photoblog with WordPress

Introduction

I’ve been using Windows Live Spaces for personal blogging, but since I moved my work blog to WordPress, I wanted to build my personal blog with WordPress too. WordPress is a great blog software, but features for photo blogging, which my personal blog is mostly about, are lacking out of box. So I spent the past weekend experimenting with various desktop software and WordPress plugins, and here is the result:

photoblog: live spaces, wp-simpleviewer, nextgen gallery 

Basically my photoblog solution consists of:

  • Build my WordPress blog site with WP-SimpleViewer and NextGEN Gallery plugins
    • WP-SimpleViewer for slow connection (< 1mbps)
    • NextGEN Gallery for fast connection (>10mbps), gallery, and widgets
  • Manage photos locally using Windows Live Photo Gallery 
  • Backup photos remotely on both Live Spaces and my WordPress site
  • Write blogs and cross post them to both Live Spaces and WordPress site using Windows Live Writer

WP-SimpleViewer

WP-SimpleViewer plugin “enables you to easily add fancy SimpleViewer Flash galleries to your posts and pages. The admin interface helps you to create the thumbs for a new gallery and change its settings.” The slideshow in the middle of the screenshot above is generated with WP-SimpleViewer. Besides the fancy UI and easy keyboard navigation for in-page photo viewing, the biggest benefit, to me at least, is that WP-SimpleViewer generates 20-30KB sized scaled down photos under reg folder, and 1-2KB sized thumbnails under tn subfolder, and display those photos instead of the originals in the slide show. This allows very slick photo viewing experience even on slow connections (<1mbps).

It is pretty easy to install WP-SimpleViewer, but making it work isn’t so trivial. By default, it always hangs during gallery creation. The fix is to uncomment line 101 of wp-simpleviewer-admin.php:

/*If the plugin should hang up while it is creating the thumbs (last line on the admin is: "Now I am working on file xzy.jpg:") you
can try to uncomment the following line (remove // ) to define a new memory limit (40MB in this case). More info on the plugins FAQ.*/
//ini_set("memory_limit","40M");

I changed it to ini_set(“memory_limit”, “120MB”). I also increased the limits in php.in:

;;;;;;;;;;;;;;;;;;;
; Resource Limits ;
;;;;;;;;;;;;;;;;;;;

max_execution_time = 30     ; Maximum execution time of each script, in seconds
max_input_time = 60    ; Maximum amount of time each script may spend parsing request data
memory_limit = 128M      ; Maximum amount of memory a script may consume (32MB)

But even with those changes, WP-SimpleViewer still hangs when there are a lot of photos in one gallery.

Another trick I did is to set the images directory to wp-content/gallery instead of the default wp-content/photos in WP-SimpleViewer’s admin page, so the same original photos can be used by both WP-SimpleViewer and NextGEN Gallery.

NextGEN Gallery

NextGen Gallery is probably the most popular image gallery plugin for WordPress. It has a basic set of image gallery functions, like albums, tags, roles etc, and a set of shortcode for different rendering. NextGEN’s slideshow and PicLens view both display the original photos. My photos are usually 2-3MB in size, so they look great with fast connection (>10mpbs), and can be displayed with cooliris browser addin (need separate install) as shown below:

cooliris view

But that doesn’t really work with slow connections (<1mbps): those MB sized photos take forever to download on slow connection, so I usually put both WP-SimpleViewer and NextGEN shortcode on a page. Besides the nice rendering UI, NextGEN Gallery also comes with two nice widgets: the NextGEN Slideshow and NextGEN Gallery widget, as shown at the top left corner of my personal blog screenshot above.

Just like WP-SimpleViewer, it is pretty easy to install to NextGEN Gallery, but it takes some effort to make it work. Besides modifying php.ini as mentioned above, I also need to uncomment line 168 of lib/gd.thumbnail.inc.php:

lib\gd.thumbnail.inc.php:168:           // @ini_set('memory_limit', '128M');

Once working, it seems more reliable than WP-SimpleViewer.

Windows Live Photo Gallery

I used to use Picasa, but after trying out the new Windows Live Photo Gallery 2009, I am really impressed. The features I really like about Windows Live Photo Gallery:

  • easy navigation with folder, date, people and tag hierarchical views
  • tagging, rating, and people tagging features
  • metadata in the info panel
  • fix functions, especially straighten photo: I am getting into the habit of applying it on all photos :-)
  • easy publishing to live spaces, and other photo sharing sites (require plugin)
  • and the publishing plugin platform

Windows Live Writer

Windows Live Writer is probably the best blog authoring tool. The nice new feature related to photo blogging is the Insert –> Photo album… function:

Window Live Writer - Insert Photo Album Dialog

It is nicely integrated with Live Spaces, allowing users to browse their existing photo albums or create new one, and insert it into blog post with four different layout. In my personal blog screenshot above, the top photo album in scatter view is inserted via Insert –> Photo album. You can find more from Windows Live Writer blog, like post Windows Live Writer 2009: Now Available for Download.

Photo in Live Spaces are also doubled scaled for easy browsing on both slow and fast connections, just like WP-SimpleViewer: thumbnail and regular photos are displayed, but user can also click on a photo to get the original version. Live Spaces allows 25GB for photo storage, so it can be used as an online storage for photos.

Conclusion

I am by no means a blog or photoblog expert, and don’t really know WordPress, PHP, or the various Live services as a developer. This post is just to share what I’ve learned about photo blogging with WordPress. I am actually quite happy with what I’ve managed to do over the weekend. It is good enough for me for now, photo blogging wise.