Apr 30, 2009

Fix Windows Live Writer Issue with WordPress

I have always been using Windows Live Writer (WLW) in writing my blogs and cross posting them to both WordPress site www.ningzhang.org and Blogger site blog.ningzhang.org, very convenient. Yesterday I ran into issues opening and posting to my WordPress site using Windows Live Writer: got an error dialog saying

Invalid Server Response - The response to the metaWeblog.newMediaObject method received from the blog server was invalid

I searched the web, tried a few things, and fixed the issue pretty quickly. Share my findings here in hope it may be helpful for others run into similar problems.

Windows Live Writer Log

WLW has pretty good log. Open Help->About Windows Live Writer menu item:

Windows Live Writer Help->About menu item

You will see the About dialog, click the Show log file link:

WLW About Windows

the folder “%localappdata%\Windows Live Writer” will pop up, which contains “Windows Live Writer.log” file. I copied relevant error log below. Pretty good stack trace, very helpful for debugging and diagnosing :-)

WindowsLiveWriter,1.7696,Fail,00031,30-Apr-2009 01:28:58.595,"WindowsLive.Writer.Extensibility.BlogClient.BlogClientInvalidServerResponseException: Invalid Server Response - The response to the metaWeblog.newMediaObject method received from the blog server was invalid:

Invalid response document returned from XmlRpc server
  at WindowsLive.Writer.BlogClient.Clients.XmlRpcBlogClient.CallMethod(String methodName, XmlRpcValue[] parameters)
  at WindowsLive.Writer.BlogClient.Clients.MetaweblogClient.DoBeforePublishUploadWork(IFileUploadContext uploadContext)
  at WindowsLive.Writer.PostEditor.WeblogBlogFileUploader.DoUploadWorkBeforePublish(IFileUploadContext uploadContext)
  at WindowsLive.Writer.PostEditor.BlogPostReferenceFixer.FileUploadWorker.DoUploadWork(String fileReference, BlogFileUploader fileUploader, Boolean isWindowsLiveLightboxCloneEnabled)
  at WindowsLive.Writer.PostEditor.BlogPostReferenceFixer.LocalFileTransformer.Transform(BeginTag tag, String reference)
  at WindowsLive.Writer.CoreServices.HTML.HtmlReferenceFixer.LocalFileReferenceFixupFilter.FixReferences(BeginTag tag, String reference)
  at WindowsLive.Writer.CoreServices.HTML.HtmlReferenceFixer.OnBeginTag(BeginTag tag)
  at WindowsLive.Writer.CoreServices.LightWeightHTMLDocumentIterator.Parse()
  at WindowsLive.Writer.CoreServices.HTML.HtmlReferenceFixer.FixReferences(TextWriter output, ReferenceFixer referenceFixer, ReferenceFixedCallback referenceFixed)
  at WindowsLive.Writer.CoreServices.HTML.HtmlReferenceFixer.FixReferences(String html, ReferenceFixer fixer, ReferenceFixedCallback referenceFixed)
  at WindowsLive.Writer.CoreServices.HTML.HtmlReferenceFixer.FixLocalFileReferences(String html, ReferenceFixer fixer, ReferenceFixedCallback referenceFixed)
  at WindowsLive.Writer.CoreServices.HTML.HtmlReferenceFixer.FixLocalFileReferences(String html, ReferenceFixer fixer)
  at WindowsLive.Writer.PostEditor.UpdateWeblogAsyncOperation.LocalSupportingFileUploader.UploadFilesBeforePublish()
  at WindowsLive.Writer.PostEditor.UpdateWeblogAsyncOperation.DoWork()
  at WindowsLive.Writer.CoreServices.AsyncOperation.InternalStart()","   at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
  at System.Environment.get_StackTrace()
  at WindowsLive.Writer.CoreServices.Diagnostics.LogFileTraceListener.Fail(String message)
  at System.Diagnostics.TraceInternal.Fail(String message)
  at System.Diagnostics.Trace.Fail(String message)
  at WindowsLive.Writer.PostEditor.BlogPostEditingManager.UpdateWeblog(Boolean publish)
  at WindowsLive.Writer.PostEditor.BlogPostEditingManager.PostToWeblog(Boolean publish)
  at WindowsLive.Writer.PostEditor.BlogPostEditingManager.PublishAsDraft()
  at WindowsLive.Writer.PostEditor.PostEditorMainControl.commandPostAsDraft_Execute(Object sender, EventArgs e)
  at WindowsLive.Writer.ApplicationFramework.Command.RaiseEvent(Object eventKey, EventArgs e)
  at WindowsLive.Writer.ApplicationFramework.Command.OnExecute(EventArgs e)
  at WindowsLive.Writer.ApplicationFramework.CommandOwnerDrawMenuItem.OnClick(EventArgs e)
  at System.Windows.Forms.MenuItem.MenuItemData.Execute()
  at System.Windows.Forms.Command.Invoke()
  at System.Windows.Forms.Command.DispatchID(Int32 id)
  at System.Windows.Forms.Control.WmCommand(Message& m)
  at System.Windows.Forms.Control.WndProc(Message& m)
  at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
  at System.Windows.Forms.ContainerControl.WndProc(Message& m)
  at System.Windows.Forms.Form.WndProc(Message& m)
  at WindowsLive.Writer.ApplicationFramework.ApplicationForm.WndProc(Message& m)
  at WindowsLive.Writer.ApplicationFramework.SatelliteApplicationForm.WndProc(Message& m)
  at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
  at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
  at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
  at System.Windows.Forms.NativeWindow.DefWndProc(Message& m)
  at System.Windows.Forms.NativeWindow.WndProc(Message& m)
  at WindowsLive.Writer.PostEditor.ImageInsertion.InsertImageDialog.ThumbnailReadinessListener.WndProc(Message& m)
  at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
  at System.Windows.Forms.NativeWindow.DefWndProc(Message& m)
  at System.Windows.Forms.NativeWindow.WndProc(Message& m)
  at WindowsLive.Writer.PostEditor.ImageInsertion.InsertImageDialog.ThumbnailReadinessListener.WndProc(Message& m)
  at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
  at System.Windows.Forms.NativeWindow.DefWndProc(Message& m)
  at System.Windows.Forms.NativeWindow.WndProc(Message& m)
  at WindowsLive.Writer.PostEditor.ImageInsertion.InsertImageDialog.ThumbnailReadinessListener.WndProc(Message& m)
  at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
  at System.Windows.Forms.NativeWindow.DefWndProc(Message& m)
  at System.Windows.Forms.NativeWindow.WndProc(Message& m)
  at WindowsLive.Writer.PostEditor.ImageInsertion.InsertImageDialog.ThumbnailReadinessListener.WndProc(Message& m)
  at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
  at System.Windows.Forms.NativeWindow.DefWndProc(Message& m)
  at System.Windows.Forms.NativeWindow.WndProc(Message& m)
  at WindowsLive.Writer.PostEditor.ImageInsertion.InsertImageDialog.ThumbnailReadinessListener.WndProc(Message& m)
  at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
  at System.Windows.Forms.NativeWindow.DefWndProc(Message& m)
  at System.Windows.Forms.NativeWindow.WndProc(Message& m)
  at WindowsLive.Writer.PostEditor.ImageInsertion.InsertImageDialog.ThumbnailReadinessListener.WndProc(Message& m)
  at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
  at System.Windows.Forms.NativeWindow.DefWndProc(Message& m)
  at System.Windows.Forms.NativeWindow.WndProc(Message& m)
  at WindowsLive.Writer.PostEditor.ImageInsertion.InsertImageDialog.ThumbnailReadinessListener.WndProc(Message& m)
  at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
  at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
  at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
  at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
  at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
  at System.Windows.Forms.Application.Run(Form mainForm)
  at WindowsLive.Writer.ApplicationFramework.SatelliteApplicationForm.Launcher.ThreadMain(Object[] parameters)
  at WindowsLive.Writer.CoreServices.Threading.ThreadStartWithParams.Run()
  at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
  at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
  at System.Threading.ThreadHelper.ThreadStart()"

