A Simple Paint App on Windows Phone 7 using InkPresenter

Windows Phone 7 tools comes shipped with InkPresenter control . In this simple application , I  will select a color and draw on drawing surface which is an InkPresenter. Lets start.

Open Expression Blend , Select Windows Phone Application template.

pic1

For drawing surface , select  the InkPresenter control from Assets panel and draw it on ContentPanel(Grid). Call it as drawingCanvas.

pic3

Draw a simple palette  with ellipses of various colors laid over a colored rectangle to make it look like a palette.

pic4

After setting the page name and background color , below is how my paint app looks. Cool isn’t it.. 🙂

alt

Ok so UI is ready, lets wire it up with code-behind.

We need 2 variables . One to capture stroke and other to set the color of stroke.

SolidColorBrush colorPicked;
Stroke _colorStroke;

To capture mouse movements, we have to handle MouseLeftButtonUp, MouseLeftButtonDown and MouseMove events for the drawingCanvas.

 private void drawingCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            drawingCanvas.CaptureMouse();
            _colorStroke = new Stroke();
            _colorStroke.StylusPoints.Add(GetStylusPoint(e.GetPosition(drawingCanvas)));
            _colorStroke.DrawingAttributes.Color = colorPicked.Color;
            drawingCanvas.Strokes.Add(_colorStroke);

        }

        private StylusPoint GetStylusPoint(Point position)
        {
            return new StylusPoint(position.X, position.Y);
        }
        private void drawingCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (_colorStroke != null)
            {
                _colorStroke.StylusPoints.Add(GetStylusPoint(e.GetPosition(drawingCanvas)));
            }
        }

        private void drawingCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            _colorStroke = null;
        }

 

For selecting a color from palette we will wire up every ellipse in palette with a MouseLeftButtonDown  event and  set brush used to draw strokes with the Fill of the ellipse .

 private void SelectColor(object sender, MouseButtonEventArgs e)
        {
            Ellipse selectedEllipse = sender as Ellipse;
            colorPicked = selectedEllipse.Fill as SolidColorBrush ;
        }

XAML code is below.

