Please visit http://www.ningzhang.org!

This is a backup site. Please go to http://www.ningzhang.org for the latest content and best viewing. Thanks!

Nov 30, 2009

Silverlight Design Time Assemblies

Introduction

If you write Silverlight controls, you should consider writing design time assemblies for your controls too, for two simple reasons:

  • developer productivity: try to imagine Silverlight development without tools like Visual Studio or Blend! For custom controls, you may need to provide much of the design time experience for your controls in Visual Studio or Blend yourself.
  • designers: XAML and tools like Blend enable developers and designers work together. A key design criteria for Silverlight controls is to make sure designers can use them without writing a single line of code.

Design time experience usually includes (but not limited to) the following:

  1. Metadata for property window, like category, infotip, property editor, binding etc.
  2. Metadata for design surface, like initializer, adorner, context menu, adapters etc.
  3. Toolbox integration, like icons, control registration
  4. intellisense for code editor

Except intellisense (please see Add Rich Intellisense for Your Silverlight Controls) and control registration (please see Register Silverlight Controls with visual Studio and Blend), above design time experience are usually delivered through design time assemblies. Below I will discuss various approaches of delivering design time experiences, in increasing complexity and flexibility, and gradually introduce pieces of the naming convention of design time assemblies.

Runtime Assembly Only

The simplest way to deliver design time experience is to package design time code into the runtime assemblies, especially when design time metadata are meaningful at runtime too, like TypeConverterAttribute.

The pros and cons of this approach:

  • Pro: simple
    • no separate design time assemblies, simpler setup
    • design time attributes are specified directly on the runtime code, easier to maintain
  • Con: tight coupling of runtime and design time code
    • perf degradation because of useless design time code at runtime
    • design time dependencies (like MWDs and other VS/Blend assemblies) get dragged into runtime unnecessarily
    • can’t service runtime or design time independently
    • can’t support multiple designers (like both VS2008/Blend2 and VS2010/Blend3)
    So this approach is highly discouraged, unless there is strong justification for it.

Shared Design Time Assembly

So the revolutionary step forward is to decouple design time and runtime code, release and service them with separate assemblies. This opens up all kinds of possibilities. Of course, we need a way to link design time assemblies to their corresponding runtime assemblies, without introducing any unnecessary dependency or perf degradation to runtime assemblies. Hence the naming convention: if a runtime assembly Foo.dll is referenced in a Silverlight project, the designer (like Visual Studio and Blend) will first try to load design time information like icons (via another naming convention, see How to Add an Toolbox Icon for Your Silverlight Control) and design time metadata (via interface, like IRegisterMetadata for VS2008 and Blend2, or IProvideAttributeTable for VS2010 and Blend3. Please see How to Write Silverlight Design Time for All Designers: Visual Studio 2008, Blend 2; Blend 3, and Visual Studio 2010) from the runtime assembly; it will then look for a design time assembly by the name Foo.Design.dll in the same directory as Foo.dll; if found, the designer will try to load design time information from Foo.Design.dll as well.

Designer Specific Design Time Assembly

Visual Studio is mostly for developers, while Blend is mostly for designers, so they have difference requirements for design time experiences. Putting all design time code in one shared design time assembly introduces tight coupling among designers. So the naming convention is enhanced: for runtime assembly Foo.dll, there is a shared design time assembly Foo.Design.dll that’s loaded by all designers; each designer will also try to load its own design time assemblies, like Foo.VisualStudio.Design.dll for Visual Studio, and Foo.Expression.Design.dll for Blend. The designer specific design time assembly is loaded after the shared design time assembly. Third party designers can define their own designer specific design time assembly. 

Design Sub Folder

So to support both Visual Studio and Blend, each runtime assembly has three design time assemblies, in the same directory, and a package (like Silverlight SDK or Silverlight Toolkit) usually contains several runtime assemblies. So the directory gets a bit crowded. A minor improvement is to put the design time assemblies under a Design subfolder. So the naming convention is further enhanced: if a designer (like Visual Studio or Blend) can’t find corresponding design time assemblies in the same directory as a runtime assembly, it will look for them in the Design subfolder, if it exists. 

