Download source files – 10 kB
Recently I’ve started programming on the Tahoe-II development platform using the .Net Micro Framework. This development kit has a touch screen, so I started creating some UI using WPF in NETMF. First thing I noticed was that it didn’t have a Button control, this was however a control that I would be using frequently in my UI, so I decided to write my own Button control. In this blog post I will explain how I implemented the basic features of this control.
Be aware that this control is developed for a specific application, so if you are wondering why I added for example a border in the control (see below) this is the reason. In a next blog post I will talk about a more generic Button Control.
The Features
- Click Event
- IsPressed Property
- Ability to set a Child (Text, Image,…)
- Automatically change Background, Border Brush based on the state of the button (Pressed / Not Pressed)
Implementation
Basics
Let’s start by creating a Class that inherits ContentControl, takes an UIElement (child) in it’s constructor and has a Click Event.
namespace Microsoft.SPOT.Presentation.Controls {
public class Button : ContentControl {
public event EventHandler Click;
public Button(UIElement child) {
}
protected virtual void OnClick() {
if (Click != null) {
Click(this, new EventArgs());
}
}
}
}
Now that we have created our class I will explain how to implement the border and the Child feature. Because I decided to add a border to the button the first problem is that the child in the constructor won’t be our real child (the border is) but instead it will be the child of the border. Knowing this we could rewrite our constructor so it looks like this:
Border border;
public Button(UIElement child) {
border = new Border();
border.Child = child;
Child = border;
}
There still remains a problem with this solution, if we implemented it this way it would give to developer the possibility to change our child from the border to another UIElement (ex: Button.Child = new Text() ).
To avoid that we can override the Child Property of our control.
public new UIElement Child {
get { return border.Child; }
set {
border.Child = value;
}
}
When the developer gets/sets the Child of our Button now, he will actually get/set the child of the border control.
Now lets implement all the brush properties and initialize them in our constructor. While we’re at it lets also create the IsPressed Property and a private variable isFocused (bool).
public class Button : ContentControl {
...
private bool isFocused, isPressed;
private Brush background, borderBrush, pressedBackground, pressedBorderBrush;
public event PropertyChangedEventHandler IsPressedChanged;
public bool IsPressed {
get { return isPressed; }
set {
if (value != isPressed) { //new value
isPressed = value;
//change the brushes
if (isPressed) {
border.Background = PressedBackground;
border.BorderBrush = PressedBorderBrush;
} else {
border.Background = Background;
border.BorderBrush = BorderBrush;
}
OnIsPressedChanged(!isPressed, isPressed);
}
}
}
public new Brush Background {
get { return background; }
set {
background = value;
if (!IsPressed) {
border.Background = background;
}
}
}
public Brush BorderBrush {
get { return borderBrush; }
set {
borderBrush = value;
if (!IsPressed) {
border.BorderBrush = borderBrush;
}
}
}
public Brush PressedBackground {
get { return pressedBackground; }
set {
pressedBackground = value;
if (IsPressed) {
border.Background = value;
}
}
}
public Brush PressedBorderBrush {
get { return pressedBorderBrush; }
set {
pressedBorderBrush = value;
if (IsPressed) {
border.BorderBrush = value;
}
}
}
public Button(UIElement child) {
border = new Border();
//Default settings
Background = new SolidColorBrush(Colors.White);
BorderBrush = new SolidColorBrush(Colors.Black);
PressedBackground = Background;
PressedBorderBrush = new SolidColorBrush(Colors.Gray);
SetBorderThickness(2);
//Set Child
Child = child;
base.Child = border;
}
public void SetBorderThickness(int thickness) {
border.SetBorderThickness(thickness);
}
protected virtual void OnIsPressedChanged(bool oldValue, bool newValue) {
if (IsPressedChanged != null) {
IsPressedChanged(this, new PropertyChangedEventArgs("IsPressed", oldValue,
newValue));
}
}
...
}
As you can see the setter of IsPressed chooses the correct brushes on a value change. I’ve also decided to add an IsPressedChanged event, which could for example be used to change properties of the child (ex: when button pressed => change text color from black to white, show another image,…). In addition the function SetBorderThickness was added so we can easily change the thickness of our border.
Instead of adding the border in the control, we could also add it later on and change it’s properties when IsPressed changed, and thus using the IsPressedChanged event.
void example() {
Text text = new Text("Hello World");
Button button = new Button(text);
Border border = new Border();
border.Child = button;
button.IsPressedChanged += new PropertyChangedEventHandler(button_IsPressedChanged);
}
void button_IsPressedChanged(object sender, PropertyChangedEventArgs e) {
//Set brushes depending on IsPressed value
}
In the next blogpost I will explain which adjustments are needed to create it this way and how to creating a new BorderButton control which basically is the same control as this one.
Making it touchable
All right we’re almost done, all that’s left to do is making the control actually a touch based button. To do this I actually ran into some problems with the TouchUp Event, I tried different solutions and this is to me the best one.
Problem
Let’s say we listen to the TouchDown Event for knowing when we are pressing the Button, now let’s also do this for knowing when we stopped pressing the button (thus using the TouchUpEvent). Problem here: when we move our finger outside the button and release our finger from the touchscreen the button won’t be notified about the TouchUp because it didn’t happen inside him. Same goes for knowing when we moved our finger outside the button or back into the button (TouchMove event).
Solution
In .NETMF we can register one UIElement to receive all the touch events, even when they aren’t inside it’s boundaries , but be careful using this will stop all other elements from receiving touch events. The idea of the solution is that when I put my finger on the button I will register this button to receive all incoming touch events allowing me to know when I’m moving my finger outside the control our when I’m releasing my finger somewhere outside the control. We just need to be careful that we unregister the control to make sure our touch based UI keeps working after pressing a button.
Code
protected override void OnTouchDown(TouchEventArgs e) {
base.OnTouchDown(e);
isFocused = TouchCapture.Capture(this); //Register ourselves to get all TouchEvents
IsPressed = isFocused;
}
protected override void OnTouchMove(TouchEventArgs e) {
base.OnTouchMove(e);
if (isFocused) { //This control receives all TouchEvents
int x, y;
e.GetPosition(this, e.Touches.Length - 1, out x, out y);
//is the current position inside the button?
IsPressed = x >= 0 && x <= ActualWidth && y >= 0 && y <= ActualHeight;
}
}
protected override void OnTouchUp(TouchEventArgs e) {
base.OnTouchUp(e);
if (isFocused) { //This control receives all TouchEvents
int x, y;
IsPressed = false;
isFocused = !TouchCapture.Capture(this, CaptureMode.None); //Unregister ourselves
e.GetPosition(this, 0, out x, out y);
if (x >= 0 && x <= ActualWidth && y >= 0 && y <= ActualHeight) {
//TouchUp was inside the button
OnClick();
}
}
}
TouchCapture.Capture(…) was the function I was talking about to register UIElement, to unregister we use the same function but now we add the CaptureMode.None parameter. In the OnTouchMove and OntouchUp functions we perform a check to see whether or not our finger is inside our control, and depending on that we change IsPressed / Fire Click Events.
Example
public class Program : Application {
public static void Main() {
Program myApplication = new Program();
Window mainWindow = myApplication.CreateWindow();
Touch.Initialize(myApplication);
myApplication.Run(mainWindow);
}
public Window CreateWindow() {
Window mainWindow = new Window();
mainWindow.Height = SystemMetrics.ScreenHeight;
mainWindow.Width = SystemMetrics.ScreenWidth;
Text text = new Text();
text.Font = Resources.GetFont(Resources.FontResources.small);
text.ForeColor = Colors.White;
text.TextContent = "Hello World!";
text.SetMargin(5);
Button textButton = new Button(text);
textButton.Background = new LinearGradientBrush(
ColorUtility.ColorFromRGB(33, 20, 110), ColorUtility.ColorFromRGB(65, 40, 209));
textButton.BorderBrush = new SolidColorBrush(Colors.Black);
textButton.PressedBackground = new SolidColorBrush(Colors.Blue);
textButton.PressedBorderBrush = new SolidColorBrush(Colors.Black);
Image image = new Image(Resources.GetBitmap(Resources.BitmapResources.Next));
image.HorizontalAlignment = HorizontalAlignment.Center;
Button imageButton = new Button(image);
imageButton.SetMargin(0, 10, 0, 0);
textButton.Click +=new EventHandler(textButton_Click);
imageButton.Click += new EventHandler(imageButton_Click);
StackPanel stackPanel = new StackPanel();
stackPanel.Children.Add(textButton);
stackPanel.Children.Add(imageButton);
mainWindow.Child = stackPanel;
stackPanel.SetMargin(20, 0, 20, 0);
mainWindow.Visibility = Visibility.Visible;
return mainWindow;
}
void imageButton_Click(object sender, EventArgs e) {
Debug.Print("ImageButton Click");
}
void textButton_Click(object sender, EventArgs e) {
Debug.Print("TextButton Click");
}
}
Here I’ve created two buttons laid-out in a Stackpanel, one containing a Text Control, the other one containing a Image Control. When one of the buttons is clicked the program prints a line to the debug console.

On the left you see the buttons in their default layout, on the right the first button is pressed.