<Grid x:Name="LayoutRoot">
		<Grid.Background>
			<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
				<GradientStop Color="#FF00243B" Offset="0"/>
				<GradientStop Color="#FF005185" Offset="0.992"/>
			</LinearGradientBrush>
		</Grid.Background>
		<Grid.RowDefinitions>
			<RowDefinition Height="Auto"/>
			<RowDefinition Height="*"/>
		</Grid.RowDefinitions>

		<!--TitlePanel contains the name of the application and page title-->
		<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
			<TextBlock x:Name="ApplicationTitle" Style="{StaticResource PhoneTextNormalStyle}" Foreground="#FFA19696"/>
			<TextBlock Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}" Foreground="#FFC04111"><Run Text="P">
					<Run.Foreground>
						<SolidColorBrush Color="#FFA120FF">
							<SolidColorBrush.RelativeTransform>
								<MatrixTransform Matrix="Identity"/>
							</SolidColorBrush.RelativeTransform>
							<SolidColorBrush.Transform>
								<MatrixTransform Matrix="Identity"/>
							</SolidColorBrush.Transform>
						</SolidColorBrush>
					</Run.Foreground>
				</Run><Run Text="a">
					<Run.Foreground>
						<SolidColorBrush Color="#FF1BADEB">
							<SolidColorBrush.RelativeTransform>
								<MatrixTransform Matrix="Identity"/>
							</SolidColorBrush.RelativeTransform>
							<SolidColorBrush.Transform>
								<MatrixTransform Matrix="Identity"/>
							</SolidColorBrush.Transform>
						</SolidColorBrush>
					</Run.Foreground>
				</Run><Run Text="i">
					<Run.Foreground>
						<SolidColorBrush Color="#FFFFE153">
							<SolidColorBrush.RelativeTransform>
								<MatrixTransform Matrix="Identity"/>
							</SolidColorBrush.RelativeTransform>
							<SolidColorBrush.Transform>
								<MatrixTransform Matrix="Identity"/>
							</SolidColorBrush.Transform>
						</SolidColorBrush>
					</Run.Foreground>
				</Run><Run Text="n">
					<Run.Foreground>
						<SolidColorBrush Color="#FF56C011">
							<SolidColorBrush.RelativeTransform>
								<MatrixTransform Matrix="Identity"/>
							</SolidColorBrush.RelativeTransform>
							<SolidColorBrush.Transform>
								<MatrixTransform Matrix="Identity"/>
							</SolidColorBrush.Transform>
						</SolidColorBrush>
					</Run.Foreground>
				</Run><Run Text="t "/></TextBlock>
		</StackPanel>
		<Grid x:Name="ContentPanel" Margin="12,0,12,0" Grid.Row="1" >
			<InkPresenter Background="White"
				x:Name="drawingCanvas" MouseLeftButtonDown="drawingCanvas_MouseLeftButtonDown"
				MouseMove="drawingCanvas_MouseMove"
				MouseLeftButtonUp="drawingCanvas_MouseLeftButtonUp" Margin="8,8,8,197"/>
			<Rectangle Height="174" RadiusY="26" RadiusX="26" Margin="8,0,8,8" VerticalAlignment="Bottom">
				<Rectangle.Fill>
					<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
						<GradientStop Color="#FF065474" Offset="0.004"/>
						<GradientStop Color="Black" Offset="1"/>
					</LinearGradientBrush>
				</Rectangle.Fill>
			</Rectangle>
			<Ellipse Height="66" Width="68" Fill="#FF005CF1"
				MouseLeftButtonDown="SelectColor" HorizontalAlignment="Left" Margin="26,0,0,20" VerticalAlignment="Bottom"/>

			<Ellipse Height="66" Width="68" Fill="#FFFF0C00"
				MouseLeftButtonDown="SelectColor" HorizontalAlignment="Left" Margin="26,0,0,96" VerticalAlignment="Bottom"/>

			<Ellipse Height="66" Width="68" Fill="#FFFDFF1F"
				MouseLeftButtonDown="SelectColor" HorizontalAlignment="Left" Margin="108,0,0,20" VerticalAlignment="Bottom"/>

			<Ellipse Height="66" Width="68" Fill="#FF3AC00C"
				MouseLeftButtonDown="SelectColor" HorizontalAlignment="Left" Margin="106,0,0,96" VerticalAlignment="Bottom"/>

			<Ellipse Height="66" Fill="#FF80007B"
				MouseLeftButtonDown="SelectColor" Margin="184,0,204,96" VerticalAlignment="Bottom"/>

			<Ellipse Height="66" Width="68" Fill="#FFE99A0B"
				MouseLeftButtonDown="SelectColor" HorizontalAlignment="Right" Margin="0,0,128,96" VerticalAlignment="Bottom"/>

			<Ellipse Height="66" Width="68" Fill="#FF9D073E"
				MouseLeftButtonDown="SelectColor" HorizontalAlignment="Right" Margin="0,0,48,96" VerticalAlignment="Bottom"/>

			<Ellipse Height="66" Fill="#FF1A0CC0"
				MouseLeftButtonDown="SelectColor" Margin="184,0,204,20" VerticalAlignment="Bottom"/>

			<Ellipse Height="66" Width="68" Fill="#FFA6C00C"
				MouseLeftButtonDown="SelectColor" HorizontalAlignment="Right" Margin="0,0,128,16" VerticalAlignment="Bottom"/>

			<Ellipse Height="66" Width="68" Fill="#FFBE02F9"
				MouseLeftButtonDown="SelectColor" HorizontalAlignment="Right" Margin="0,0,46,18" VerticalAlignment="Bottom"/>
		</Grid>

		<!--ContentPanel - place additional content here-->
	</Grid>

This is it . Run your app and enjoy playing with strokes of different colors.

pic6

The xaml code above can surely be customized to include a WrapPanel with ellipses of various colors and loading the palette on click of button.

Below are some real good links for working with InkPresenter.

http://www.nickharris.net/2010/03/silverlight-for-mobile-on-windows-phone-7-inkpresenter-fun/

http://rongchaua.net/blog/windows-phone-signature-capturing-with-inkpresenter-and-save-to-png-file/

Advertisements

Push Notification in Windows Phone 7

Windows Phone 7 has now got support for Push Notifications. Before diving into the implementation let us understand the working of Push Technology.

Push Technology:

This technology involves communication in form of messages initiated by a publisher or central server. Push services follow publish-subscribe model where in the server pushes the information to the clients and the client application subscribes and pulls the information.

Windows Phone 7 Push Notification functionality support :

Windows Phone 7 does not support multitasking, hence push notification is the only method to notify the user.

The push services work as follows in line of business applications:

 image

Steps:

1)  The cloud service sends notification requests to Push Notification service.

2)  It routes the notification to the application. Notifications may be one of the three types:

       a. Tile Notification – The tile notifications are handled by phone ,if the application is pinned to the start page. The title, tile image and the tile number are three items that can be altered with a tile notification. The phone receives tile notifications only when application is not in running state.

       b. Toast Notifications – A toast notification comes in when the application is running. If the user clicks the notification, the application will launch.

       c. Raw Notifications – Raw notifications include raw data of any format and size but within limits. The phone receives them only when the application is running.

