Posts Tagged ‘XAML’

Thoughts of Improvements to Silverlights XAML-Parser

Monday, September 14th, 2009

Those of you, who have already developed with WPF “and” Silverlight might know, that Silverlight contains a different XAML-Parser than WPF does.

Some days ago I read on the blog of Rob Relyea a great post about the XAML-Compilers, which create the g.cs- and (only for WPF) the .baml-Files. Rob is talking about improvements for future versions of the XAML-Compilers. Find the post here:

http://blogs.windowsclient.net/rob_relyea/archive/2009/08/25/msbuild-pipeline-for-wpf-amp-silverlight.aspx

I asked in a comment, what about the XAML-Parsers, and Rob has started a new discussion about that topic (Thanks a lot for that) and quoted my comment. So read on here and then go over to Rob’s post and write your thoughts about improvements of the Silverlight XAML-Parser to the Comments-section of his post:

http://blogs.windowsclient.net/rob_relyea/archive/2009/09/08/silverlight-xaml-developer-experience-issues.aspx

Below I’ll summarize my top 3 features I miss most in Silverlights XAML-Parser. All of them exist in the WPF-XAML-Parser:

1. Direct Content with ContentPropertyAttribute

Support direct Content for all Elements that have a ContentPropertyAttribute set on it’s class-definition. E.g. the following doesn’t work in Silverlight yet.

<Button>OK</Button>

In Silverlight you have to do it this way:

<Button Content="OK"/>

This also means you can’t make use of the ContentPropertyAttribute, if your Content isn’t placed in its own element. Take a look at the Friend-class below.

[ContentProperty("FirstName")]
public class Friend
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
}

In WPF you could write the following which not works in Silverlight:

<local:Friend>
  Mary
</local:Friend>

In Silverlight you have to write it the following way using the Attribut-Syntax, which also works without using the ContentPropertyAttribute:

<local:Friend FirstName="Mary"/>

In Silverlight you could also write it this way, by placing the content in its own element.

<local:Friend/>
  <sys:String>Mary</sys:String>
</local:Friend>

Especially if you create custom classes you want to use in XAML, the ContentPropertyAttribute could be very useful.

2. Support for the XmlnsDefinitionAttribute

Also the XmlnsDefinitionAttribute-class exists in Silverlight, you cannot map your own CLR-namespaces with it. You get an “Unknown Namespace”-Message, if you try to use it. In WPF the Attribute is used on assembly-level like below:

[assembly: XmlnsDefinition(
  "http://www.thomasclaudiushuber.com/xaml/thoughts/2009",
  "Thomas.Xaml.Thoughts")]
[assembly: XmlnsDefinition(
  "http://www.thomasclaudiushuber.com/xaml/thoughts/2009",
  "Thomas.Xaml.Thoughts.Controls")]
[assembly: XmlnsDefinition(
  "http://www.thomasclaudiushuber.com/xaml/thoughts/2009",
  "Thomas.Xaml.Thoughts.Data")]

In XAML all the three CLR-Namespaces above could be mapped to one XML-Namespace by using the specified string:

xmlns:thomas=
"http://www.thomasclaudiushuber.com/xaml/thoughts/2009"

Especially for building librariers consisting of more than one CLR-Namespace, it would be great to support the XmlnsDefinitionAttribute, so that a library developer can map all his CLR-Namespace to one XML-Namespace that is used in XAML. That makes the usage of the libary in XAML very compact.

3. Support some missing Markup Extensions

There are also some missing Markup-Extensions in Silverlight. The following would be very great:

  • x:Static – allows you to reference Static Members from XAML.
  • DynamicResource – references a resource and get’s the changes to the resource. Even if a resource with the same key is defined dynamically on a lower level of the Element Tree, this MarkupExtensions grabs the new one
  • x:Type – create a Type-Object of a specific class in XAML

Other thoughts…

… about Markup Extensions

1. Custom Markup-Extensions

If you dig deeper into Silverlight, you’ll notice that there is no support for Custom Markup Extensions. Some Extensions are integrated into the XAML-Parser, and some implement the IMarkupExtension interface as Reflector shows.

