Click here to Skip to main content
15,867,288 members
Articles / Desktop Programming / WPF

Vector Brush Library

Rate me:
Please Sign up or sign in to vote.
5.00/5 (20 votes)
25 Apr 2014CPOL6 min read 44.6K   2.1K   56   5
How to conveniently produce and use a vector Icon Library, with sample Icons
Image 1

Introduction

This article is of threefold purpose. It explains how to use resources housed in separate assemblies with pleasant syntax. A tool is also provided to help generate the plumbing necessary to use the nice syntax. Finally there are a few vector versions of some familiar icons that should be useful.

This article started when I wanted to place the icon for a public type on a button as part of an application that selected types. I was surprised and dismayed at being unable to find one but rather than put “type” on my button, I created one in Illustrator. Being pleased with the result, more icons followed and they ended up in their own library. At this point, my pleasure started to subside. The syntax using the ComponentResourceKey bothered me much more than I expected. I began to wonder if the reason I was unable to get results from my vector icon search was this difficulty (Bitmaps are plentiful). This article was written to promote vector icons, so I included the application I used to create the XAML. Understanding the road to the pleasant syntax is a good thing, but nice icons are important enough that understanding binding should be optional. Also graphic artists, who create a lot of the icons out there, often care very little for the subtleties of programming syntax.

References to Resources in Other Assemblies

Referencing resources is separate assemblies is significantly harder than those in the assembly using the resource, but with a little preparation it does not need to be. A reference of...

C#
{StaticResource MethodIconBrush}

... can easily become something on the order of...

C#
{DynamicResource {ComponentResourceKey TypeInTargetAssembly=
    {x:Type icon:LibraryResources}, ResourceId=MethodIconBrush}}

... which can be a bit on the disheartening side. Fortunately for DrawingBrush objects, we can walk all the way back to the simpler syntax.

ComponentResourceKey Method

In order to access resource in separate assemblies, Microsoft provides the ComponentResourceKey markup extension. In order to use it, there are some preconditions.

  • There must be at least one public type in the library assembly. (It cannot be entirely composed of resource dictionaries.)
  • The Resource must be defined either directly of indirectly in Generic.xaml which is located in the themes folder.
  • The Key for the resource should be in a special form.

If those preconditions are met, then the following syntax will work.

XML
<Rectangle Margin="31,12,0,0"  Stroke="Black" Height="62" VerticalAlignment="Top"
                   Fill="{DynamicResource {ComponentResourceKey TypeInTargetAssembly=
           {x:Type icon:LibraryResources}, ResourceId=EventIconBrush}}"
                  HorizontalAlignment="Left" Width="81" />

This fills the Rectangle with the EventIconBrush so one can see the lightning bolt. The EventIconBrush is a DrawingBrush that is located in the ReflectionIconLib assembly. It is referenced in the window via icon.

XML
xmlns:icon ="clr-namespace:ReflectionIconLib;assembly=ReflectionIconLib"

ReflectionIconLib is for this purpose simply a class in the assembly. Finally the EventIconBrush has the following key where it is defined.

XML
x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:LibraryResources}, 
    ResourceId=EventIconBrush}"

Static ComponentResourceKey Property Method

One notices that a large portion of the binding expression is taken up with getting the ComponentResourceKey. Since we are required to have a class in the resource library, we could use it to generate the ComponentResourceKey.

C#
public static ComponentResourceKey XmlIconBrushKey
       {
           get
           {
               ComponentResourceKey result =
       new ComponentResourceKey(typeof(LibraryResources), "XmlIconBrush");
               return result;
           }
       }

This generates the ComponentResourceKey for the XmlIconbrush. It also allows one to simplify the binding expression to:

C#
{StaticResource {x:Static icon:LibraryResources.DelegateIconBrushKey}}

Static Resource Property Method

Now if one can have a static method generate the ComponentResourceKey, why shouldn't one just generate the entire resource as a static property.

C#
public static DrawingBrush XmlIconBrush
       {
           get
           {
               ResourceDictionary rd = new ResourceDictionary();
               rd.Source = new  Uri
       ("ReflectionIconLib;component/Themes/XmlIconBrush.xaml",
       UriKind.Relative);

               DrawingBrush dt = (DrawingBrush)rd[LibraryResources.XmlIconBrushKey];
               return dt;
           }
       }

This gets us the XmlIconBrush and allows us to use the following still shorter binding expression syntax.

C#
{x:Static icon:LibraryResourcesDelegateIconBrush}

This is probably the best that one can do that will work for arbitrary resource types. There is probably some overhead from creating the ResourceDictionary but it could easily be cached if desired.

Static Resource Method

We can take advantage of the fact that the Drawing property of a DrawingBrush is a read-write DependencyProperty. This means that the Drawing property can be set by a binding expression, so one can do the following!

XML
<DrawingBrush x:Key="A"/>
            
<DrawingBrush x:Key="B" Drawing="{Binding Source={StaticResource  A}, Path=Drawing}"/>

Normally this is not very exciting, however in the case of these extra-assembly brushes, it allows the following:

XML
<DrawingBrush x:Key="PropertyIconBrush" 
 Drawing="{Binding Source={x:Static icon:LibraryResources.PropertyIconBrush},  
    Path=Drawing}" Stretch="Uniform"/>

Assuming that the proper setup has been performed, Drawing brushes outside the assembly can be used with the same syntax as those within. The next section will explain how easily and efficiently we can generate the necessary plumbing.