Support Multiple Versions of MWDs

Design time are built on top of designer extensibility framework, which consists of several dlls like Microsoft.Windows.Design.Extensibility.dll and Microsoft.Windows.Design.Interaction.dll. We usually call them collectively as MWDs. To make life more interesting, sometimes we have to introduce breaking changes to the extensibility framework, like VS2008 and Blend2 use MWD version 3.5, VS2010 and Blend3 use MWD version 4.0, and they are incompatible. So if you have a runtime assembly Foo.dll, and you want your users to be able to develop against it with both VS2008 and VS2010, you have to provide two sets of design time assemblies: one set built against v3.5 MWDs and used by VS2008, and another set built against v4.0 MWD and used by VS2010. The two sets of design time assemblies probably have a lot of code in common, while the one for VS2010 may have some new code to leverage the new functionalities exposed by VS2010. Since design time assemblies are loaded by name and you can’t have two files with the same name, so the naming convention is enhanced yet again:

for a runtime assembly Foo.dll, the shared design time assembly is named Foo.Design*.dll, the Visual Studio specific design time assembly is named Foo.VisualStudio.Design*.dll, and the Blend specific design time assembly is named Foo.Expression.Design*.dll, where * can be zero or more valid characters for file names. When a designer (like Visual Studio or Blend) tries to load a design time assembly and several fit the naming convention, zero or one will be loaded:

  • If the MWD version referenced by the design-time assembly has a different major version number than the designer’s MWD version, then the design-time assembly will not load and is bypassed.
  • If more than one design-time assembly is compatible with the designer’s MWD version, the Designer loads the one compiled against the highest MWD version that is less than or equal to the designer’s MWD version.

Please see Extensibility Series – WPF & Silverlight Design-Time Code Sharing – Part I and How to Write Silverlight Design Time for All Designers: Visual Studio 2008, Blend 2; Blend 3, and Visual Studio 2010 for more information.

Support Both WPF and Silverlight

To complicate life further, since WPF and Silverlight are so awfully similar, you may be tempted to try to write it once and run with both WPF and Silverlight. You are not alone. There are many articles on how to share source code and/or binaries across Silverlight and WPF. We’d like to do that for design time assemblies too. One approach is to make most of the design time assemblies platform agnostic, and limit platform specific (WPF or Silverlight) code and references to a small platform specific assembly. Please see Extensibility Series – WPF & Silverlight Design-Time Code Sharing – Part I for more information on this.

Last One Wins

The same design time metadata for a class or its member can be specified multiple times in multiple design time assemblies, which allows the shared design time assembly to specific the default/common behavior and then designer specific design time assemblies to override it if necessary. The same design time metadata can also be specified multiple times within a single design time assembly (please see Design Time Feature Implementation in Silverlight Toolkit as an example, where DescriptionAttribute for a class or its property can be added by AddDescriptions, AddAttributes and AddTables methods). So we need to know which metadata wins. The simplest and most logic design is that last one wins. This is mostly true but not always. Sometimes the result is un-deterministic when the same design time metadata is specified several times but with different values.

Feedback

Devil is in the details! Feedbacks are always appreciated. Please let me know what issues you run into, what requests/improvements you’d like to make, for both design times experience and designer extensibility framework. Thanks!

 

Nov 28, 2009

Silverlight Drag and Drop API

Introduction

Drag and drop is a common UX paradigm and a top requested feature. In Silverlight 4 Beta, we introduced a basic set of API to enable the most common scenario of dragging pictures files and dropping them to Silverlight applications, and we designed the API to allow us to expose more drag and drop functionalities down the road without API change, hopefully.

API

Below is the core of the new API:

namespace System.Windows
{
public abstract class UIElement : DependencyObject
{
// Fields
public static readonly DependencyProperty AllowDropProperty;

// Events
public event DragEventHandler DragEnter;
public event DragEventHandler DragLeave;
public event DragEventHandler DragOver;
public event DragEventHandler Drop;

// Properties
public bool AllowDrop { get; set; }
}

public delegate void DragEventHandler(object sender, DragEventArgs e);

public sealed class DragEventArgs : RoutedEventArgs
{
// Methods
public Point GetPosition(UIElement relativeTo);

// Properties
public IDataObject Data { [SecuritySafeCritical] get; }
public bool Handled { get; set; }
}
}

namespace System.Windows.Controls
{
public abstract class Control : FrameworkElement
{
protected virtual void OnDragEnter(DragEventArgs e);
protected virtual void OnDragLeave(DragEventArgs e);
protected virtual void OnDragOver(DragEventArgs e);
protected virtual void OnDrop(DragEventArgs e);
}
}

  • AllowDrop: this is dependency property, indicating whether an element is a drop target.
  • DragEnter, DragLeave, DragOver & Drop: these are routed events. They bubble up, and fire only on elements with AllowDrop set to true.
  • OnDragEnter, OnDragLeave, OnDragOver & OnDrop: these are protected virtual functions for subclasses to override. 
  • DragEventArgs: this class allows drop target event handlers or method overrides to access the data being dragged or dropped.

Usage

There are primarily two ways to use Silverlight’s drag and drop API:

  • Silverlight applications can handle those drop target events and process the files dropped in the event handlers.
  • Silverlight controls can override those drop target virtual functions to process the data being dragged and dropped, and enable themselves to be drop targets.

Limitations

