How to Add an Icon to a Button Control in Silverlight from a Resource

It’s been a long time since I posted something on my blog. This past month I’ve been working on a LOB Silverlight 4 application with one of my co-workers. I’ve been learning quite a lot and I’m excited about Silverlight. I think it’s a great technology. My only disappointment with it has been the lack of good up-to-date articles. This, I think, has been the most frustrating part about learning Silverlight. Having said that, I learned the basics of Silverlight several months ago while on the bench at my current employer.

I didn’t think I would ever have the chance to use Silverlight in the real world. I’m glad I’ve had the opportunity to grow in my knowledge of Silverlight and I’m going to share a nice trick that I learned while working on my project.

Probably a common question when dealing with XAML is “How do I add an icon to a button control?” I found two very nice articles that pointed me to the solution. I was happy with the approach but my need required the ability to reuse those solutions throughout my application. Here’s an example of what I was looking for:

Silverlight Button Controls with Icons

The XAML for producing the above buttons is shown below. This assumes that the icons are in the folder /Assets/Icons/Silk and that the current page is in a folder called /Views, where the beginning slash “/” is the root folder of the Silverlight project. (Note that the term “page” in this context refers to a Silverlight Page, which is different from an HTML or ASP.NET page).

<Button x:Name="AddUserButton"
		Width="150"
		Margin="0,0,0,10">
	<Button.Content>
		<StackPanel Orientation="Horizontal">
			<Image Source="../Assets/Icons/Silk/user_add.png"
					Width="16"
					Height="16" />
			<TextBlock Text="Add User" Margin="10,0,0,0" />
		</StackPanel>
	</Button.Content>
</Button>

<Button x:Name="EditUserButton"
		Width="150"
		Margin="0,0,0,10">
	<Button.Content>
		<StackPanel Orientation="Horizontal">
			<Image Source="../Assets/Icons/Silk/user_edit.png"
					Width="16"
					Height="16" />
			<TextBlock Text="Edit User" Margin="10,0,0,0" />
		</StackPanel>
	</Button.Content>
</Button>

<Button x:Name="DeleteUserButton"
		Width="150"
		Margin="0,0,0,10">
	<Button.Content>
		<StackPanel Orientation="Horizontal">
			<Image Source="../Assets/Icons/Silk/user_delete.png"
					Width="16"
					Height="16" />
			<TextBlock Text="Delete User" Margin="10,0,0,0" />
		</StackPanel>
	</Button.Content>
</Button>

So far so good. Let me discuss the code, but it should be straight forward to follow.

First of all, the Button control is a Content Control. A content control inherits from the System.Windows.Controls.ContentControl class. In the case of the Button control, it inherits directly from the ButtonBase class, which is a subclass of ContentControl. Content controls expose a property called Content, which accepts a value of any type, including other controls. This allows us to do some very powerful transformations, as shown in the external links at the bottom of this article.

The next item that may seem a bit foreign for those new to XAML is the x:Name property. By the context it should be easy to deduce that this is the name of the control. x:Name is the equivalent of the ID attribute for those coming from an HTML background.

The <Button.Content>…</Button.Content> tag is a XAML feature called attached property. Attached properties, in my own words, are extensions of a control’s property where assigning a complex value to the mere PropertyName=”value” syntax isn’t possible. They let us extend a control’s property by adding additional code to the control and property being referred to. They are called attached because it lets us associate that property with a parent control. The parent control in this case is Button and the property being referenced is Content. For more information on attached properties, please visit http://msdn.microsoft.com/en-us/library/ms749011.aspx.

In the code above, we’re declaring an Image control and TextBlock (for displaying plain text) in each of the button controls. We group these two controls with a StackPanel control, which is one of the layout controls available in XAML. At the time of writing, XAML in Silverlight supports three layout controls out of the box: StackPanel, Grid, and Canvas. For the StackPanel control above, we set its orientation to Horizontal so that the Image and TextBlock controls display next to each other.

