How to use the ButtonImage control that supports the Pressed and IsEnabled states

Introduction

There is a lot of examples in the Windows Phone community about creating a button with an image only. We can achieve this result with the following code:

<Button>
    <Image Source="MyImage.png"/>
</Button>

The problem with the previous code is the impossibility to set a different image when the button is disabled or pressed.


Solution

I created a ButtonImage class to set a different image for the disabled and the pressed states.

If you are not interested on the code of the ButtonImage class, you can skip this section and go directly to the Usage section.

The ButtonImage class has XML code and C# code.

XML code

<Button x:Class="DotNetApp.ButtonImage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    IsEnabledChanged="ButtonIsEnabledChanged"
    MouseEnter="ButtonMouseEnter"
    MouseLeave="ButtonMouseLeave">

    <Image Stretch="None"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        x:Name="image" />
</Button>

There is nothing fancy here, it is a button that has an image has content.

C# code

using System;
using System.Windows;
using System.Windows.Media.Imaging;

namespace DotNetApp
{
    public partial class ButtonImage
    {
        #region Fields

        public new static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register("IsEnabled", typeof (bool), typeof (ButtonImage), null);
        public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register("ImageSource", typeof (string), typeof (ButtonImage), null);
        public static readonly DependencyProperty ImagePressedSourceProperty = DependencyProperty.Register("ImagePressedSource", typeof (string), typeof (ButtonImage), null);
        public static readonly DependencyProperty ImageDisabledSourceProperty = DependencyProperty.Register("ImageDisabledSource", typeof (string), typeof (ButtonImage), null);

        private BitmapImage _image;
        private BitmapImage _imagePressed;
        private BitmapImage _imageDisabled;
        private bool _isPressed;

        #endregion

        #region Constructor

        public ButtonImage()
        {
            InitializeComponent();
        }

        #endregion

        #region Properties

        public new bool IsEnabled
        {
            get { return (bool)GetValue(IsEnabledProperty); }

            set
            {
                SetValue(IsEnabledProperty, value);

                SetImageFromState();
            }
        }

        public string ImageSource
        {
            get { return (string) GetValue(ImageSourceProperty); }

            set
            {
                SetValue(ImageSourceProperty, value);

                _image = SetImage(value);
                SetImageFromState();
            }
        }
        
        public string ImagePressedSource
        {
            get { return (string) GetValue(ImagePressedSourceProperty); }

            set
            {
                SetValue(ImagePressedSourceProperty, value);

                _imagePressed = SetImage(value);
                SetImageFromState();
            }
        }

        public string ImageDisabledSource
        {
            get { return (string) GetValue(ImageDisabledSourceProperty); }

            set
            {
                SetValue(ImageDisabledSourceProperty, value);

                _imageDisabled = SetImage(value);
                SetImageFromState();
            }
        }

        #endregion

        #region Event Handlers

        private void ButtonIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            SetImageFromState();
        }

        private void ButtonMouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
            _isPressed = true;

            SetImageFromState();
        }

        private void ButtonMouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
        {
            _isPressed = false;

            SetImageFromState();
        }

        #endregion

        #region Private Methods

        private static BitmapImage SetImage(string imageSource)
        {
            BitmapImage bitmapImage = null;

            if (!string.IsNullOrEmpty(imageSource))
            {
                bitmapImage = new BitmapImage(new Uri(imageSource, UriKind.RelativeOrAbsolute));
            }

            return bitmapImage;
        }

        private void SetImageFromState()
        {
            if (!IsEnabled)
            {
                image.Source = _imageDisabled;
            }
            else if (_isPressed)
            {
                image.Source = _imagePressed;
            }
            else
            {
                image.Source = _image;
            }
        }

        #endregion
    }
}

Once again, there is nothing scientific here. I load the images and based on the event handlers (ButtonIsEnabledChanged, ButtonMouseEnter and ButtonMouseLeave), I set the right image.


Usage

ButtonImage.png

The button has an image for each state:

  • Normal

ImageNormal.png

  • Pressed

ImagePressed.png

  • Disabled

ImageDisabled.png

To use the ButtonImage control in your app, just follow those steps:

  1. Add the files ButtonImage.xaml and ButtonImage.xml.cs to your project.
  2. Add the normal image, the pressed image and the disabled image to your project. If your button does not need a pressed image or a disabled image, you don’t have to specify one.
  3. Add the XML code
<ButtonImageApp:ButtonImage ImageSource="/ImageNormal.png"
                                                    ImageDisabledSource="/ImageDisabled.png"
                                                    ImagePressedSource="/ImagePressed.png"
                                                    Style="{StaticResource ButtonImageStyle}"
                                                    Height="50"
                                                    Width="150" />

All the properties are important. The ImageSource, ImageDisabledSource, ImagePressedSource are the actual images that you want for each state. The Height and the Width need to be the same as the specified images.

The last piece of the puzzle is the Style property set to "{StaticResource ButtonImageStyle}". If you do not set this property, there will be an unwanted transparent area around the image and the image will be smaller. Your last step is to define the ButtonImageStyle into your project. You can add it into your page or in the App resources section. The code to copy is:

Page