3)  Once the notification type is set, a notification channel is created to receive the push notifications at the client side. A subscription is also created which allows the cloud service to push notifications to that channel. A channel is represented by a URI which contains all of the information associated with the subscription.

4)  Once an application receives the push notification, it can access the cloud service using the cloud service’s protocol to retrieve any needed information.

Now lets us go through the steps to complete our demo :

Step 1: Simulating the cloud service to sending Push Notifications.

Step 2 : Setting up notification channel to receive notifications.

Step 3: Registering with the notification service.

Step 4 : A client application on Windows Phone 7 receiving notifications.

The solution structure will include 3 projects:

img1

NotificationService:  A WCF project acting as a background process to send messages.
NotificationSender:   A WPF project sending messages.
NotificationConsumer:  Windows Phone 7 client application receiving messages and displaying them as notifications.

NotificationService Project:

 img2

The service contract includes methods to send toast messages, send raw messages and let client application subscribe for notifications:

[ServiceContract]
    public interface IService1
    {

      //function to let client application subscribe to notification service.
      [OperationContract]
        void Subscribe(Guid _uniqueID,string uri);

        //method to send toast notifications
        [OperationContract]
        void SendToastNotification(string message);

        //method to send raw notifications.
        [OperationContract]
        void SendRawNotification(string message);

    }

The class(Service1.svc.cs) implementing the service is as follows:

namespace NotificationService
{

    //Notification Types
    public enum NotificationType
    {
        Tile = 1,
        Toast = 2,
        Raw = 3
    }

    //Time interval required to send messages.
    public enum BatchingInterval
    {
        TileImmediately = 1,
        ToastImmediately = 2,
        RawImmediately = 3,
        TileWait450 = 11,
        ToastWait450 = 12,
        RawWait450 = 13,
        TileWait900 = 21,
        ToastWait900 = 22,
        RawWait900 = 23
    }

    public class Service1 : IService1
    {

        private static Dictionary<Guid, Uri> _clientUris = new Dictionary<Guid, Uri>();

        public void Subscribe(Guid _uniqueID, string uri)
        {
            if (_clientUris.ContainsKey(_uniqueID))
            {
                _clientUris[_uniqueID] = new Uri(uri);
            }

            else
            {
                _clientUris.Add(_uniqueID, new Uri(uri));
            }
        }

        public void SendToastNotification(string message)
        {
            var toastMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                 "<wp:Notification xmlns:wp=\"WPNotification\">" +
                    "<wp:Toast>" +
                       "<wp:Text1>{0}</wp:Text1>" +
                      "</wp:Toast>" +
                 "</wp:Notification>";
            toastMessage = string.Format(toastMessage,message);

            var messageBytes = System.Text.Encoding.UTF8.GetBytes(toastMessage);

            foreach (var uri in _clientUris.Values)
            {
                SendMessage(uri, messageBytes, NotificationType.Toast);
            }
        }

        public void SendRawNotification(string message)
        {
            var messageBytes = Encoding.UTF8.GetBytes(message);

            foreach (var uri in _clientUris.Values)
            {
                SendMessage(uri, messageBytes, NotificationType.Raw);
            }   
        }

        public void SendTileNotification(string message)
        {
            throw new NotImplementedException();
        }

        private void SendMessage(Uri uri, byte[] messageBytes, NotificationType notificationType)
        {
            var request = (HttpWebRequest)WebRequest.Create(uri);
            request.Method = WebRequestMethods.Http.Post;
            request.ContentType = "text/xml";
            request.ContentLength = messageBytes.Length;

            request.Headers.Add("X-MessageID", Guid.NewGuid().ToString());

            if(notificationType == NotificationType.Toast)
            {
                    request.Headers["X-WindowsPhone-Target"] = "toast";
                    request.Headers.Add("X-NotificationClass",((int)BatchingInterval.ToastImmediately).ToString());
            }
            else
            {

                    request.Headers.Add("X-NotificationClass", ((int)BatchingInterval.RawImmediately).ToString());
            }

            using (var requestStream = request.GetRequestStream())
            {
                requestStream.Write(messageBytes, 0, messageBytes.Length);
            }

         }
    }
}

Web.config file including endpoint settings for the service.

img3

 

NotificationSender Project:

img4

This WPF project sends text lines to service which are sent either as toast notifications or raw notifications.

MainWindow.xaml

img5