That’s it! That’s all it takes to display an icon in a Button control using XAML. However, this is not the solution I’m looking for. I want to be able to reuse those button styles throughout my application. For example, what if instead of using a specific icon, such as “user_add.png”, we had more generic icons such as “add.png”, “edit.png”, and “delete.png”? Using a generic icon lets us show it in different places throughout an application. For example, we could have buttons for adding, deleting or editing a user, or buttons for adding, deleting or editing a product. You get the idea.

XAML lets us create exactly that through the use of resources and the Style element. I know two ways to accomplish that. First let me show how to create a style resource that is local to the current page (or user control for that matter). The sample code below is adding local resources relative to the current Page control (via the <navigation:Page.Resources>…</navigation:Page.Resources> attached property) but this is applicable to Silverlight user controls as well (<UserControl.Resources>…</UserControl.Resources>)

<navigation:Page.Resources>
	<Style x:Key="AddButtonStyle" TargetType="Button">
		<Setter Property="Width" Value="150" />
		<Setter Property="Margin" Value="0,0,0,10" />
		<Setter Property="ContentTemplate">
			<Setter.Value>
				<DataTemplate>
					<StackPanel Orientation="Horizontal">
						<Image Source="../Assets/Icons/Silk/add.png"
								Width="16"
								Height="16" />
						<ContentPresenter Content="{Binding}" Margin="10,0,0,0" />
					</StackPanel>
				</DataTemplate>
			</Setter.Value>
		</Setter>
	</Style>

	<Style x:Key="DeleteButtonStyle" TargetType="Button">
		<Setter Property="Width" Value="150" />
		<Setter Property="Margin" Value="0,0,0,10" />
		<Setter Property="ContentTemplate">
			<Setter.Value>
				<DataTemplate>
					<StackPanel Orientation="Horizontal">
						<Image Source="../Assets/Icons/Silk/delete.png"
								Width="16"
								Height="16" />
						<ContentPresenter Content="{Binding}" Margin="10,0,0,0" />
					</StackPanel>
				</DataTemplate>
			</Setter.Value>
		</Setter>
	</Style>

	<Style x:Key="EditButtonStyle" TargetType="Button">
		<Setter Property="Width" Value="150" />
		<Setter Property="Margin" Value="0,0,0,10" />
		<Setter Property="ContentTemplate">
			<Setter.Value>
				<DataTemplate>
					<StackPanel Orientation="Horizontal">
						<Image Source="../Assets/Icons/Silk/pencil.png"
								Width="16"
								Height="16" />
						<ContentPresenter Content="{Binding}" Margin="10,0,0,0" />
					</StackPanel>
				</DataTemplate>
			</Setter.Value>
		</Setter>
	</Style>
</navigation:Page.Resources>

The code below uses the styles defined above. Note that I’m illustrating reuse by creating an additional button called “DeleteProductButton”.

<Button x:Name="AddUserButton"
		Content="Add User"
		Style="{StaticResource AddButtonStyle}" />

<Button x:Name="EditUserButton"
		Content="Edit User"
		Style="{StaticResource EditButtonStyle}" />

<Button x:Name="DeleteUserButton"
		Content="Delete User"
		Style="{StaticResource DeleteButtonStyle}" />

<Button x:Name="DeleteProductButton"
		Content="Delete Product"
		Style="{StaticResource DeleteButtonStyle}" />

In the samples above the most important points to note are the following:

  • Styles are defined in a Resource. For example, <navigation:Page.Resources>… <Style …>…</Style>…</navigation:Page.Resources>
  • A style must have a unique ID defined by the x:Key attribute.
  • A style that modifies a control must be associated with a specific type through the TargetType attribute of the Style element. In the example above, each Style was associated with a Button control.
  • Properties of a control are defined via the <Setter Property=”ControlPropertyName” Value=”PropertyValue” /> element. Note that we use attached properties for defining the ContentTemplate property value of the Button control.
  • The <ContentPresenter /> element, as its name implies, let’s us define the content to display in the control. In the styles above, we set this control’s Content property to {Binding}. This tells Silverlight that the value of the ContentPresenter should be set to whatever value is set in the Button control’s Content property. For example, <Button Content=”Hello” Style=”{StaticResource AddButtonStyle}” />.
  • To use the style, set the Style attribute of the control to {StaticResource StyleKey}, where StyleKey is the ID (x:Key) of the style.
  • The {…} is XAML’s binding syntax.

