preload
May 31

I’m working on the PinCodeKeeper Windows Phone 7 app and in this article I will walk you through how I made the Pin code card screen with a custom control to display the pin code card. I will continue from where I left when I previously completed the Pin code list screen.

The Pin code card screen will display the name of the card and a matrix with numbers on a coloured pattern. For the matrix that represents the good old pin code card I will create a custom control.

PinCodeCard custom control

To create a custom control right click in your Solution Explorer and select “Add” –> “New Item” –>”Windows Phone User Control”, name it PinCodeCard and click “Add”.

In the PinCodeCard custom control I will add all GUI elements dynamically from code so the only thing I add to the PinCodeCard.xaml is a Canvas that will act as a place holder for my controls.

The PinCodeCard.xaml looks like this.

<UserControl x:Class="PinCodeKeeper.PinCodeCard"
    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"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    d:DesignHeight="555" d:DesignWidth="480" xmlns:my="clr-namespace:PinCodeKeeper">
    
    <!-- Canvas used as a placeholder for dynamically created controls-->
    <Canvas x:Name="LayoutRoot" Height="555" Width="480" Background="{StaticResource PhoneBackgroundBrush}" VerticalAlignment="Top">
    </Canvas>
</UserControl>

In the PinCodeCard.xaml.cs I define a matrix (or a table if you like) consisting of 6 columns with 7 rows each. I define height and width for the cells. I wondered a while if I would like to have the colour pattern in the matrix generated randomly but I landed on that it was better to design the pattern myself and keep it hard coded in the class. Then I have one method that will create the pin code card by looping through the matrix adding a rectangle with a given colour for each cell. There are also a method that will loop through the matrix and add numbers (using TextBlock) on top of the rectangles.

This is all I did with the custom control for now since the screen I am making are only going to display the matrix. Later on when I’m creating the Add new pin code card screen I need to extend the custom control so the user can click on a cell and enter a number in it, I will cover that in another article later on.

You can see the PinCodeCard.xaml.cs below.

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace PinCodeKeeper
{
    public partial class PinCodeCard
    {
        //Constants used to calculate and draw matrix
        private const int SquareHeight = 75;
        private const int SquareWidth = 75;
        private const int NumberOfHorizontalSquares = 6;
        private const int NumerOfVerticalSquares = 7;

        //Offset used to align matrix in control
        private int _xOffset;
        private int _yOffset;

        //My color definitions
        private static readonly Color ColorRed = Color.FromArgb(0xFF, 0xD8, 0x3C, 0x3C);
        private static readonly Color ColorBlue = Color.FromArgb(0xFF, 0x6D, 0x97, 0xE8);
        private static readonly Color ColorGreen = Color.FromArgb(0xFF, 0x38, 0xCB, 0x5D);
        private static readonly Color ColorYellow = Color.FromArgb(0xFF, 0xFA, 0xEA, 0x29);

        //Hardcoded color pattern for the matrix
        private readonly Color[,] _colorMatrix = new Color[6,7] {
        { ColorGreen, ColorBlue, ColorGreen, ColorYellow, ColorRed, ColorBlue, ColorGreen },
        { ColorRed, ColorYellow, ColorRed, ColorYellow, ColorBlue, ColorYellow, ColorYellow },
        { ColorBlue, ColorYellow, ColorBlue, ColorBlue, ColorYellow, ColorGreen, ColorBlue },
        { ColorGreen, ColorYellow, ColorYellow, ColorGreen, ColorGreen, ColorBlue, ColorGreen },
        { ColorRed, ColorBlue, ColorRed, ColorBlue, ColorRed, ColorBlue, ColorGreen },
        { ColorRed, ColorGreen, ColorGreen, ColorYellow, ColorBlue, ColorRed, ColorRed } };

        //Matrix with numbers
        public int[,] PinCodeMatrix { get; set; }

        public PinCodeCard()
        {
            InitializeComponent();
            //Calculate offset to align matrix
            _xOffset = (int)(LayoutRoot.Width - (SquareWidth * NumberOfHorizontalSquares)) / 2;
            _yOffset = (int)(LayoutRoot.Height - (SquareHeight * NumerOfVerticalSquares)) / 2;
        }

        //Create graphics for the pin code card color pattern
        public void CreatePinCodeCard()
        {
            //Loop through matrix and add graphics
            for (int horizontal = 0; NumberOfHorizontalSquares > horizontal; horizontal++)
            {
                //Start x-position for the given horizontal index
                double xPosition = _xOffset + (horizontal*SquareWidth);
                for (int vertical = 0; NumerOfVerticalSquares > vertical; vertical++)
                {
                    //Start y-position for the given vertical index
                    double yPosition = _yOffset + (vertical*SquareHeight);

                    //Create a rectangle in the matrix
                    Rectangle rectangle = new Rectangle();
                    rectangle.Fill = new SolidColorBrush(_colorMatrix[horizontal,vertical]);
                    rectangle.Height = SquareHeight;
                    rectangle.Width = SquareWidth;
                    rectangle.SetValue(Canvas.LeftProperty, xPosition);
                    rectangle.SetValue(Canvas.TopProperty, yPosition);
                    rectangle.Stroke = new SolidColorBrush(Colors.Black);
                    rectangle.RadiusX = 10;
                    rectangle.RadiusY = 10;
                    LayoutRoot.Children.Add(rectangle);

                }
            }
        }

        //Add numbers for each cell in the matrix
        public void PopulateWithNumbers()
        {
            //Loop through matrix and add graphics
            for (int horizontal = 0; NumberOfHorizontalSquares > horizontal; horizontal++)
            {
                //Start x-position for the given horizontal index
                double xPosition = _xOffset + (horizontal * SquareWidth);
                for (int vertical = 0; NumerOfVerticalSquares > vertical; vertical++)
                {
                    //Start y-position for the given vertical index
                    double yPosition = _yOffset + (vertical * SquareHeight);

                    //Create a text block on top of the rectangle and align it to center
                    TextBlock text = new TextBlock();
                    text.Text = PinCodeMatrix[horizontal, vertical].ToString();
                    text.FontSize = 40;
                    text.Width = SquareWidth;
                    text.SetValue(Canvas.LeftProperty, xPosition);
                    double yTextOffset = (SquareHeight - text.ActualHeight) / 2;
                    text.SetValue(Canvas.TopProperty, yPosition + yTextOffset);
                    text.TextAlignment = TextAlignment.Center;
                    text.Foreground = new SolidColorBrush(Colors.Black);
                    LayoutRoot.Children.Add(text);
                }
            }
        }
    }
}

