Implementing WP7 ToggleImageControl from the ground up: Part2
published on: 1/13/2011 | Views: N/A | Tags: UI CustomControls
by WindowsPhoneGeek
This is Part 2 of the "Implementing WP7 ToggleImageControl from the ground up" series of articles in which give an example of creating a fully functional Silverlight for Windows Phone 7 Custom Control.
This series is a step by step guide that include the following posts:
- Part1: I demonstrated how to implement the ToggleImageControl basic prototype,how to add custom Dependency Properties and Visual States.
- Page2 : This post will be focused on implementing the ToggleItemControl custom behavior, overriding OnApplyTemplate and adding some helper methods. I will finish part2 with a detailed Demo that demonstrate the control usage in different scenarios.
NOTE: ToggleImageControl is written only for demonstration purpose. It aims to guide you through the steps to creating a fully functional custom control.
In our previous post we created a ToggleImageControl which inherits from ContentControl and enables users to perform Check/Uncheck operations.It is actually something between advanced TooggleButon and extended ContentControl with the only difference that the whole logic is implemented from the ground up. Basically ToggleImageControl will consist of an Image part and Content parts. The final goal is to build a Stylable control with custom logic that can be used in a Silverlight for Windows Phone 7 application.
In this article I am going to talk about implementing the ToggleImageControl Custom behavior. We will add some custom events and will add the necessary attributes so that our control will be Editable in ExpressionBlend. So take a look at the previous post for reference if necessary now lets continue with the ToggleImageControl implementation.
Overriding OnApplyTemplate
This method is called just before a UI element displays in an application. According to the official documentation derived classes can use OnApplyTemplate as a notification or entry point for the following scenarios:
-
Build the remainder of a visual tree using custom code.
-
Run code that relies on the visual tree from templates having been applied, such as obtaining references to named elements that came from a template.
-
Introduce services that only make sense to exist after the visual tree from templates is complete.
-
Attach class-defined event handlers to parts of the template. For example, you might want class logic to handle KeyDown events from a TextBox template part so that UI states are updated, and other events that are specific to your control are raised instead.
-
Set states and properties of elements within the template that are dependent on other factors. For instance, property values might only be discoverable by knowing the parent element, or when a specific derived class uses a common template. However, note that a well-designed control should generally handle its visual and behavioral states through VisualStateManager. For details on this concept, see Control Customization.
NOTE: OnApplyTemplate is often a more appropriate point to deal with adjustments to the template-created visual tree than is the Loaded event. In Silverlight, the Loaded event might occur before the template is applied, and therefore, you might not be able to adjust the visual tree that is created through applying a template in a Loaded handler.
In our case we will override OnApplyTemplate so that we can get a reference to the CheckBox element when the template is loaded. We will also subscribe to the CheckBox Click event. Calling ChangeVisualState is another common scenario for OnApplyTemplate: making sure that the visual state is set for the control's starting state, in this case by calling a private method that accounts for all of the control's defined states and calls GoToState to set the appropriate state.
NOTE: Take a look at Part 1 of this article for ChangeVisualState reference.
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (checkBox != null)
{
checkBox.Click -= OnCheckBoxClick;
}
checkBox = this.GetTemplateChild("CheckBox") as CheckBox;
// Attach to the Click event
if (checkBox != null)
{
checkBox.Click += OnCheckBoxClick;
}
this.ChangeVisualState(false);
}
Add some helper Methods
We will add OnToggle() method which will reverse the IsChecked property current value.
protected internal virtual void OnToggle()
{
bool isChecked = this.IsChecked;
if (isChecked == true)
{
this.IsChecked = false;
}
else
{
this.IsChecked = true;
}
}
Defining ToggleImageControl behavior
The next thing to do is to determine the visual behavior of our control. In the Checked state we will add a visual effect over the Contentcontrol and will show the CheckBox element. In UncheckedState state we will see only the Image and ContentControl elements. The following schemas can give you a basic idea of states:
At first we will override OnMouseLeftButtonDown so that when the control is checked the Checked Visual State animation begins:
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
this.OnToggle();
base.OnMouseLeftButtonDown(e);
this.ChangeVisualState(false);
}
After that we will add the necessary logic that will Show/Hide elements when the CheckBox element is Checked/Unchecked.
private void OnCheckBoxClick(object sender, RoutedEventArgs e)
{
if (this.IsChecked)
{
this.IsChecked = false;
this.checkBox.IsChecked = true;
}
}
Add Custom Events
We will add Checked and Uncheced events to our Control. We can implement Checked/Unchecked events using RoutedEventHandlers with RoutedEventArgs or we can implement our own EventArgs. The RoutedEventHandler delegate is used for any routed event that does not report event-specific information in the event data.
public event RoutedEventHandler Unchecked;
internal virtual void OnUnchecked(RoutedEventArgs e)
{
this.ChangeVisualState(false);
RoutedEventHandler handler = this.Unchecked;
if (handler != null)
{
handler(this, e);
}
}
public event RoutedEventHandler Checked;
internal virtual void OnChecked(RoutedEventArgs e)
{
this.ChangeVisualState(false);
RoutedEventHandler handler = this.Checked;
if (handler != null)
{
handler(this, e);
}
}
We will rise these events in the OnIsCheckedPropertyChanged method.
NOTE: The reason for passing an empty instance of the RoutedEventArgs is the fact that its OriginalSource property is read only. So if we want to pass any parameter we will need t create a custom EventArgs as demonstrated in the next section.
private static void OnIsCheckedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ToggleImageControl button = d as ToggleImageControl;
bool newValue = (bool)e.NewValue;
if (newValue == true)
{
button.OnChecked(new RoutedEventArgs());
}
else if (newValue == false)
{
button.OnUnchecked(new RoutedEventArgs());
}
}
When the control is used in any Windows Phone 7 application these events can be used as a notification for Checked/Unchecked state of the control and the source element can be referenced through the sender.
public MainPage()
{
InitializeComponent();
this.toggleImage.Unchecked += new RoutedEventHandler(toggleImage_Unchecked);
}
void toggleImage_Unchecked(object sender, RoutedEventArgs e)
{
//you can reference the source element through the sender
}
Add CustomEventArgs if necessary
As I explained previously if you need to pass any parameter then you have to implement a custom EventArgs class. Just with a test purpose we will create a ToggleEventArgs class.
NOTE: You can pass whatever type of parameter you prefer like string, int, double, object etc.
public class ToggleEventArgs : RoutedEventArgs
{
public ToggleEventArgs(object newsource)
{
this.Source = newsource;
}
public object Source
{
get;
set;
}
}
public event EventHandler<ToggleEventArgs> Checked;
internal virtual void OnChecked(ToggleEventArgs arg)
{
this.ChangeVisualState(false);
if (this.Checked != null)
{
this.Checked(this, arg);
}
}
We need to make some minor changes into the OnIsCheckedPropertyChanged so that we will be able to pass the desired parameter through the ToggleEventArgs.
private static void OnIsCheckedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ToggleImageControl button = d as ToggleImageControl;
bool newValue = (bool)e.NewValue;
ToggleEventArgs args = new ToggleEventArgs(button);
if (newValue == true)
{
button.OnChecked(args);
}
else if (newValue == false)
{
//unchecked logic here
}
}
When the control is used in any Windows Phone 7 application this event can be used as a notification for Checked state of the control and the source element can be referenced either through e.Source or through the sender.
public MainPage()
{
InitializeComponent();
this.toggleImage.Checked += new EventHandler<CustomControlSample.ToggleImageControl.ToggleEventArgs>(toggleImage_Checked);
}
void toggleImage_Checked(object sender, CustomControlSample.ToggleImageControl.ToggleEventArgs e)
{
//you can referense the source element either through e.Source or through the sender
}
Add Expression Blend support
To specify what FrameworkElement objects the control expects, you use the TemplatePartAttribute, which specifies the name and type of the expected elements. To specify the possible states of a control, you use the TemplateVisualStateAttribute, which specifies the state's name and which VisualStateGroup it belongs to. Put the TemplatePartAttribute and TemplateVisualStateAttribute on the class definition of the control.
NOTE: In order to be editable in Expression Blend our control have to define its TemplatedParts and TemplateVisualStates.
[TemplateVisualState(Name = "UnChecked", GroupName = "CommonStates")] [TemplateVisualState(Name = "Checked", GroupName = "CommonStates")] [TemplatePart(Name = "CheckBox", Type = typeof(CheckBox))] public class ToggleImageControl : ContentControl
ToggleImageControl Demo
We will create a sample Windows Phone 7 application project that will be used in order to test the newly created ToggleImageControl. So add reference to the control assembly and add the following code in MainPage.xaml:
<StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<myControl:ToggleImageControl Height="100" Width="300" Content="Content"/>
<TextBlock Text="Second Example" Margin="20"/>
<ItemsControl>
<myControl:ToggleImageControl IconSource="Images/icon1.png" Content="Soccer" x:Name="toggleImage"/>
<myControl:ToggleImageControl IconSource="Images/icon2.png" Content="American Football" />
<myControl:ToggleImageControl IconSource="Images/icon3.png" >
<myControl:ToggleImageControl.Content>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Tennis" Margin="0,0,20,0"/>
<Image Source="Images/icon3.png" Height="50" Width="50"/>
</StackPanel>
</myControl:ToggleImageControl.Content>
</myControl:ToggleImageControl>
<myControl:ToggleImageControl IconSource="Images/icon4.png" Content="Hockey"/>
<myControl:ToggleImageControl IconSource="Images/icon5.png" Content="Basketball ball"/>
</ItemsControl>
</StackPanel>
That was all about implementing a fully functional sample Custom Control in Silverlight for Windows Phone 7 and the end of "Implementing WP7 ToggleImageControl from the ground up" series of two articles(Part1 , Part2). I hope that the articles was helpful.
You can find the full source code here:
You can also follow us on Twitter @winphonegeek
Comments
Our Top Articles & Free books
- Our FREE e-book: "Windows Phone Toolkit In Depth" 2nd edition
- 400+ Windows Phone Development articles in our Article Index
- 21 WP7 Toolkit in Depth articles covering all controls
- 12 WP7 Coding4Fun Toolkit in Depth articles covering all controls
- Performance Tips when creating WP7 apps
- Creating a WP7 Custom Control in 7 Steps
- WP7 working with VisualStates: How to make a ToggleSwitch from CheckBox
- What makes a WP7 App successful
- Creating theme friendly UI in WP7 using OpacityMask
- Implementing Windows Phone 7 DataTemplateSelector and CustomDataTemplateSelector
- All about Splash Screens in WP7 – Creating animated Splash Screen
- Getting Started with Unit Testing in Silverlight for WP7
- WP7 WatermarkedTextBox custom control
Our Top Tips & Samples
- All about WP7 Isolated Storage series
- WP7 Dynamically Generating DataTemplate in code
- 5 tips for a successful WP7 Marketplace submission
- WP7: Navigating to a page in different assembly
- WP7 ContextMenu: answers to popular questions
- WP7 ListBox: answers to popular questions
- WP7 working with Images: Content vs Resource build action
- WP7 Element Binding samples
- WP7 working with XML: reading, filtering and databinding
- Drawing in WP7: #2 Drawing shapes with finger
- WP7 TextBox Light theme problems - the solution
- Changing the WP7 Panorama Background Image dynamically with Animation