Generating An Icon Library - Step by Step

Illustrator

First create your icons in Illustrator. The ai file is among this article's downloads so it can be used as a template. As this file is going to be imported into Blend, there are two ways one can work. Either one can put each icon on its own layer or have one icon per file. Personally I prefer the more organized method although it may be slightly more time consuming.

Blend

Blend is capable of Importing illustrator files and then converting them to DrawingBrush resources.

Image 2

It is important that one makes DrawingBrush rather than VisualBrush resources. One gets the poetic "freezable cannot be frozen" error if one tries to use a VisualBrush resource across assemblies.

Icon Library Helper

Image 3

The Icon Library Helper helps generate the code for the four files that need to be created/updated for each resource that is added. These are the:

  • Resource dictionary to hold the new resource definition
  • Generic.Xaml to reference this new resource dictionary
  • LibraryResources.cs to contain the static properties used to access the resource
  • The satellite resource dictionary that allows us to use the static resource syntax

The helper will generate either the content for entire files or if "Additions Only" is checked, it will only generate the new information. As a special bonus if "Turbo Copy" is checked, then selecting one of the treeview nodes will not only generate the code but place it in the clipboard as well. If you are creating a new icon library, it is best to start with a WPF custom control library as that will create the appropriate necessary structure.

New Resource Dictionary

The Helper will generate:

XML
  <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:local="clr-namespace:ReflectionIconLib"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <DrawingBrush x:Key="{ComponentResourceKey TypeInTargetAssembly=
    {x:Type local:LibraryResources}, ResourceId=NewResource}" >
    <drawingbrush>
    One needs only to create a new resource dictionary in the themes folder. 
    Replace it's contents with the generated contents of (in this case)
    NewItem.xaml from the helper. Then copy the DrawingBrush.Drawing from Blend 
    into the DrawingBrush. You may also want to 
    copy in the  Viewbox, ViewboxUnits  and Stretch attributes.
</resourcedictionary>

Generic.xaml

In order to be accessed from other assemblies, the resource needs to be merged into the Generic.xaml resource dictionary. The helper will generate the appropriate XAML which for a Library assembly named ReflectionIconLib and resource NewResource would be as follows:

XML
<resourcedictionary source="/ReflectionIconLib;component/Themes/NewResource.xaml" />

It is worth noting that the /ReflectionIconLib;component portion is not optional.

LibraryResources.cs

After that, the LibraryResources.cs file needs to be edited. Unlike Generic.xaml the use of LibraryResources is an arbitrary convention. For each Resource, two static properties are needed that return the ComponentResourceKey and the resource itself. The helper will generate the appropriate C# code which for a Library assembly named ReflectionIconLib and resource NewResource would be as follows:

C#
public static ComponentResourceKey NewResourceKey
    {
        get
        {
            ComponentResourceKey result =
       new ComponentResourceKey(typeof(LibraryResources), "NewResource");
            return result;
        }
    }

    public static DrawingBrush NewResource
    {
        get
        {
            ResourceDictionary rd = new ResourceDictionary();
            rd.Source = new
              Uri("ReflectionIconLib;component/Themes/NewResource.xaml",
                  UriKind.Relative);
            DrawingBrush dt = (DrawingBrush)rd[LibraryResources.NewResourceKey ];
            return dt;
        }
    }

At this point, we have completed all of the alterations needed in the library assembly.

Satellite Resource Dictionary

All that remains is the satellite resource dictionary. The helper will generate something like this:

XML
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:icon ="clr-namespace:ReflectionIconLib;assembly=ReflectionIconLib"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<DrawingBrush x:Key="NewResource" 
    Drawing="{Binding Source={x:Static icon:LibraryResources.NewResource},  
    Path=Drawing}" Stretch="Uniform"/>

</ResourceDictionary>

The convention used is to give it the name of the Library assembly, but that is arbitrary. It is also noteworthy that there is no reference in the file to the assembly in which it is being used. In other words, the same resource dictionary can be used in any assembly.

Testing

The Icon Library Helper can also test assemblies that are put together via the conventions in this article.

Image 4

Conclusion

In conclusion, using references across assemblies is significantly more trouble than it probably ought to be. Fortunately with some tools and organization, it can be made no harder for the end developer than using in assembly resources. You are most welcome and encouraged to use the ReflectionIconLib. There is also a clickonce publish for IconLibraryHelper.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Written software for what seems like forever. I'm currenly infatuated with WPF. Hopefully my affections are returned.

Comments and Discussions

 
GeneralMy vote of 5 Pin
netizenk31-Mar-16 4:15
professionalnetizenk31-Mar-16 4:15 
QuestionMy Vote of 5 Pin
Ian Good2-May-14 12:52
Ian Good2-May-14 12:52 
GeneralMy vote of 5 Pin
Volynsky Alex25-Apr-14 23:23
professionalVolynsky Alex25-Apr-14 23:23 
GeneralRe: My vote of 5 Pin
KenJohnson26-Apr-14 8:46
KenJohnson26-Apr-14 8:46 
GeneralRe: My vote of 5 Pin
Volynsky Alex26-Apr-14 11:18
professionalVolynsky Alex26-Apr-14 11:18 
Thumbs Up | :thumbsup: Thumbs Up | :thumbsup: Thumbs Up | :thumbsup:
It's very interesting article Ken!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.