In WPF every Markup Extension inherits from MarkupExtension. So you can create your own one by inheriting also from this class and overwrite its ProvideValue-Method. This would be a great feature for Silverlight 4, but as the base-class isn’t in Silverlight yet, there would be “some” things to change.

2. Visual Studio 2010 Beta 1 Intellisense for Markup Extensions

Visual Studio 2010 Intellisense supports Markup Extensions. But it creates e.g. a StaticResource the following way:

<TextBox Background="{StaticResource ResourceKey=myBrush}" />

In Silverlight specifying the ResourceKey-Property explicit isn’t supported. The upper syntax one matches WPF. In Silverlight you’ve to use the markup Extension this way (which also works in WPF):

<TextBox Background="{StaticResource myBrush}" />

Personally I prefer the last way without specifying the ResourceKey-Property explicitly. Maybe this could be done by the Visual Studio team instead of extend the XAML-Parser of Silverlight.

… about x:Name-Attribut

The x:Name-Attributes allows you to give Elements in XAML a name, and access them via Codebehind-File. While WPF supports all elements to have an x:Name-Attribut, Silverlight only supports those deriving from DependencObject.

Even if most classes derive from DependencObject, naming other classes in XAML would by great.

… about IDictionary

Seems that the Silverlight-XAML-Parser is hard-coded to the ResourceDictionary. In WPF you can initialize every collection that implements IList or IDictionary. In Silverlight you can initialize every collection that implements IList or is a ResourceDictionary. It’s not a big issue, cause most times a dictionary is created in code.

So these are my thoughts and wishes for a new release of a Silverlight XAML-Parser. Thanks for reading and now place your own suggestions into the comments section on robs blog here

Bind to methods with ObjectDataProvider

Thursday, January 10th, 2008

Two classes inherit from DataSourceProvider. XmlDataProvider and ObjectDataProvider. ObjectDataProvider wraps an object and allows you to bind to the wrapped object in XAML. The ObjectDataProvider has a ConstructorParameters-Property for creating an object by using a constructor with parameters.

The cool thing is that a Data Binding to the wrapped object of the ObjectDataProvider is not all. With ObjectDataProvider you can even bind to a static method or to an instance method of the wrapped object. I’ll show you an example.

Just think about a really simple ComboBox that allows the user to chose some colors. Between the name of the color a rectangle filled with the color should be displayed (In WPF this is quite easy. You haven’t to create eventhandlers like you would do in Windows Forms. There you consumed the DrawItem-event of the ComboBox and used the Graphics-object to do a little bit of GDI+. In WPF GDI+ is gone). WPF’s Color class doesn’t have a name property. So just create a static class ColorHelper with one static method GetColorNames that returns an IEnumerable<string> and uses reflection to iterate over all public static properties of the Colors class that all contain Color-objects.

public static class ColorHelper
{
  public static IEnumerable<string> GetColorNames()
  {
    foreach (PropertyInfo p
      in typeof(Colors).GetProperties(
      BindingFlags.Public | BindingFlags.Static))
    {
      yield return p.Name;
    }
  }
}

The Window in the following XAML contains an ObjectDataProvider that uses the Static GetColorNames-Method of the ColorHelper class. The ItemsSource-Property (Typ: IEnumerable) of the ComboBox is bound to the ObjectDataProvider. The ItemTemplate-Property contains a DataTemplate that specifies the Visual Tree (the "look") of an item. Thanks to TypeConverters, a SolidColorBrush-Object is created for the Fill-Property of each Rectangle specified in the DataTemplate.

<Window    xmlns:local="clr-namespace:SimpleObjectDataProvider">
 <Window.Resources>
  <ObjectDataProvider x:Key="colors"
   ObjectType="{x:Type local:ColorHelper}"
   MethodName="GetColorNames"/>
 </Window.Resources>
 <StackPanel>
  <ComboBox
   ItemsSource="{Binding Source={StaticResource colors}}">
   <ComboBox.ItemTemplate>
    <DataTemplate>
     <StackPanel Margin="1" Orientation="Horizontal">
      <Rectangle Fill="{Binding}" Height="10" Width="10"
        Margin="2"/>
      <TextBlock Text="{Binding}" Margin="2 0 0 0"/>
     </StackPanel>
    </DataTemplate>
   </ComboBox.ItemTemplate>
  </ComboBox>
 </StackPanel>