The code above produces the following result:

Silverlight Buttons with Reusable Icons

We’re not quite done! I mentioned that I wanted to be able to reuse the styles throughout the entire application. For that to be possible, we need to move our styles to a global stylesheet. Yes, this sounds like CSS now, but I think this is the correct terminology in XAML as well. The code below shows how to accomplish that. These styles are declared in a file called Styles.xaml, located under the folder /Assets of the Silverlight application. For the sake of brevity, I’m showing only one of the styles.

<Style x:Key="AddButtonStyle" TargetType="Button">
	<Setter Property="Width" Value="150" />
	<Setter Property="Margin" Value="0,0,0,10" />
	<Setter Property="ContentTemplate">
		<Setter.Value>
			<DataTemplate>
				<StackPanel Orientation="Horizontal">
					<Image Source="/ButtonsWithIcons;component/Assets/Icons/Silk/add.png"
							Width="16"
							Height="16" />
					<ContentPresenter Content="{Binding}" Margin="10,0,0,0" />
				</StackPanel>
			</DataTemplate>
		</Setter.Value>
	</Setter>
</Style>

Everything above is the same as the local resource example shown earlier. However, there is one important distinction. We must tell Silverlight how to find the icon used in the Button control. We use a special syntax in the Source attribute of the Image control. Unfortunately, I don’t know the exact term of this technique, as I haven’t found an MSDN article that talks about it. I learned about the technique from another sample project I studied a couple of weeks ago. The syntax is as follows:

<Image Source=”/SilverlightXap;component/folder/subfolder/imagefile.ext” />

where:

  • SilverlightXap is the name of the Silverlight .XAP file generated by Visual Studio. It must be preceded with a forward slash (/).
  • /folder/subfolder/imagefile.ext is the absolute path to the image within the Silverlight client project. It must be preceded by the word “component”.
  • The semicolon is required between the XAP file name and “component” is required.

Below is the result of the global style sheet:

Silverlight BUttons with Reusable Icons (Add User, Edit User, Delete User, Refresh)

Credits

My work above wouldn’t have been possible without the following articles:

In case you’re curious where I found those cool icons, you can download them at FamFamFam.com

I hope that you found the contents of this article helpful.

Source Code

Download Source Code. The sample project uses Silverlight 4 and the .NET Framework 4.0. You will need the following to be able to build and run this code:

&lt;Style x:Key=&quot;AddButtonStyle&quot; TargetType=&quot;Button&quot;&gt;
&lt;Setter Property=&quot;Width&quot; Value=&quot;150&quot; /&gt;
&lt;Setter Property=&quot;Margin&quot; Value=&quot;0,0,0,10&quot; /&gt;
&lt;Setter Property=&quot;ContentTemplate&quot;&gt;
&lt;Setter.Value&gt;
&lt;DataTemplate&gt;
&lt;StackPanel Orientation=&quot;Horizontal&quot;&gt;
&lt;Image Source=&quot;/ButtonsWithIcons;component/Assets/Icons/Silk/add.png&quot;
Width=&quot;16&quot;
Height=&quot;16&quot; /&gt;
&lt;ContentPresenter Content=&quot;{Binding}&quot; Margin=&quot;10,0,0,0&quot; /&gt;
&lt;/StackPanel&gt;
&lt;/DataTemplate&gt;
&lt;/Setter.Value&gt;
&lt;/Setter&gt;
&lt;/Style&gt;

4 thoughts on “How to Add an Icon to a Button Control in Silverlight from a Resource

  1. I am really very very happy to find some source where the explanation is excellently good. It took me 2 days, now I found your page and now I am able to do the Image control even dynamically

    Thank you.

    However, it would be good if you also included which file is to contain the specific codes, example: App.xaml should have the style set.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>