<phone:PhoneApplicationPage.Resources>

    <Style x:Key="ButtonImageStyle"
            TargetType="ButtonImageApp:ButtonImage">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ButtonImageApp:ButtonImage">
                    <Grid Background="Transparent">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal" />
                                <VisualState x:Name="MouseOver" />
                                <VisualState x:Name="Pressed" />
                                <VisualState x:Name="Disabled" />
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Border x:Name="ButtonBackground">
                            <ContentControl x:Name="ContentContainer"
                                            ContentTemplate="{TemplateBinding ContentTemplate}"
                                            Content="{TemplateBinding Content}" />
                        </Border>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</phone:PhoneApplicationPage.Resources>

or App

<Application.Resources>

    <Style x:Key="ButtonImageStyle"
        TargetType="ButtonImageApp:ButtonImage">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ButtonImageApp:ButtonImage">
                    <Grid Background="Transparent">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal" />
                                <VisualState x:Name="MouseOver" />
                                <VisualState x:Name="Pressed" />
                                <VisualState x:Name="Disabled" />
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Border x:Name="ButtonBackground">
                            <ContentControl x:Name="ContentContainer"
                                        ContentTemplate="{TemplateBinding ContentTemplate}"
                                        Content="{TemplateBinding Content}" />
                        </Border>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</Application.Resources>


Conclusion

The ButtonImage derives from a Button so you can add a Command or the Click handler.


Download

My WinSource app is now open source on the Nokia developer website

Introduction

In the summer 2012, the ​WinSource website held a contest among the Windows Phone developers to create an official application for their website.

I was a participant in the contest and I can tell you that it was a really interesting and challenging contest. My application finished in third position.

You can download the application from the winner at ​here.

Important notice

Before I decided to put the full source code of my complete application. I asked the owners of the ​WinSource from the Neverstill company if I can publish the source code of my application. They accepted my idea, because it is a great way to help the Windows Phone community.

In the current form, the application has enough features that it can be an alternate ​WinSource application. The only restriction is a user cannot publish or use the ​WinSource’s logo, the assets or the content from the ​WinSource. NOTE, this project is ​licensed under a slightly modified MIT license which reflects the above restriction.

The purpose of having this open project is to help the community creating a professional news reader for any other websites that give the permissions. It also shows different concepts which can be applied in different type of applications.

The application

https://projects.developer.nokia.com/dav/newsreaderpro/WinSource.png

My application contains the following features:

  • The metro design is used throughout the app, while the main page is showcased using the panorama design.
  • Fast navigation between pages.
  • Articles classified by date.
  • Play YouTube videos.
  • A description of each team member, including a display of the author name & image on each author’s page. Also included is the option to send the author an email.
  • Article links can be shared on Twitter, Windows Live, Facebook, or by email.
  • The application bar contains shortcuts to the Facebook page and the Twitter page of the website.
  • The About page provides a way to send an email and to write a review of the app.
  • Option to automatically refresh articles when the application launches.

The following list contains advanced features:

  • Offline mode. Articles can be read even when no internet connection is available.
  • Unlimited space for saving articles, which can then be accessed using the Saved Articles page.
  • Articles can be periodically downloaded (every ~4 hours) in the background, even when the app is not running. This feature does not require server support.
  • Toast notification will display the number of new articles available, and a badge count in the application live tile will also display this number.
  • The back of the live tile will display the latest article title.

https://projects.developer.nokia.com/dav/newsreaderpro/LiveTileAndToastNotification.png

The application uses the Pivot control for the team members:
https://projects.developer.nokia.com/dav/newsreaderpro/Pivot.png
And a standard page for an article:
https://projects.developer.nokia.com/dav/newsreaderpro/Article.png

Open source libraries

I used three open source libraries that are well known in the developer community:

Architecture

I use the Model View-Model View (MVVM) pattern in my solution. The solution is separated in five assemblies:

  • DotNetApp.Toolkit: It includes utility classes that can be reused in any Windows Phone projects. There is also my Request class that helps calling web resources.
  • WinSource: This is the main project that includes the views, the data service, the view models and many more things.
  • WinSource.Agent: It is the project that includes the schedule background service which permits to periodically download articles in the background.
  • WinSource.Client: It contains all the web requests.
  • WinSource.Model: It contains only the data structure.

You won’t find a lot of comment in my code, but I’m an heavy user on code convention and I tend use write small methods so the code should be self-explanatory.

How to build the application

All the required files are built-in in the solution. Make sure that the selected project is “WinSource” then press F5 to launch the application.

  • The application has been tested with the Windows Phone SDK 7.1.

Future of the application

The application in its current form contains more features then some published news applications in the Windows Phone Store. That’s been said, the application is in a release state. As all software, it is always possible to polish and add features. You are welcome to add and suggest features.

What can you do with the source files of the application?

Simple answer: create your own news application! The source files provided are the best starting point to get your news application ready. The mechanism is ready and your tasks are:

  • Replace the assets in the Assets folder of the WinSource assembly.
  • Modify the classes in the WinSource.Client assembly in order to fetch the article descriptions and videos from your news source.
  • Remove or add sections in the Panorama.
Learning

Also, if you read the code you can learn a lot about the Windows Phone SDK and about some best practices. The application is more complete than the ones in the MSDN samples.

Conclusion

You can contribute directly to the project on the Nokia developer website or you can download the source code.

Happy coding!

Contribute on the Nokia developer website
Download Sample project