Continuing to our 2nd chapter of the series here on Silverlight Custom Control. In this chapter we will discuss on dynamically setting the Content and other properties to our Custom Control that we implemented earlier. After reading this article, you will be able to create properties and set content dynamically to your control.
So, lets start describing them and learn something again today. These series of posts are mainly targeted for beginners but anyone can read it to refresh their learning. Don't forget to leave your comment at the end of the post. Any queries, please drop a line. I will try to help you as early as I can.
Background
I hope, you read my previous posts. Those will help you to understand this one very easily. If you didn't read them or want to brush up before starting this chapter, find them here:
- How to create a Custom Control in Silverlight?
- How to design a Custom Control by editing the Part Template?
Once you are familiar with the previous context, lets start with this one. We will use the same example here which will give you more visualization on the sample code.
Working with the Template Part
Template parts are defined in the XAML inside the style. In our example, we have three part called "PART_HeaderPanel", "PART_HeaderText" and "PART_Content". Those are already declared in the style and you can find them here:
![image image](//lh3.ggpht.com/_MOj9mkIt81A/TZywuup8uUI/AAAAAAAAHNw/1wr0f-fBomo/rw/image3.png?imgmax=800)
Now it's time to initialize them in the code. You can escape this step but it is recommended that you should do this before working with those parts. In this article, we don't need them directly but we are doing this steps here itself. We will talk more on this in next chapter.
We will declare and initialize the template part by setting the TemplatePart attribute to the class. You need to do this for all the template part. Again this step is not mandatory but recommended. This also ensures that, your user of this custom control should know what are the template parts that you used in your style. This information you will find in the meta class if you declare it here.
See the below sample code snippet to know how to declare the Template Part:
![image image](//lh6.ggpht.com/_MOj9mkIt81A/TZywvnb_L1I/AAAAAAAAHN0/zeTB3SXroa4/rw/image7.png?imgmax=800)
We are skipping the explanation and more code for the next chapter of the series.
Defining the Dependency Properties
As we discussed in our previous article, we will use dynamic content and brushes for our Custom Control, here is the implementation of the same. In this section, we will create some properties to expose them to the user and set them dynamically on user request.
To do this, we must have to declare some dependency properties. Make sure that, you are creating dependency properties only as they are used for Data Binding and Template Binding. If you use normal property, the code will not work and will throw exception.
To implement the Dependency Property, open your control's class file (MyControl.cs, in our case) and inside the class declare the same as per your requirement. To create a Dependency Property, you can use the code snippet called "propdp". This will create the same and help you to properly enter the definition.
For now, we will create two dependency properties to hold the Header text and Content for the main area. We will name them as "Caption" and "Content" respectively.
#region Dependency Properties
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
"Content", typeof(FrameworkElement), typeof(MyControl), new PropertyMetadata(null));
public static readonly DependencyProperty CaptionProperty = DependencyProperty.Register(
"Caption", typeof(string), typeof(MyControl), new PropertyMetadata(string.Empty));
#endregion
#region Properties
public FrameworkElement Content
{
get { return (FrameworkElement)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public string Caption
{
get { return (string)GetValue(CaptionProperty); }
set { SetValue(CaptionProperty, value); }
}
#endregion
The above code will help you to understand the same. There we created one property called "Caption" which is nothing but a string type. Setting the value here will change the Header text of our control. The other property called "Content" will be responsible to change the main content of our control.
Binding the Properties in the XAML
Once we created the dependency properties as per requirement, it's time to bind them in the XAML. We have to modify our Template/Style present in our Generic.xaml page. Remember that, you can't use normal Binding there, instead you have to use TemplateBinding. have a look into the below screenshot:
![image image](//lh6.ggpht.com/_MOj9mkIt81A/TZywwvYDTII/AAAAAAAAHN4/YhUHB9TJnYI/rw/image11.png?imgmax=800)
You will notice that, we added TemplateBinding to the properties respective to the parts. This will create the bonding between the control properties and dependency properties.
Setting Custom Value to the Properties
If we run the code now, voila!!! Our control doesn't have anything inside it. In our previous chapter we had some text but nothing is here. Can you think why this happened here? Just try to think the reason before scrolling next to read the answer.
![image image](//lh3.ggpht.com/_MOj9mkIt81A/TZywxWMhtKI/AAAAAAAAHN8/qnY673V7ayQ/rw/image14.png?imgmax=800)
If you check our dependency property, you will see that, it has empty strings and null values as the default one that we binded. We didn't mention a new value from the user's side. So, need to add the property values in our main page.
To do this, open the MainPage.xaml where we added our custom control. Try to add the Caption property to the control. You will notice that, the property is present in the dropdown intellisense too. Hence, add a caption to the property.
![image image](//lh5.ggpht.com/_MOj9mkIt81A/TZywyAvc_zI/AAAAAAAAHOA/Q0VZ1f4LfTw/rw/image18.png?imgmax=800)
For example, we will add "My Custom Control" as the header text / caption. You can add other text too. Here is the code snippet for your reference:
![image image](//lh6.ggpht.com/_MOj9mkIt81A/TZywzMiNFlI/AAAAAAAAHOE/X4RO_33-a9M/rw/image25.png?imgmax=800)
Now run it once again and this time you will see that the string which we entered to the caption property, has been added as header title.
![image image](//lh6.ggpht.com/_MOj9mkIt81A/TZywz4NqFSI/AAAAAAAAHOI/12UEDa87RIY/rw/image21.png?imgmax=800)
Similarly, we will add the content control. As our content property type is FrameworkElement, hence we can't add normal string there. We have to add some framework elements. If you want to give a provision to your user to add anything like FrameworkElement and/or strings, use "string" as the type of the Dependency Property there.
In our case, we will add 2 textblocks inside a Stackpanel as the content of our custom control. Have a look into the below screenshot. The code snippet will give you better visibility of what we are doing here.
![image image](//lh5.ggpht.com/_MOj9mkIt81A/TZyw0-DJuHI/AAAAAAAAHOM/ylG2cH9ROrA/rw/image33.png?imgmax=800)
Once done, run your application. This time you will view the content that we added right now. It is so easy, right? Yes...
![image image](//lh3.ggpht.com/_MOj9mkIt81A/TZyw1m-UorI/AAAAAAAAHOQ/yZnAMDOdDuY/rw/image36.png?imgmax=800)
Let us customize it further and create another Dependency Property to take the background color of the header panel; name it as HeaderPanelBackground. It will be a Brush type, so that, you can add any brush to it like Solid color or Gradient color.
Here is the full source code of our Custom control class:
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
namespace CustomControlDemo
{
[TemplatePart(Name = "PART_HeaderPanel", Type = typeof(Border))]
[TemplatePart(Name = "PART_HeaderText", Type = typeof(TextBlock))]
[TemplatePart(Name = "PART_Content", Type = typeof(ContentPresenter))]
public class MyControl : Control
{
#region Dependency Properties
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content",
typeof(FrameworkElement),
typeof(MyControl),
new PropertyMetadata(null));
public static readonly DependencyProperty CaptionProperty =
DependencyProperty.Register("Caption",
typeof(string),
typeof(MyControl),
new PropertyMetadata(string.Empty));
public static readonly DependencyProperty HeaderPanelBackgroundProperty =
DependencyProperty.Register("HeaderPanelBackground",
typeof(Brush),
typeof(MyControl),
new PropertyMetadata(new SolidColorBrush(
Colors.Blue)));
#endregion
#region Public Properties
public Brush HeaderPanelBackground
{
get { return (Brush)GetValue(HeaderPanelBackgroundProperty); }
set { SetValue(HeaderPanelBackgroundProperty, value); }
}
public FrameworkElement Content
{
get { return (FrameworkElement)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public string Caption
{
get { return (string)GetValue(CaptionProperty); }
set { SetValue(CaptionProperty, value); }
}
#endregion
public MyControl()
{
DefaultStyleKey = typeof(MyControl);
}
}
}
It's time to bind the template property to the style. Get the complete source code of the control template here:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControlDemo">
<Style TargetType="local:MyControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MyControl">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition MinHeight="100" Height="*"/>
</Grid.RowDefinitions>
<Border x:Name="PART_HeaderPanel"
Background="{TemplateBinding HeaderPanelBackground}"
Grid.Row="0">
<TextBlock x:Name="PART_HeaderText"
Text="{TemplateBinding Caption}"
Foreground="White"
VerticalAlignment="Center"/>
</Border>
<ContentPresenter x:Name="PART_Content"
Grid.Row="1"
Content="{TemplateBinding Content}"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Similarly, add the HeaderPanelBackground property value to the MainPage where you added the Custom Control. For example, we will use Green color while demonstrating now.
Get the full code from here (just for reference):
<UserControl x:Class="CustomControlDemo.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:CustomControlDemo="clr-namespace:CustomControlDemo" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<CustomControlDemo:MyControl
Width="200"
Height="200"
HeaderPanelBackground="Green"
Background="Yellow"
BorderBrush="Red"
BorderThickness="1"
Caption="My Custom Control">
<CustomControlDemo:MyControl.Content>
<StackPanel Orientation="Vertical">
<TextBlock Text="First line in Content"/>
<TextBlock Text="Second line in Content"/>
</StackPanel>
</CustomControlDemo:MyControl.Content>
</CustomControlDemo:MyControl>
</Grid>
</UserControl>
Run the application now, once again. Oh yeah, you will see that the header now has a Green background. Change it to something else, it will automatically change. You can further customize it to dynamically change it by user input.
![image image](//lh3.ggpht.com/_MOj9mkIt81A/TZyw2pfwhsI/AAAAAAAAHOU/VYOVn6Ti2SY/rw/image%5B3%5D.png?imgmax=800)
Hope this thing now cleared the implementation part and their usages. Try something more and you will get chance to explore it more.
Setting default value to the Template
Sometime you may want to set the default value to the template directly. User can use the same color or can change it from the place where they use it. To do this, you can use the Setter tag to insert default property value to the style. If you start typing the property name, it will automatically come in the intellisense.
![image image](//lh6.ggpht.com/_MOj9mkIt81A/TZyw3Uh0RVI/AAAAAAAAHOY/v5oFkaNMhOo/rw/image%5B7%5D.png?imgmax=800)
Set the values properly for the respective properties. Find the code snippet here:
![image image](//lh4.ggpht.com/_MOj9mkIt81A/TZyw4fbS0sI/AAAAAAAAHOc/1tUlJFPkTfI/rw/image%5B11%5D.png?imgmax=800)
If you run the application now, you will not see any change. To see the default value effect, go to the MainPage and remove the properties that we already added to the control (as shown below).
![image image](//lh6.ggpht.com/_MOj9mkIt81A/TZyw5BHNWII/AAAAAAAAHOg/1VLFMnHbm4Q/rw/image%5B15%5D.png?imgmax=800)
Once you removed the properties, run the solution. You will see those default values as part of your UI now.
![image image](//lh3.ggpht.com/_MOj9mkIt81A/TZyw59EfuqI/AAAAAAAAHOk/xK4HrKq-Ofo/rw/image%5B18%5D.png?imgmax=800)
Is it clear now? Hope, this cleared the smoke from your eye. Once you read the complete post and follow those steps properly, you will be able to create custom controls in Silverlight very easily.
What Next?
Nothing much. This series will help you to understand the implementation of Custom Controls. Though it is demonstrated in Silverlight but will be same for WPF, Windows Phone 7 too. In the next chapter, we will discuss on the Control parts from code behind (i.e. inside the control class). Will publish that part soon. That will be the last chapter of the series and it will cover more on the Custom control part.
Follow my blog for articles and news. Also find me on Twitter @kunal2383. You will get update there too. Also, I have a blog called Silverlight-Zone, where I post latest articles on Silverlight, LightSwitch, XAML, Blend, XNA and related articles by digging the internet. We update the site very frequently. You may follow us in twitter @SilverlightZone.
Thank you so much for reading my articles/tutorials. Appreciate your feedback and/or suggestion. Happy Coding.
CodeProject