Code behind for MainWindow.xaml.cs

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void SendtoastNotification_Click(object sender, RoutedEventArgs e)
        {
            var toastService = new NotificationSenderReference.Service1Client();
            toastService.SendToastNotification(txtToastMessage.Text);
        }

        private void SendRawNotification_Click(object sender, RoutedEventArgs e)
        {
            var rawService = new NotificationSenderReference.Service1Client();
            rawService.SendRawNotification(txtRawMessage.Text);
        }
    }

 

NotificationConsumer Project:

img6

MainPage.xaml: XAML Code for the above design. All messages are added as items in Listbox.

<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–>
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="24,24,0,12">
            <TextBlock x:Name="ApplicationTitle" Text="PUSH NOTIFICATION SAMPLE" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="UPDATES" Margin="-3,-8,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!–ContentPanel – place additional content here–>
        <Grid x:Name="ContentGrid" Grid.Row="1" Background="Gray" >

            <ListBox x:Name="lstOffers">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                            <TextBlock Text="{Binding}" FontFamily="Segoe WP Light"  FontSize="20"/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Grid>

MainPage.xaml.cs code:

public partial class MainPage : PhoneApplicationPage
   {
       private HttpNotificationChannel _channel;
       private const string ChannelName = "Notifications.Phone.NotificationChannel";
       private Guid _deviceId;

       public MainPage()
       {
           InitializeComponent();
           if (DesignerProperties.IsInDesignTool)
               return;

           if (IsolatedStorageSettings.ApplicationSettings.Contains("DeviceId"))
           {
               _deviceId = (Guid)IsolatedStorageSettings.ApplicationSettings["DeviceId"];
           }
           else
           {
               _deviceId = Guid.NewGuid();
               IsolatedStorageSettings.ApplicationSettings["DeviceId"] = _deviceId;
           }

           SetupNotificationChannel();
       }

       private void SetupNotificationChannel()
       {
           _channel = HttpNotificationChannel.Find(ChannelName);

           if (_channel == null)
           {
               _channel = new HttpNotificationChannel(ChannelName);
               _channel.ChannelUriUpdated += ChannelUriUpdated;
               _channel.ErrorOccurred += (s, e) => Deployment.Current.Dispatcher.BeginInvoke(() => ErrorOccurred(e));
               _channel.Open();
           }
           else
           {
               RegisterForNotifications();
           }
       }

       private void RegisterForNotifications()
       {
           RegisterWithNotificationService();
           _channel.ShellToastNotificationReceived += (s, e) => Deployment.Current.Dispatcher.BeginInvoke(() => ToastReceived(e));
           _channel.HttpNotificationReceived += (s, e) => Deployment.Current.Dispatcher.BeginInvoke(() => HttpNotificationReceived(e));
           _channel.ErrorOccurred += (s, e) => Deployment.Current.Dispatcher.BeginInvoke(() => ErrorOccurred(e));
       }

       private void HttpNotificationReceived(HttpNotificationEventArgs e)
       {
           var reader = new StreamReader(e.Notification.Body);
           var message = reader.ReadToEnd();
           .Items.Add("Raw notification : " + message);
           reader.Close();
       }

       private void RegisterWithNotificationService()
       {
           var svc = new NotificationServiceReference.Service1Client();

           svc.SubscribeCompleted += (s, e) =>
           {
               if (e.Error != null)
               {
                   Debug.WriteLine("Error registering with notification service");
               }
           };

           svc.SubscribeAsync(_deviceId, _channel.ChannelUri.ToString());
       }

       private void ToastReceived(NotificationEventArgs e)
       {
           lstOffers.Items.Add("Toast Recevied : "+ e.Collection["wp:Text1"]);
        }

       private void ErrorOccurred(NotificationChannelErrorEventArgs e)
       {

           lstOffers.Items.Add(e.Message);
           Debug.WriteLine("ERROR:" + e.Message);
       }

       private void ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
       {
           _channel = HttpNotificationChannel.Find(ChannelName);

           if (!_channel.IsShellToastBound)
           {
               _channel.BindToShellToast();
           }

           RegisterForNotifications();
       }
   }

 

Now let’s run the project:

First

1) Run the NotificationService, to start the service.

2) Start a new instance of NotificationSender application .

3) Start the Windows Phone 7 application to receive notifications.

Output:

img7

 

img8

 

So that was a simple demo on Push Notifications on Windows Phone 7. For receiving Tile notifications you can check below links and more information:

Excellent blogs:

http://www.thisisfanzoo.com/Blog/JeffF/archive/2010/08/02/a-really-long-post-about-the-windows-phone-7-push.aspx

http://chris.59north.com/post/Using-Windows-Phone-7-Push-Notifications.aspx

MSDN Links:

http://msdn.microsoft.com/en-us/library/ff402537%28v=VS.92%29.aspx