</Window>

The displayed, open ComboBox looks like this:

comboBoxObjectDataProvider

Working with the XmlDataProvider

Monday, January 7th, 2008

The XmlDataProvider class enables Binding to XML inside XAML. This will allow you to bind e.g. to the rss feed of a blog like http://www.thomasclaudiushuber.com/blog/feed/, cause rss is simple XML.

A XmlDataProvider-Element is declared as a logic resource. You simply set the Source-Property to a XML-Document and optionally set the XPath-Property. The default value of the IsAsynchronous-Property of the XmlDataProvider is true, so your client won’t freeze if you specify a url as the source.

The following XAML creates a simple rss-reader. The root is a Window object that contains a XmlDataProvider inside its Resources-Property. The XmlDataProvider loads the feed of this blog and its XPath-Property points to the blogs items. The elements inside the Window are indirectly bound to the XmlDataProvider. They use the DataContext specified on the DockPanel. The special thing of the Bindings on this elements is that they do not directly point to the XmlDataProvider. Instead the Bindings start at the /rss/channel/item like specified in the XPath-Property of the XmlDataProvider. If you want to point directly to the XmlDataProvider-object, you’ve to set the BindsDirectlyToSource-Property of the Binding to true. This is used by the first TextBox. It allows the user to change the Source-Property of the XmlDataProvider to navigate to another feed like http://blog.trivadis.com/blogs/MainFeed.aspx. The Binding for this TextBox also specifies that the UpdateSourceTrigger is PropertyChanged. Without this, the Source would be updated on LostFocus, cause the TextBox.TextProperty has specified this behaviour in its metadata.

<Window x:Class="SimpleRssReader.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/…"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="RSS Reader" Height="600" Width="800">
 <Window.Resources>
  <XmlDataProvider x:Key="blog"
   Source="http://www.thomasclaudiushuber.com/blog/feed/"
   XPath="/rss/channel/item"/>
 </Window.Resources>
 <DockPanel
   DataContext="{Binding Source={StaticResource blog}}">
  <StackPanel DockPanel.Dock="Top"
    TextElement.FontWeight="Bold" Background="LightGray">
   <TextBlock Text="{Binding XPath=./../title}"
     FontSize="20" Margin="10 10 10 0"/>
    <TextBlock Text="{Binding XPath=./../description}"
      FontSize="10" FontWeight="Normal" Margin="10 0"/>
   <TextBox Margin="5" Text="{Binding
     Source={StaticResource blog},
     BindsDirectlyToSource=True,
     Path=Source,
     UpdateSourceTrigger=PropertyChanged}"/>
 </StackPanel>

  <StatusBar DockPanel.Dock="Bottom">
   <StatusBarItem Content="{Binding XPath=title}"/>
   <Separator/>
   <StatusBarItem Content="{Binding XPath=pubDate}"/>
  </StatusBar>

  <Grid>
   <Grid.ColumnDefinitions>
    <ColumnDefinition Width="308"/>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition/>
   </Grid.ColumnDefinitions>

   <GroupBox Header="Blog-Eintr?ge">
    <ListBox IsSynchronizedWithCurrentItem="True"
      ItemsSource="{Binding}" DisplayMemberPath="title"/>
   </GroupBox>

   <GridSplitter Grid.Column="1"
     HorizontalAlignment="Center" Width="10"/>

   <Frame Grid.Column="2" Source="{Binding XPath=link}"/>
  </Grid>

 </DockPanel>
</Window>

To test the rss reader yourself, simply create a new WPF-Application Project and paste the XAML above into the Window1.xaml. You have just to set the value of the x:Class-Attribut on the Window-Element so that it matches your class in the Codebehind-File. The Codebehind-File itself doesn’t need any logic. The reader looks like this:

rssreader_XmlDataSource