It is the Gold Star Rating Plugin!

Since WLW has always worked fine with my WordPress site till now, so I suspect it is some recent changes on my WordPress site, and most likely recent plugin updates, so I tried deactivating plugins one by one. I was lucky! Since I knew what plugins I updated lately, so I first deactivated Gold Start Rating plugin and then use WLW to open and post, and it worked!

Register Silverlight Controls with Visual Studio and Blend

 Introduction

This is part of the series on design time implementation changes in Silverlight Toolkit March 2009 Release. This post illustrates the integration of Silverlight Toolkit with Visual Studio and Blend 3 after running Silverlight 3 Toolkit March 2009 Installer, and explains how it is done, so you can register your own Silverlight controls with Visual Studio and Blend too.

Blend Integration

After installing Silverlight 3 Beta1 Tools, Blend 3 Preview, and Silverlight 3 Toolkit March 2009 (please see Silverlight Toolkit Design Time Features: March 2009 Release Update, Silverlight 3 Development with Visual Studio 2008 and Blend 3 section for more information), create a Silverlight 3 Application from Blend 3, we can see that Silverlight Toolkit controls show up automatically in Asset Library:

Toolkit Controls in Asset Library, Controls Tab

Toolkit Controls in Asset Library, Custom Controls Tab 

User can drag and drop Toolkit controls from Asset Library to design or xaml view, and Blend will automatically add assembly reference, xmlns prefix and xaml code, very convenient:

Drag and Drop Chart control from Asset Library

Visual Studio Integration

Open the project in Visual Studio, we can see that Toolkit controls show up automatically in Visual Studio Toolbox, and user can drag and drop controls from Toolbox to design or xaml view as well:

Toolkit Controls in Visual Studio Toolbox 

Besides Toolbox, Toolkit assemblies also show up in Add Reference Dialog:

Add Reference Dialog

and Choose Toolbox Items Dialog:

image

 Implementation

The integration with Visual Studio and Blend is done via registry. If you open “Silverlight 3 Toolkit March 2009.msi” in Orca, you can see the registration magic:

Silverlight 3 Toolkit March 2009.msi in Orca

Register with Visual Studio

AssemblyFoldersEx

Registering with Visual Studio is mostly done via [HKLM|HKCU]\Software\Microsoft\Microsoft SDKs\Silverlight\v3.0\AssemblyFoldersEx:

Visual Studio Registration

The References in Visual Studio page has very good explanation on AssemblyFoldersEx and reference resolution. It is a bit outdate, and is about .net instead of Silverlight, but it is mostly right for Silverlight too. I duplicated the registration schema section below, and modified it to fit Silverlight. Text in small italic fonts are purely hypothetical, meaning: it doesn’t work for Silverlight today; I have it here simply because it is in originally text, works for .NET, and seems making sense for Silverlight to duplicate or to be consistent with .Net.

Registration Schema

Assembly directories can be added to the registry so that the build process can resolve references to assemblies in those directories. Here’s an example of the registry schema.

[HKLM | HKCU]\SOFTWARE\MICROSOFT\Silverlight\

  v3.0

    AssemblyFoldersEx

      Silverlight Toolkit

        @Default = C:\Program Files\Microsoft SDKs\Silverlight\v3.0\Toolkit\March 2009\Libraries\

        @Description = Silverlight 3 Toolkit March 2009

        20090401

          @Default = C:\Program Files\Microsoft SDKs\Silverlight\v3.0\Toolkit\March 2009\20090401

          @Description = April Fool’s Updates

In the above example, there are several interesting fields.

(1) AssemblyFolderBase – The registry path "\SOFTWARE\MICROSOFT\Silverlight" indicates the target framework platform. Typical values for this property are:

\SOFTWARE\MICROSOFT\Silverlight(default) – for Silverlight

\SOFTWARE\MICROSOFT\.NetCompactFramework– for .NET Compact Framework

(2) FrameworkVersion – The version of the frameworks that this component is meant to work against.

(3) AssemblyFoldersSuffix – Describes the sub-target, examples include:

AssemblyFoldersEx (default) – for Silverlight

PocketPC\AssemblyFoldersEx – for the .NET Compact Framework for Pocket PC

SmartPhone\AssemblyFoldersEx – for the .NET Compact Framework for SmartPhone

WindowsCE\AssemblyFoldersEx – for the .NET Compact Framework for WindowsCE

(4) ControlName – The name of the control. In the example above, “Silverlight 3 Toolkit March 2009”. The control vendor chooses this name.

(5) ServicingCode – For service packs that update the same component. In the above example, “20090401”. The control vendor chooses this name.

Toolbox Controls Installer

Jim Nakashima blogged about Toolbox Controls Installer for WPF controls in his post Have you seen the Toolbox Controls Installer? in 2007. Toolbox Controls Installer package is now part of Visual Studio 2008, and kind of works, with a bug that will be fixed in Visual Studio 2010. It can be tweaked to work for Silverlight controls too: set string registry value SilverlightControls instead of WPFControls to 1.