The drag and drop functionality shipped in Silverlight 4 Beta only enables the most common scenario: Silverlight plugin as a file drop target. Please notice the following limitations with current implementation:

  • there is no drop source support (QueryContinueDrag & GiveFeedback).
  • there is no DragDropEffects or DragDropKeyStates in DragEventArgs.
  • there is no visual for the dragged object or DragDropEffects.
  • only file drag and drop is supported:
    • all drop target events fire only when files are being dragged and dropped.
    • IDataObject, DataObject and DragEventArgs.Data support only one format: “FileDrop”, and the data is of type FileInfo[].
  • most of IDataObject methods throw NotImplementedException.
  • for drag and drop to work on Windows, Silverlight plugin must be windowed. In another word, if <param name="windowless" value="true"/> is specified, drop events won’t fire.
  • since Silverlight plugin on Mac is always windowless, you need to hook up JavaScript drop events to trigger Silverlight drop events. In another word, if you want your Silverlight application to support file drop on Mac as well as Windows, you will need to add following script and Silverlight plugin attributes to the hosting html or aspx page:
    <!-- add following script before end of HEAD tag -->
    <script type="text/javascript">
    function handleDragEnter(oEvent) {
    // Prevent default operations
    oEvent.preventDefault();

    var flag = silverlightControl.dragEnter(oEvent);

    // If we handled it successfully then stop propagation of the event
    if (flag)
    oEvent.stopPropagation();
    }

    function handleDragLeave(oEvent) {
    // Prevent default operations
    oEvent.preventDefault();

    var flag = silverlightControl.dragLeave(oEvent);

    // If we handled it successfully then stop propagation of the event
    if (flag)
    oEvent.stopPropagation();
    }

    function handleDragOver(oEvent) {
    // Prevent default operations
    oEvent.preventDefault();

    var flag = silverlightControl.dragOver(oEvent);

    // If we handled it successfully then stop propagation of the event
    if (flag)
    oEvent.stopPropagation();
    }

    function handleDropEvent(oEvent) {

    // Prevent default operations
    oEvent.preventDefault();

    var flag = silverlightControl.dragDrop(oEvent);

    // If we handled it successfully then stop propagation of the event
    if (flag)
    oEvent.stopPropagation();
    }
    </script>
    <!-- use JavaScript drop target events to trigger Silverlight's drop target events -->
    <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%"
    id="silverlightControl" ondragenter="handleDragEnter(event)" ondragover="handleDragOver(event)"
    ondragleave="handleDragLeave(event)" ondrop="handleDropEvent(event)" >


  • DragEventArgs.Data is only accessible in Drop event. Accessing DragEventArgs.Data in DragEnter, DragOver and DragLeave may cause SecurityException. And accessing Directory, DirectoryName or FullName property of the FileInfo object from DragEventArgs.Data may cause ArgumentNullException. 

    Demo App

    Below is a demo app:
  • the top left nested grids use drop target event handler to display DragEventArgs info.
  • the top right nested grids contain an ImageDropTarget user control, which overrides OnDrop method to render the dropped file if it is an image.
  • the bottom listbox shows the sequence of drop target events, their bubbling up, and DragEventArgs info.
    DragDropDemo

    you can find the complete source here.

    Feedback

    While we are excited to add drag and drop to Silverlight, we know there are a lot to finish/improve, and what we released is just a Beta. As always, your feedback is highly appreciated, and will be used as an important data point to help us prioritize the work. Thanks!

     

    Technorati Tags: ,

    Nov 18, 2009

    Silverlight 4 Adds Arabic Support

    Silverlight 4 adds support for Arabic culture:

    • Install, configuration and runtime UI are in Arabic and RTL layout on Arabic culture
    • Support mixed BiDi/ComplexScript text
    • Support RTL layout with FlowDirection attribute and implemented by all controls

    Below screenshots demonstrate some of the support:

    • Install UI:
      Silverlight developer runtime install UI
    • Configuration UI:
      Silverlight Configuration UI
    • Controls with FlowDirection set to RightToLeft:
      RTL Layout and Controls Demo

    For those curious, you may notice that:

    • there is a new ar directory under %programfiles%\Microsoft Silverlight\4.0.41108.0, besides the 10 directories that have been there since SL3: de, en-us, es, fr, it, ja, ko, zh-Hans, zh-Hint. The ar directory contains Arabic resources for Silverlight runtime assemblies.
    • the MUI resource dlls contain Arabic resources:
      image

    We will continue adding more support to more cultures in Silverlight. The Arabic support debuted in Silverlight 4 Beta is particularly important, since it is the first RTL language Silverlight supports. If you notice any bug or have suggestions, please let me know.

    Technorati Tags: ,

    Silverlight Clipboard API

    Clipboard API in Silverlight

    Silverlight 4 adds Clipboard support. Check out the amazing demos by Scott Guthrie and Brian Goldfarb today at PDC that showcase scenarios enabled by Silveilght4 Clipboard support.

    Silverlight’s Clipboard API is a subset of WPF’s Clipboard API: 

    public static class Clipboard
    {
    public static bool ContainsText();
    public static string GetText();
    public static void SetText(string text);
    }
    It allows copy/paste Unicode strings to/from system clipboard. GetText and SetText require either of following to succeed:
    1. the calling Silverlight application is an elevated trust application, or
    2. it is user initiated and user grants clipboard access
    For the second case, when GetText or SetText is first called in an user initiated event handling, Silverlight pops up a dialog like the one below (UI may change at RTW):
      Clipboard Prompt
      If user clicks yes, Silverlight will allow this and later GetText/SetText calls to succeed for this application during this session; if user clicks no, the default, Silverlight will throw a SecurityException. In another word, the setting is per application, per session, for both get and set access to system clipboard, and it is not persisted.

    Clipboard API in JavaScript and WPF

    We need to consider many factors while designing Clipboard API for Silverlight. Among them, the Clipboard APIs of JavaScript and WPF are particularly interesting, because we need to study the security lessons in JavaScript and try to be compatible with WPF.

    There is no cross browser, cross platform JavaScript API for clipboard access. IE provides clipboardData object that allows get/set/clear strings in system clipboard:

    bResult = window.clipboardData.setData(sDataFormat, sData);
    sData = window.clipboardData.getData(sDataFormat);
    bResult = window.clipboardData.clearData([sDataFormat]);

    where sDataFormat is a string constant of "Text" or "URL", sData is a string, and bResult is a Boolean.

    The first time the clipboadData object is accessed, IE will prompt user to allow the script to access clipboard, and the setting is valid for the session only.

    WPF’s Clipboard API supports more and extensible data format via IDataObject, and has helper functions for common formats like text, image, audio, and filedrop.

    public static class Clipboard
    {
    // Methods
    [SecurityCritical]
    public static void Clear();
    public static bool ContainsAudio();
    public static bool ContainsData(string format);
    public static bool ContainsFileDropList();
    public static bool ContainsImage();
    public static bool ContainsText();
    public static bool ContainsText(TextDataFormat format);
    public static Stream GetAudioStream();
    public static object GetData(string format);
    [SecurityCritical]
    public static IDataObject GetDataObject();
    public static StringCollection GetFileDropList();
    public static BitmapSource GetImage();
    public static string GetText();
    public static string GetText(TextDataFormat format);
    public static bool IsCurrent(IDataObject data);
    public static void SetAudio(byte[] audioBytes);
    public static void SetAudio(Stream audioStream);
    public static void SetData(string format, object data);
    [SecurityCritical]
    public static void SetDataObject(object data);
    [SecurityCritical]
    public static void SetDataObject(object data, bool copy);
    public static void SetFileDropList(StringCollection fileDropList);
    public static void SetImage(BitmapSource image);
    public static void SetText(string text);
    public static void SetText(string text, TextDataFormat format);
    }

    Clipboard API in Windows and Mac OS X

    Silverlight’s clipboard API is ultimately implemented on top of the clipboard APIs of the underlying operating system. Windows’ clipboard APIs are documented at MSDN, below excerpt gives an overview on how to set/get clipboard data:

    Cut and Copy Operations

    To place information on the clipboard, a window first clears any previous clipboard content by using the EmptyClipboard function. This function sends the WM_DESTROYCLIPBOARD message to the previous clipboard owner, frees resources associated with data on the clipboard, and assigns clipboard ownership to the window that has the clipboard open. To find out which window owns the clipboard, call the GetClipboardOwner function.

    After emptying the clipboard, the window places data on the clipboard in as many clipboard formats as possible, ordered from the most descriptive clipboard format to the least descriptive. For each format, the window calls the SetClipboardData function, specifying the format identifier and a global memory handle. The memory handle can be NULL, indicating that the window renders the data on request. For more information, see Delayed Rendering.

    Paste Operations

    To retrieve paste information from the clipboard, a window first determines the clipboard format to retrieve. Typically, a window enumerates the available clipboard formats by using the EnumClipboardFormats function and uses the first format it recognizes. This method selects the best available format according to the priority set when the data was placed on the clipboard.

    Alternatively, a window can use the GetPriorityClipboardFormat function. This function identifies the best available clipboard format according to a specified priority. A window that recognizes only one clipboard format can simply determine whether that format is available by using the IsClipboardFormatAvailable function.

    After determining the clipboard format to use, a window calls the GetClipboardData function. This function returns the handle to a global memory object containing data in the specified format. A window can briefly lock the memory object in order to examine or copy the data. However, a window should not free the object or leave it locked for a long period of time.

    For now, Silverlight Clipboard API only supports CF_UNICODETEXT format for copy/paste Unicode text to/from clipboard.

    Mac OS X uses Pasteboard Manager. It is documented at Mac Dev Center. Below excerpt (with APIs added) gives an overview:

    The copying application is responsible for placing copied or cut data onto the pasteboard:

    1. The user selects some data and invokes the Copy (or Cut) menu item.

    2. If the application doesn’t already have a reference to the Clipboard pasteboard, it creates one (PasteboardCreate).

    3. The application then takes ownership of the pasteboard and clears the current contents (PasteboardClear).

    4. The application assigns item IDs to the selected data.

    5. If any data is to be promised, the application must register a promise keeper callback function to supply the promised data (PasteboardSetPromiseKeeper).

    6. The application adds one or more flavors of each item to the pasteboard, including either the actual flavor data or a promise with each flavor (PasteboardPutItemFlavor).

    The receiving application has a slightly different set of tasks to handle the Paste action:

    1. When the application becomes active, it checks to see if the pasteboard has been modified (PasteboardSynchronize). If so, it obtains a listing of the flavors on the pasteboard. If there are any flavors the application supports, it can enable its Paste menu item.

    2. The user invokes the Paste menu item.

    3. The application requests the item (or items) on the pasteboard in the flavors that it supports (PasteboardGetItemCount, PasteboardGetItemIdentifier, PasteboardCopyItemFlavors, PasteboardCopyItemFlavorData).

    4. If the pasted data is to be stored as a file, the receiving application needs to set a paste location before requesting any flavor data. In any other case, the receiving application doesn’t need to worry about whether the paste data was promised or not.

    If the copying application’s promise keeper is called, the callback must do the following:

    • If the data is to be stored as a file, determine the paste location specified by the receiving application.

    • Generate or otherwise prepare the promised data for transfer.

    • If the promised data is not be stored as a file, add the flavor and data to the pasteboard. Otherwise, transfer the promised data to the specified file location.

    Some time later, when the application quits, or when it no longer needs the pasteboard, the application can release the pasteboard reference.

    As on Windows, Silverlight Clipboard API only uses kPasteboardClipboard and kUTTypeUTF16PlainText flavor to support copy/paste Unicode text to/from clipboard.

    Feedback

    We are excited to add clipboard support to Silverlight, and see all the important scenarios enabled by this simple API. I am interested in hearing your feedbacks, especially:

    • any security concerns over Silverlight clipboard API design and implementation
    • how important it is to add support for other data type/format, and in what priority order
    • how important it is to make the API extensible, like supporting IDataObject, allowing custom format/flavor and delayed rendering/promised data

    Thanks!

    -Ning

    Technorati Tags: ,

    Oct 21, 2009

    Silverlight Design Time: Toolkit October 2009 Release Update

    Overview

    Visual Studio 2010 Beta 2 is available now. For Silverlight developers, the most exciting news about VS2010 is that Silverlight designer will be in feature parity with WPF designer, and VS2010 will support multi-targeting for Silverlight development. Silverlight Toolkit team has been working closely with Cider team in providing the design time experience for Silverlight SDK and Toolkit controls. The design time for Silverlight 3 SDK controls has been released with SL3 SDK in August 2009, and is also chain installed by VS2010 Beta2. The design time for Toolkit controls, together with sources for both SDK and Toolkit controls, and their design time, samples, unit tests etc, are in Silverlight Toolkit October 2009 Release. We purposely made October 2009 release to be in sync with Visual Studio 2010 Beta 2 on Monday, 10/19/2009. This post demonstrates the new design time experiences for Silverlight controls in VS2010. 

    Install

    VS2010 installs side by side with VS2008. Multiple releases of Toolkit install side by side too. So it is perfectly safe to install VS2010 and Toolkit October 2009 release on your main machine. The only caveat is that if you have a pre August 2009 version of Silverlight 3 SDK installed, you will need to uninstall it first, before installing VS2010. You can install VS2010 Beta2 from http://msdn.microsoft.com/en-us/vstudio/dd582936.aspx, and Silverlight Toolkit from http://silverlight.codeplex.com.

    Walkthrough

    The October 2009 release is for Silverlight 3 only. It provides design time support for both VS2008 and VS2010, as well as Blend 3. Below walkthrough will be VS2010 only. Design time experience for VS2008 and Blend 3 are similar to July 2009 release. Please see my previous posts for design time experience with VS2008 and Blend3.

    • Create a new Silverlight Application project in VS2010, notice that:
      Create New Silverlight Application Project
      • the toolbox is populated with both SDK and Toolkit controls, with nice icons. If you have Blend3 installed before Toolkit, you may see a lot of Blend controls on the toolbox that are not supposed to. You can safely remove them by right click a control in the toolbox and select delete.
      • it is a real designer: both the design view and the properties window work!
    • double click TabControl in the toolbox will add a tabControl1. Notice that:
      Add Tab Control from Toolbox
      • A reference to System.Windows.Controls.dll is added, together with a xmlns definition: xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
      • tabControl1 is initialized with a tabItem1, which itself is initialized with a header and <Grid/> content. (via DefaultInitializer)
      • Property Items is selected in the Properties window (via DefaultPropertyAttribute)
      • Properties are categorized (via CategoryAttribute), and have tooltip (via DescriptionAttribute)
    • Double click tabControl1 will add an event handler for SelectionChanged event (via DefaultEventAttribute)
      Default Event
    • right click tabControl1, select Add Tab (via PrimarySelectionContextMenuProvider):
      VS10SL3TabAddTabCM
    • A new tabItem2 is added. Notice that:
      VS10SL3TabAddTabResult
      • tabItem2 is initialized with a <Grid/> content
      • tabItem2’s default property Header is highlighted. You can type into the text box next to it and change the object typed Header property directly. (via TypeConverterAttribute)
      • all properties have nice infotip
    • double click DatePicker on toolbox:
      VS10SL3DatePickerAdd
    • Now click tabItem1, notice that tabItem2’s content is hidden and tabItem1’s is shown:
      VS10SL3TabItemSelect
    • click Chart on toolbox:
      VS10SL3TabAddChartpng
      • a chart1 is added to the right place, initialized with sample data, and nicely rendered
      • default property Series is selected in the properties window
    • click the … button next to Series property to pop up the Collection Editor:
      VS10SL3ChartSeries
      • the Select item combobox is populated with the correct types (via NewItemTypesAttribute). The pink background for the icons are fixed now.
      • the Properties window for the series highlights the default property DependentValuePath, have all properties categorized (like the Data Visualization category), and you can modify the object typed Title property directly in the text box.
    • Create a simple data class with some simple properties, and add it as a data source, via Data |  Show Data Sources menu item: 
      VS10SL3Binding1
    • Drag MyData over tabItem1’s header and drop it :
      Data Binding
    • Just for fun, open the project in Blend 3, you will see very similar design time behaviors:
      VS10SL3Blend3

    Conclusion

    Above walkthrough is only a peek into all the design time features we have implemented for VS2010. I am very excited that VS2010 now provides a real designer for Silverlight, and I will continue enriching the design time experience for Silverlight in VS and Blend. Your feedback is welcome.

    I will write a series of posts explaining how those design time experience are implemented.

    Sep 29, 2009

    Silverlight Design Time: Toolkit July 2009 Release Update

    I have been very busy and haven’t post for a while, so this is a quick late post on Toolkit July 2009 Release. This release is the last Toolkit release for Silverlight 2: when a new version of Silverlight RTMs, we drop support for the previous version, so each Toolkit release needs to support at most two versions of Silverlight. As for design time feature updates, there have been a lot of improvements over March 2009 Release. I will use a simple walkthrough to highlight a few noticeable new features:

    • Install Silverlight 3, Blend 3 and Toolkit from http://silverlight.net/getstarted/
    • Open Blend 3, create a new Silverlight 3 Application project
    • Open Asset Library, notice few improvements:
      • the new Asset Library. See Unni’s blog The Blend 3 Asset Library for more info. 
      • the nicer icons and more informative infotip

    Blend 3 Asset Library

    • Add an Expander control, and set it as the new active container:
    Pin Active Container
    • Add a DatePicker to the Expander, notice that both the DatePicker and its Calendar drop down are shown, even though both Expander’s IsExpanded and DatePicker’s IsDropDownOpen are false.

    image

    • Select LayoutRoot so Expander and the nest DatePicker are no longer selected, notice that both are now collapsed.

    image

    Visual Studio ToolboxOpen the project in Visual Studio,notice that the Toolbox is populated with lots of controls from Silverlight SDK and Toolkit, with nice icons.

     

    So far, design time features for Silverlight are mostly exposed through Blend. This will change with next beta of Visual Studio 2010, which will come out soon, according to this Visual Studio Magazine article. Stay tuned!

    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.