Dynamic Styling – Detecting ResourceDictionaries at Site of Origin

Here’s an XBAP that dynamically grabs the “styles” available to it from site of origin.

Note: By “styles,” I mean distinct look & feels defined in different ResourceDictionary files, not simply the <Style> class.  You could also call these “themes.” 

The resulting proof of concept app looks like this:

The app contains a button whose look is determined by the “style” selected in a combobox.  The list of available “styles” (xml file) and the associated ResourceDictionaries (xaml files) are kept up at the site of origin.  Right now, the “styles” available to the app are:

  1. Fish “style” (thanks to Fil Fortes)
  2. Shiny “style” (thanks to Robert Ingebretsen)
  3. KevinButton “style (thanks to Robert Ingebretsen and Kevin Moore)

You can grab the xbap code here.  Or you can run the RC1 XBAP

 

Implementation

The list of available styles are kept in Styles.xaml up at site of origin:

  <Styles>
     <Style
        Name="fish"
        File="pack://siteoforigin:,,,/Styles/Fish.xaml"/>
     <Style
        Name="shiny"
        File="pack://siteoforigin:,,,/Styles/Shiny.xaml"/>
     <Style
        Name="kevinbutton"
        File="pack://siteoforigin:,,,/Styles/KevinButton.xaml"/>
  </Styles>

 

In my Page1.xaml, I define a XmlDataProvider for that xml file, providing an XPath query that returns a collection of Style elements.  Note that I’m using the pack:// syntax to refer to the site of origin xml file.  Ashish Shetty has a great post about the pack:// concept.

  <Page.Resources>
    <ResourceDictionary>
      <XmlDataProvider
        Source="pack://siteoforigin:,,,/Styles.xml"
        x:Key="XmlStyles"
        XPath="/Styles/Style"/>
    </ResourceDictionary>
  </Page.Resources>

 

Next, I bind my ComboBox to that XmlDataProvider.  I can use the DisplayMemberPath to easily set how visualize the items.  In this case, I want to show the value of the Name attribute.

  <ComboBox x:Name="StyleComboBox" 
    SelectionChanged="OnSelectionChanged"
    ItemsSource="{Binding Source={StaticResource XmlStyles}}" 
    DisplayMemberPath="@Name"
    FontFamily="Calibri, Verdana"
    Width="90"
  />

Because the Resources property is not a DependencyProperty, I cannot bind to it.  Instead, I write a little bit of code to swap the Grid’s resource dictionary on selection changed:

  void OnSelectionChanged(object sender, RoutedEventArgs e)
  {
      XmlElement styleElement= (XmlElement) this.StyleComboBox.SelectedItem;
      String styleFile = (String) styleElement.Attributes["File"].Value;
      ResourceDictionary dictionary = new ResourceDictionary();
      dictionary.Source = new Uri(styleFile, UriKind.RelativeOrAbsolute);
      this.Grid.Resources = dictionary;
  } 

 

 The overall app architecture looks something like this:

4 comments

  1. Karen,

    That post made me think of something we want to investigate. We would like to have an XBAP application running as a framework, and have user controls running into them. However, the number of user controls is unknown, and they might even be implemented by our clients. We want to leave the user controls on the server of origin until they are needed by the XBAP “framework”. I think there is a way to load these user controls “on demand” from the server, is that correct? Do you have pointers to get me started?

    Thanks a lot,
    Laurent

  2. Hi Laurent,

    Today, we do not have a good way to load dll’s that are not known at build time. (If you do know about the dll’s at build time, you can use on demand to lazily bring them down at runtime.)

    The only way to do something accomplish something like this is to use the Assembly.LoadFrom(String) method. However, this method today has several disadvantages listed in the MSDN API reference page.

    We do understand the importance of enabling first class support for “plugin”-like scenarios. And we’re definitely taking that feedback in to consideration as we plan V.Next.

  3. Karen,

    Happy to report that I got this to work exactly according to our specifications. In fact, it works even better, because neither the WPF clients nor the web server need to be restarted when a new control is added.

    I cannot publish a prototype yet (the existing prototype would probably harm Siemens’ confidentiality rules) but I want to implement a simpler example of that and put it on my website. I’ll let you know. (For the record, thinking about this gave Andy and I a few headaches in the past, so we are really happy that we got it to work).

    Question 1: Do you plan to implement “Publish” for the Flexible Application Template soon? It would be nice to have that feature.

    Question 2: Is there a way to communicate with you per email? I know you’re very busy, and I promise you to be reasonable. My main job currently at Siemens is to create prototypes using WPF (standalone and XBAP) for all the many things that we are not sure about, and the requirements we don’t know how to implement yet. I have Karsten’s email and he helped in the past, but some questions would be best answered by you (see the question list Andy gave you last week).

    Thanks a lot and greetings,
    Laurent

Leave a Reply

Your email address will not be published.