I am working with various teams inside Microsoft to improve the Visual Studio registration story for Silverlight controls. All feedbacks and suggestions are welcome!

 Register with Blend

Registering with Blend is via HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Expression\Blend\v3.0\Toolbox\Silverlight\v3.0:

Blend Registration

Unni Ravindranathan’s blog post Blend 3 Extensibility has more information about this mechanism. One caveat: if the registered assemblies depend on other assemblies, those other assemblies need to be in the same directory as registered assemblies, or registered with AssemblyFoldersEx mentioned above. 

Conclusion

Registration with Visual Studio and Blend makes Silverlight Toolkit controls a lot easier to use for developers. We are constantly looking at improving the registration mechanism in hope to make it simple and consistent across designers (Visual Studio and Blend) and technologies (WPF and Silverlight). All feedbacks are welcome!

Apr 22, 2009

Add Rich Intellisense for Your Silverlight Controls

Introduction

This is part of the series on design time implementation changes in Silverlight Toolkit March 2009 Release. This post illustrates the rich intellisense support for Silverlight Toolkit, and explains implementation workflow from xmldoc in source to infotip in intellisense xml file and design assemblies.

Intellisense

Silverlight Toolkit has rich Intellisense in code and XAML editors in both Visual Studio and Blend:

  • Visual Studio Code Editor:
    VS2008 Code Editor Intellisense
  • Blend 3 Code Editor:
    Blend 3 Code Editor Intellisense
  • Blend 3 XAML editor:
    Blend 3 Xaml Editor Intellisense

Visual Studio XAML editor has intellisense too, but no infotip: Visual Studio Xaml Editor Intellisense

Implementation

Code Editor Intellisense Infotip from XML Files

The intellisense infotips in code editors in Visual Studio and Blend come from intellisense xml files installed under Silverlight SDK and Toolkit directories:

Intellisense xml files installed

Localization

Those intellisense xml files can be localized. If you install localized Visual Studio and localized Silverlight SDK, you will see localized infotips, which is a great help for those who don’t speak English. Below is the screenshot of Visual Studio running with Simplified Chinese language and simplified Chinese version of Silverlight Tools for Visual Studio SP1:

Chinese VS and Infotip

The Chinese infotip is from localized System.Windows.Controls.xml under zh-CHS sub directory:

C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Libraries\Client>dir/s /b system.windows.controls.xml
C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Libraries\Client\system.windows.controls.xml
C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Libraries\Client\zh-CHS\system.windows.controls.xml

 

XAML Editor Intellisense Infotip from Design Assemblies

The intellisense infotips in Blend 3 XAML editor come from a different mechanism: they come from DescriptionAttribute implemented by design dlls installed under the Design subdirectory:

SDK Design folder

So it is the same as infotips in Blend Properties panel (please see Design Time Features in Silverlight Toolkit for more information):

Blend Property Inspector Infotip

xmldoc comment –> intellisense xml file –> DescriptionAttribute in design assembly

All those infotips come from xml documentation comments in source code, as seen from below screenshot:

Workflow of xmldoc to infotip

  1. we put xmldoc comments in source code
  2. build generates .xml files from those xmldoc comments
  3. the generated .xml files are embedded in design dlls (see Design Time Feature Implementation in Silverlight Toolkit for more information)
  4. the control dlls, design dlls, and xml files are installed by SDK and Toolkit setup

Conclusion

This post describes the rich intellisense support of Silverlight Toolkit and its implementation workflow from xmldoc comments to intellisense xml files to design dlls. Good commenting pays!

Apr 3, 2009

Property Value Editors for Silverlight Controls

Introduction

This is part of the series on design time implementation changes in Silverlight Toolkit March 2009 Release. This post discusses how to enhance property editing experience for Silverlight controls using property value editor and type converter. I will start with describing the overall property editing architecture in WPF/Silverlight designer extensibility framework, then use examples in  Silverlight Toolkit March 2009 Release to demonstrate how it is done, and unique issues/tricks in Silverlight design time development.

Property Editing Architecture

Visually editing object properties is an important part of designers. Designers usually don’t know how to render properties of custom type (struct, class or interface), much less to provide an nice editing user interface. Control developers usually need to provide TypeConverter, PropertyValueEditor, or both, to provide rendering/editing UI and XAML serialization for properties of custom types.

The designer extensibility framework defines three types of property value editor: inline editor, extended editor, and dialog editor, each implemented by a class:

PropertyValueEditor class diagram

Editing UI of those editors are defined by DataTemplate. The property being edited is exposed to editor as DataContext of PropertyValue type. Editor UI usually bind to the underlying property being edited via one of the three properties of PropertyValue: Value, StringValue, or Collection.

PropertyValueEditor

PropertyValueEditor holds a single inline editor defined by InlineEditorTemplate property. Inline editor appears inside the properties window. Below is an simple example of InlineEditorTemplate that uses a TextBox to display and edit a property:

<DataTemplate x:Key="TextBoxEditor">
   <TextBox Text="{Binding Path=Value}"/>
</DataTemplate>
 

ExtendedPropertyValueEditor

ExtendedPropertyValueEditor has two editors: an inline editor inherited from PropertyValueEditor, and an additional extended editor defined by the ExtendedEditorTemplate property. The extended editor is usually popped up by inline editor via PropertyValueEditorCommands.ShowExtendedPinnedEditor or PropertyValueEditorCommands.ShowExtendedPopupEditor command. Below is a simple example: the inline editor is a button; when clicked, it pops up the extended editor that uses a Slider to display and edit the underlying property.
<DataTemplate x:Key="inlineEditor">
   <Button Content="..." Command="{x:Static PropertyEditing:PropertyValueEditorCommands.ShowDialogEditor}"/>
</DataTemplate>
<DataTemplate x:Key="extendedEditor" xmlns:PropertyEditing="clr-namespace:Microsoft.Windows.Design.PropertyEditing;assembly=Microsoft.Windows.Design.Interaction">
   <Slider x:Name="slider" Value="{Binding Path=Value}" />
</DataTemplate>
 

DialogPropertyValueEditor

DialogPropertyValueEditor has two editors too: the inline editor inherited from PropertyValueEditor, and an additional dialog editor defined by DialogEditorTemplate property. The dialog editor is usually popped up by inline editor via PropertyValueEditorCommands.ShowDialogEditor command. Below is a simple example:

<DataTemplate x:Key="inlineEditor">
   <Button Content="..." Command="{x:Static PropertyEditing:PropertyValueEditorCommands.ShowDialogEditor}"/>
</DataTemplate>
<DataTemplate x:Key="dialogEditor" xmlns:PropertyEditing="clr-namespace:Microsoft.Windows.Design.PropertyEditing;assembly=Microsoft.Windows.Design">
   <Grid>
       <Grid.ColumnDefinitions>
           <ColumnDefinition Width="Auto" />
           <ColumnDefinition Width="*" />
       </Grid.ColumnDefinitions>
       <Grid.RowDefinitions>
           <RowDefinition Height="*"/>
           <RowDefinition Height="*"/>
       </Grid.RowDefinitions>
       <TextBlock Text="User Name:" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,4,4"/>
       <TextBox Text="{Binding Path=Value}" VerticalAlignment="Center" HorizontalAlignment="Stretch" Margin="0,0,4,4" Grid.Column="1"/>
   </Grid>
</DataTemplate>

Implement Custom Property Editor

To implement a custom property editor for a Silverlight control:

  • implement a custom property editor class
  • Associate the custom property editor with a property of a Silverlight control via a AddCustomAttributes call, something like
    attributeTableBuilder.AddCustomAttributes(
       typeof(MyControl),
       "MyProperty",
       new PropertyValueEditor.CreateEditorAttribute(typeof(MyValueEditor)));
    A correctly implemented property value editor must satisfy the following requirements:
  • The property value editor must be designed so that the inline editor and extended editor parts can be used independently.
  • A property value editor must not store state. Property value editors are stateless, might be cached by a host implementation, and can be re-used across multiple property values.
  • A property value editor must not assume that only one value editor part (view/inline/extended) control is active at a given time. For example, a dialog box could have the view part, inline part, and extended UI part active at the same time.
  • A control implemented as part of a property value editor must not store state. A control implemented as part of a value editor should not assume that it will only be bound to one property value. Controls may be recycled to change different property values. Any information that is cached should be flushed if the data model is updated.
  • A control implemented as part of a property value editor must not make any assumptions about the host or its parent controls. The only communication mechanisms that should be used are the PropertyValue data model, by way of the DataContext, and the standard set of commands.
     

Reference Both WPF and Silverlight Assemblies

