Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Style Browser for WPF

0.00/5 (No votes)
25 Apr 2011 1  
Application for previewing XAML styles in WPF.

Introduction

In this article, I will show you a simple application that can help you navigate your XAML styles. You can download the source from the link given above.

Background

I am sure every WPF developer has met with the following problem: a lot of styles...a lot of images, brushes, different textboxes, and textblocks. And sometimes it is hard to find a resource key you need. This problem is often met in large WPF projects with a lot of custom controls. When there are multiple developers - it can be much more hard! But for some cases, there is a rather simple solution. The application described in this article can help you make this process easier. Using StyleBrowser, you can select some XAML and look "inside" it. The application will show you all the styles, images, and brushes that are stored in a specified XAML file.

Also, this application can be used when you want to use styles that you have created before and don't remember what exactly is inside a XAML file. Just open the XAML, find the needed style, and use it!

Using the application

When you open the application, you will see the following screen:

Push "Select XAML" and an Open-dialog will appear on your desktop. Select XAML the file with styles and press OK. If XAML is parsed, then you will see something like this:

In the middle of the application, there is TabControl with a number of tabs. Each tabs is a group of some styles from the XAML. In the screenshot, the tab "Buttons" is open and you can see the buttons from the XAML. "Sample content" is added automatically to each control that has the Content property. Reflection is used for detection. To the right, there is style key and its class name. All styles are sorted in alphabetical order.

Also, there are two useful features that can help you. You can select the background, because some styles are white, some black... some black and white :). Right now, there are only three predefined colors: black, white and gray. I think that there will be something like a color picker in future.

The second feature is searching. Just type any word in the Search textbox and you will see only styles that match the searched text. The color and search are shown on the following image:

The last interesting useful feature is that you can see XAML. Just click on the "XAML" button to the right of the style you want to see. But this feature has one disadvantage: it show you the XAML with the fully qualified class names and also XML namespaces are added. But simple XAML looks nice and can be useful.

Code screenshots

Let's make a short trip through the code and take a look at how it works. In the beginning, an OpenDialog is used to select a file.

To parse XAML, I simply use the following code:

XamlReader reader = new XamlReader();
FileStream stream = new FileStream(dlg.FileName, FileMode.Open);
Uri uri = new Uri((new FileInfo(dlg.FileName)).Directory.FullName + "\\");
object xaml = reader.LoadAsync(stream, new ParserContext() { BaseUri = uri });

We have to specify BaseUri here, so the parser is able to process the relative paths to images or any other files, if they exist in XAML.

This code has a disadvantage. If a tag can't be parsed, the XAML will not be read at all. So here we have restrictions: the XAML should contain only the namespaces defined by default in the .NET version the source was compiled in. My solution uses .NET 4.0.

Also, you should be careful with images paths.

The next step is to fill the resources of the current window with the loaded styles. We need it because there can be some styles that use other styles from that XAML. If we don't copy the XAML to the resources, then the referenced XAML will not loaded.

this.Resources.Clear();
if (xaml is ResourceDictionary)
{
    foreach (DictionaryEntry item in (xaml as ResourceDictionary))
    {
        this.Resources.Add(item.Key, item.Value);
        entries.Add(item);
    }
}

Right now, this code has another bad thing: sometimes a control in the main window that has its style in the resources will change its look, even if this control is not in the preview area. I am going to fix this in future.

So we have a collection on styles, brushes, and images, We can do everything with it. Sort, group, etc. In my app, styles are sorted in alphabetical order.

The next task is go through each item in a collection and create a preview in the related tab control.

foreach (var item in list.OrderBy(e => e.Key.ToString()))
{
    StyleItem styleItem = null;
    DictionaryEntry entry = (DictionaryEntry)item;
    if (entry.Value is Style)
    {
        Style style = entry.Value as Style;
        Type type = style.TargetType;
        object obj = Activator.CreateInstance(type);
        
        if (obj is AnimationTimeline) continue;

        styleItem = new StyleItem(obj as FrameworkElement, 
                                  style, type, entry.Key.ToString());

        if (type == typeof(Button) || type == typeof(ToggleButton))
        {
            buttonsPanel.Children.Add(styleItem);
        }

StyleItem is a custom control that represents style preview. Each DictionaryEntry has a Key and Value (like a Dictionary :)). Key is a x:Key attribute from XAML. Value is the content of the XML element.

We have to check what type the value is, because for example, you can't apply a Style property if the current item is an image. Also, we need to skip any animation because it is impossible to create a universal preview for animations. If the item is a brush or image, I just assign the background of the preview as the image or brush.

If the item is a Style, there are three important lines, which extract the style, object type, and create an instance of the extracted type.

Style style = entry.Value as Style;
Type type = style.TargetType;
object obj = Activator.CreateInstance(type);

Then StyleItem uses this info to create the related preview item in the TabControl.

Another interesting thing is how the preview XAML text is created.

string xaml;
try
{
    xaml = XamlWriter.Save(entry.Value).Replace(">", ">\n").NormalizeXaml();
}
catch { xaml = "XAML cannot be parsed"; }

Why is the Replace method used here? Because Save() returns a string without line breaks. So I decided to insert a line break after each ">".

Also, there is an extension method for String: NormalizeXaml().

This method inserts spaces at the beginning of each line depending on its depth in XML. I am not sure if I should show this method implementation in this article.

Restrictions and future

As for now, there are a number of issues in this application. But if you will find it useful, please tell me about it. If developers will like what this app is doing, I will work on it and add new useful features and fix the current issues.

In the future, I am going to:

  • Skip styles with errors.
  • Add DLLs with resources.
  • Feature to add different XAML. For example, if one XAML has references to styles defined in another.
  • Add color picker to choose custom background color.
  • Clean XAML source viewer of namespaces and fully qualified paths.
  • Create a smarter object creator. For example: use a predefined content template for menus, tabcontrols, and other controls that have complicated content.
  • Make preview areas resizable. For example, a small text needs a small rectangle, but a slider or image sometimes needs a huge area.
  • Maybe you will tell me about any interesting features that could be included.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here