The PinCodeCard custom control is done for now and we will look at the PinCodeView that will be using the control.

Pin Code Screen

The PinCodeView.xaml is using the custom control and are also displaying a header with the name of the card and some application bar buttons.

You can see the PinCodeView.xaml below.

<phone:PhoneApplicationPage 
    x:Class="PinCodeKeeper.View.PinCodeView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    mc:Ignorable="d" d:DesignHeight="696" d:DesignWidth="480"
    shell:SystemTray.IsVisible="True" xmlns:my="clr-namespace:PinCodeKeeper">

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel contains the name of the application and page title(binded to the name of selected pin code card-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="PinCodeKeeper" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="{Binding Name}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!--Using the custom control PinCodeCard-->
        <my:PinCodeCard HorizontalAlignment="Left" x:Name="pinCodeCard" VerticalAlignment="Top" Width="480" Height="600" Grid.Row="1" Margin="0,-27,0,0" />
    </Grid>

    <!--ApplicationBar with "New", "Delete" and "About" buttons-->
    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="False">
            <shell:ApplicationBar.Buttons>
                <shell:ApplicationBarIconButton IconUri="/Images/plus32.png" Text="Add New" Click="AddNewBtnClicked"/>
                <shell:ApplicationBarIconButton IconUri="/Images/stop32.png" Text="Delete" Click="DeleteBtnClicked"/>
                <shell:ApplicationBarIconButton IconUri="/Images/questionbook32.png" Text="About" Click="AboutBtnClicked"/>
            </shell:ApplicationBar.Buttons>
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>

</phone:PhoneApplicationPage>

The code behind for the view in PinCodeView.xaml.cs take care of the logic and are using the PinCodePresenter to ask for data.

using System;
using System.Windows;
using PinCodeKeeper.Model;
using PinCodeKeeper.Presenter;

namespace PinCodeKeeper.View
{
    public partial class PinCodeView
    {
        private PinCodePresenter _presenter;

        public PinCodeView()
        {
            InitializeComponent();
            Loaded += PinCodeViewLoaded;
        }

        // When view is loaded create the presenter and tell the presenter that
        // the view wants to be initialized.
        void PinCodeViewLoaded(object sender, RoutedEventArgs e)
        {
            if (_presenter == null)
            {
                _presenter = new PinCodePresenter(this);
                _presenter.InitializeView();
            }
        }

        // Presenter passes viewData and the PinCodeList is set
        // as DataContext to ensure databinding.
        // The pin code matrix for the custom control is set manually.
        public void InitializeView(PinCode viewData)
        {
            DataContext = viewData;
            pinCodeCard.PinCodeMatrix = viewData.PinCodeMatrix;
            pinCodeCard.CreatePinCodeCard();
            pinCodeCard.PopulateWithNumbers();
        }

        // Add new pin code application bar button is clicked,
        // navigate to NewPinCodeView.
        private void AddNewBtnClicked(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("/View/NewPinCodeView.xaml", UriKind.Relative));
        }

        // About application bar button is clicked,
        // navigate to AboutView.
        private void AboutBtnClicked(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("/View/AboutView.xaml", UriKind.Relative));
        }

        // Delete pin code card application bar button is clicked.
        // Display confirmation box so user can confirm deleting
        // the pin code.
        private void DeleteBtnClicked(object sender, EventArgs e)
        {
            if (MessageBox.Show("Do you want to delete the pin code?", "Delete Pin Code", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
            {
                //Delete pin code
                _presenter.DeletePinCode();
                NavigationService.GoBack();
            }
        }
    }
}

The PinCodePresenter get the pin code selected from the previous view (stored in main model) and passes it to the view. The presenter is also responsible for deleting pin codes (notice that I’m not using the isolated storage at this point).

using PinCodeKeeper.Model;
using PinCodeKeeper.View;

namespace PinCodeKeeper.Presenter
{
    public class PinCodePresenter
    {
        private readonly PinCodeView _view;
        private PinCode _viewData;

        public PinCodePresenter(PinCodeView view)
        {
            _view = view;
        }

        // Initialize view with selected pin code.
        public void InitializeView()
        {
            _viewData = MainModel.Instance.SelectedPinCode;
            _view.InitializeView(_viewData);
        }

        // Delete pin code
        public void DeletePinCode()
        {
            //TODO Delete pin code from isolated storage
            MainModel.Instance.SelectedPinCode = null;
        }

    }
}

In the previous article I showed that I’m mocking up the data instead of getting it from isolated storage. At that time I only added the pin code name for the mock up data so now I have to go back and also add the numbers. The updated MockUpPinCodes method in PinCodeListPresenter.cs is displayed below.

        // Mockup pin codes so the we can test the
        // app with fake data and don't care about the
        // isolated storage.
        private void MockUpPinCodes()
        {
            //Mock some pin codes
            _viewData.PinCodes.Add(new PinCode()
            {
                Name = "Visa",
                PinCodeMatrix = new[,]
                                    {
                                        {1,5,8,3,0,4,7,},
                                        {3,8,6,7,9,0,1},
                                        {7,0,4,7,2,6,3},
                                        {6,6,8,4,2,3,5},
                                        {6,8,2,4,1,3,4},
                                        {9,0,5,3,1,6,2}
                                    }
            });
            _viewData.PinCodes.Add(new PinCode()
            {
                Name = "Mastercard",
                PinCodeMatrix = new[,]
                                    {
                                        {1,5,8,3,0,4,7,},
                                        {3,8,6,7,9,0,1},
                                        {7,0,4,7,2,6,3},
                                        {6,6,8,4,2,3,5},
                                        {6,8,2,4,1,3,4},
                                        {9,0,5,3,1,6,2}
                                    }
            });
            _viewData.PinCodes.Add(new PinCode()
            {
                Name = "Company door",
                PinCodeMatrix = new[,]
                                    {
                                        {1,5,8,3,0,4,7,},
                                        {3,8,6,7,9,0,1},
                                        {7,0,4,7,2,6,3},
                                        {6,6,8,4,2,3,5},
                                        {6,8,2,4,1,3,4},
                                        {9,0,5,3,1,6,2}
                                    }
            });
        }

The result

I have now completed the Pin code card screen and it looks like this.

Pin code card screen

The next step now will be to create the Add new pin code functionality and extend the custom control with touch capabilities, animation and numeric keyboard.

Follow me on twitter @PerOla

Share & enjoy
You can subscribe to my comments feed to keep track of new comments.

No Comments to “PinCodeKeeper WP7 app – Creating the Pin code card Custom Control”

2 Pingbacks to “PinCodeKeeper WP7 app – Creating the Pin code card Custom Control”

  1. […] a WP7 project for the PinCodeKeeper app PinCodeKeeper WP7 app – Creating the Pin code card Custom Control May […]

  2. […] PinCodeKeeper WP7 app – Creating the Pin code card Custom Control Jun 09 […]

Leave a Reply

Subscribe to my comments feed

Subscribe to my feeds Follow me on Twitter
DZone MVB