A design assembly is a .NET/WPF assembly loaded by Visual Studio or Blend, but it usually needs to reference Silverlight assemblies (at least the Silverlight control assembly it provides design time features for). This can create reference ambiguity for design assembly project: it sometimes needs to reference the same fully qualified type in both WPF and Silverlight, say System.Windows.FrameworkElement in both PresentationFramework.dll of WPF and System.Windows.dll of Silverlight. To avoid confusing Visual Studio, you can use extern alias to distinguish WPF and Silverlight references.

For example, see the screenshot below for Controls.DataVisualization.Toolkit.Design project in Silverlight 3 Toolkit in March 2009 Release:

image

  • the project references both PresentationFramework and System.Windows, but System.Windows is under Silverlight alias, instead of the default global alias. The System.Windows reference under Silverlight alias is persisted in .csproj file as below:
    <Reference Include="System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, processorArchitecture=MSIL">
     <SpecificVersion>False</SpecificVersion>
     <HintPath>..\Binaries\System.Windows.dll</HintPath>
     <Private>False</Private>
     <Aliases>Silverlight</Aliases>
    </Reference>
  • in source code, we add
    extern alias Silverlight;
    using SSW = Silverlight::System.Windows;

    and reference Silverlight’s FrameworkElement via SSW::FrameworkElement.

TextBoxEditor

There is a inline editor TextBoxEditor in Controls.DataVisualization.Toolkit.Design project for displaying and editing Title property of object type for  [Area|Bar|Bubble|Column|Line|Pie|Scatter]Series and Chart controls, as shown below:

TextBoxEditor

Text filled in Title field in Properties window on the right shows up automatically in XAML and design views in the middle.

The implementation of TextBoxEditor followed the steps outlined before:

TextBoxEditor.cs

TextBoxEditor.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.

using System;
using Microsoft.Windows.Design.PropertyEditing;
using System.Windows;
using System.Windows.Data;

namespace System.Windows.Controls.DataVisualization.Design
{
   /// <summary>
   /// Simple TextBox inline editor.
   /// </summary>
   public partial class TextBoxEditor : PropertyValueEditor
   {
       /// <summary>
       /// Preserve the constructor prototype from PropertyValueEditor.
       /// </summary>
       /// <param name="inlineEditorTemplate">Inline editor template.</param>
       public TextBoxEditor(DataTemplate inlineEditorTemplate)
           : base(inlineEditorTemplate)
       { }

       /// <summary>
       /// Default constructor builds the default TextBox inline editor template.
       /// </summary>
       public TextBoxEditor()
       {
           FrameworkElementFactory textBox = new FrameworkElementFactory(typeof(TextBox));
           Binding binding = new Binding();
           binding.Path = new PropertyPath("Value");
           binding.Mode = BindingMode.TwoWay;
           textBox.SetBinding(TextBox.TextProperty, binding);

           DataTemplate dt = new DataTemplate();
           dt.VisualTree = textBox;

           InlineEditorTemplate = dt;
       }
   }
}

CultureInfoEditor

My colleague RJ wrote inline editor CultureInfoEditor for TimePicker.Culture property, because the default editing experience for CultureInfo in Blend leads to invalid XAML. Below screenshot shows the CultureInfoEditor uses a ComboBox to display all CultureInfo and generates right XAML.

CultureInfoEditor

CultureInfoEditor is a more complex example than TextBoxEditor, really shows how the underlying property is associated with editor via DataContext property.

CultureInfoEditor.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.

using System.Reflection;
using Microsoft.Windows.Design.PropertyEditing;
using System.Globalization;
using System.ComponentModel;

namespace System.Windows.Controls.Input.Design
{
   /// <summary>
   /// Editor for CultureInfo.
   /// </summary>
   /// <remarks>Currently does not support binding from xaml to the editor.</remarks>
   public class CultureInfoEditor : PropertyValueEditor
   {
       /// <summary>
       /// The ComboBox being used to edit the value.
       /// </summary>
       private ComboBox _owner;

       /// <summary>
       /// Preserve the constructor prototype from PropertyValueEditor.
       /// </summary>
       /// <param name="inlineEditorTemplate">Inline editor template.</param>
       public CultureInfoEditor(DataTemplate inlineEditorTemplate)
           : base(inlineEditorTemplate)
       { }

       /// <summary>
       /// Default constructor builds a ComboBox inline editor template.
       /// </summary>
       public CultureInfoEditor()
       {
           // not using databinding here because Silverlight does not support
           // the WPF CultureConverter that is used by Blend.
           FrameworkElementFactory comboBox = new FrameworkElementFactory(typeof(ComboBox));
           comboBox.AddHandler(
               ComboBox.LoadedEvent,
               new RoutedEventHandler(
                   (sender, e) =>
                   {
                       _owner = (ComboBox) sender;
                       _owner.SelectionChanged += EditorSelectionChanged;
                       INotifyPropertyChanged data = _owner.DataContext as INotifyPropertyChanged;
                       if (data != null)
                       {
                           data.PropertyChanged += DatacontextPropertyChanged;
                       }
                       _owner.DataContextChanged += CultureDatacontextChanged;
                   }));

           comboBox.SetValue(ComboBox.IsEditableProperty, false);
           comboBox.SetValue(ComboBox.DisplayMemberPathProperty, "DisplayName");
           comboBox.SetValue(ComboBox.ItemsSourceProperty, CultureInfo.GetCultures(CultureTypes.SpecificCultures));
           DataTemplate dt = new DataTemplate();
           dt.VisualTree = comboBox;

           InlineEditorTemplate = dt;
       }

       /// <summary>
       /// Handles the SelectionChanged event of the owner control.
       /// </summary>
       /// <param name="sender">The source of the event.</param>
       /// <param name="e">The <see cref="System.Windows.Controls.SelectionChangedEventArgs"/> 
       /// instance containing the event data.</param>
       private void EditorSelectionChanged(object sender, SelectionChangedEventArgs e)
       {
           // serialize with name.
               object DataContext = _owner.DataContext;
               DataContext
                   .GetType()
                   .GetProperty("Value", BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty)
                   .SetValue(DataContext, ((CultureInfo)_owner.SelectedItem).Name, new object[] { });
       }

       /// <summary>
       /// Handles the PropertyChanged event of the context object.
       /// </summary>
       /// <param name="sender">The source of the event.</param>
       /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
       private void DatacontextPropertyChanged(object sender, PropertyChangedEventArgs e)
       {
           // deserialize from name.
           if (e.PropertyName == "Value")
           {
               object value = sender
                   .GetType()
                   .GetProperty("Value", BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty)
                   .GetValue(sender, new object[] { });

               if (value != null)
               {
                   if (value is string)
                   {
                       CultureInfo setCulture = new CultureInfo(value.ToString());
                       _owner.SelectedItem = setCulture;
                   }
               }
           }
       }

       /// <summary>
       /// Called when the context is changed.
       /// </summary>
       /// <param name="sender">The sender.</param>
       /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
       private void CultureDatacontextChanged(object sender, DependencyPropertyChangedEventArgs e)
       {
           INotifyPropertyChanged old = e.OldValue as INotifyPropertyChanged;
           if (old != null)
           {
               old.PropertyChanged -= DatacontextPropertyChanged;
           }
           INotifyPropertyChanged newDataContext = e.NewValue as INotifyPropertyChanged;
           if (newDataContext != null)
           {
               newDataContext.PropertyChanged += DatacontextPropertyChanged;
           }
       }
   }
}

ExpandableObjectConverter

As discussed at the beginning, besides custom property value editors, sometimes you can use appropriate type converter to provide good editing experience and XAML serialization. One example is ColumnSeries.DependentRangeAxis: it is of IRangeAxis type, Blend doesn’t know how to edit it, so it shows DependentRangeAxis as read only in Properties Panel. By associating ExpandableObjectConverter to ColumnSeries.DependentRangeAxis:

b.AddCustomAttributes(
   Extensions.GetMemberName<ColumnSeries>(x => x.DependentRangeAxis),
   new TypeConverterAttribute(typeof(ExpandableObjectConverter)));
b.AddCustomAttributes(
   Extensions.GetMemberName<ColumnSeries>(x => x.IndependentAxis),
   new TypeConverterAttribute(typeof(ExpandableObjectConverter)));

Blend displays a New button next to this property in Properties Panel; when clicked, it pops up the Select Object dialog, filtered with the property’s type IRangeAxis:

ExpandableObjectConverter

Conclusion

Blend and Silverlight together allow designers to create amazing UI against real controls directly, so it is very important that control developers take designer experience as part of overall control design and implementation. Having a custom property editor that provides nice editing UI and generates correct XAML is an important part of the designer experience. Hopefully this post helps you understand how to create custom property editors. Thanks!