Windows Phone – Embedded fonts and Bing maps

Today I would like to illustrate about how I can embed font and Bing maps into Windows Phone application. I know it’s a such simple example that every programmer on Windows Phone can write but I just want to make a demo to show that we can use embedded font and host a Bing map control. For Bing map example, I use this example http://www.earthware.co.uk/blog/index.php/2010/03/writing-a-bing-maps-location-aware-application-for-windows-phone-7-series/ with some modifications so that I can relatively find my location on Bing map.

Embedded fonts

Silverlight allows us to embed font in application without installing it on operating system. Using custom font makes GUI of application really beautiful and creative. To embed font, copy it to a folder of your project, set “Build Action” to “Content” and link to control as following

<TextBlock FontFamily="Fonts/AnnabelScript.ttf#Annabel Script" Text="Duration" x:Name="txtDuration" Margin="20,530,173,82"/>

Bing maps

To use Bing map control, you can read the example above. I just tell which I learned when I was trying to use Microsoft.Maps.MapControl library. It’s really very strange. After adding reference to Microsoft.Maps.MapControl, if I add this reference in XAML first

xmlns:bing="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"

Then I can not use Map control in XAML.

<bing:Map Name="mapMain" NavigationVisibility="Collapsed" Mode="AerialWithLabels" CredentialsProvider="USE_YOUR_BING_MAP_KEY" Height="577">
	<bing:MapLayer>
		<Ellipse Fill="Red" Width="20" Height="20" bing:MapLayer.Position="0,0" Name="ppLocation" Visibility="Collapsed" />
	</bing:MapLayer>
</bing:Map>

Visual Studio says that it can not find Map control and asks if I forget to add reference to assembly. But if I use Map control in XAML first and add reference later then everything works.

private void btnBrowse_Click(object sender, RoutedEventArgs e)
{
	GetLocationOfCity();
}

private void GetLocationOfCity()
{
	string strGetIpUrl = "http://www.whatismyip.com/automation/n09230945.asp";
	WebClient wcIP = new WebClient();
	wcIP.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wcIP_DownloadStringCompleted);
	wcIP.DownloadStringAsync(new Uri(strGetIpUrl, UriKind.Absolute));
}

void wcIP_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
	string strIP = e.Result;
	string strLocation = "http://api.hostip.info/?ip=" + strIP + "&position=true";
	WebClient wcLocation = new WebClient();
	wcLocation.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wcLocation_DownloadStringCompleted);
	wcLocation.DownloadStringAsync(new Uri(strLocation, UriKind.Absolute));
}

void wcLocation_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
	string strResult = e.Result;
	string strOpenTag = "<gml:coordinates>";
	int nStartIndex = strResult.IndexOf(strOpenTag) + strOpenTag.Length;
	string strClosedTag = "</gml:coordinates>";
	int nEndIndex = strResult.IndexOf(strClosedTag);
	string strCoordinates = strResult.Substring(nStartIndex, nEndIndex - nStartIndex);
	double longtitude = Convert.ToDouble(strCoordinates.Split(',')[0]);
	double latitude = Convert.ToDouble(strCoordinates.Split(',')[1]);
	Location locCurrent = new Location(latitude, longtitude);
	mapMain.SetView(locCurrent, 15);

	MapLayer.SetPosition(ppLocation, locCurrent);
	ppLocation.Visibility = System.Windows.Visibility.Visible;
}

As you can see, because the location service is not available with emulator cause of lacking GPS device. I modify the example by getting my IP address through service of whatismyip and then pass my IP to service of hostip to get the longitude and latitude of that IP. With these information I can use Bing map control to navigate to my location but it’s not exact 100%. I hope with a real phone and GPS device, the Bing map can locate exactly where I am.
You can download the source code of Bing map example “Windows Phone Bing Map

Windows Phone – Signature capturing with InkPresenter and save to PNG file

Today I received a small package by post for my neighbor and I must sign on a small device to confirm that I received the package. Therefore the deliver can be sure that I will give it to my neighbor. I thought it was very interesting to write a small application on Windows Phone which should work like the device of postman. This application allows people signing on the screen and it will store this signature into IsolatedStorageFile and show it again in image control below to confirm that the program works correctly.

As you can see in the demo image of application, I have 4 buttons: Undo, Redo, Clear and Save. The “Undo”, “Redo” buttons allow me to remove the strokes or redraw them. The strokes will be drawn on InkPresenter control and then saved into a PNG file. To draw the stroke on InkPresenter, I must handle the MouseLeftButtonDown to capture the mouse movement and draw stroke from point to point.

private StylusPoint GetStylusPoint(Point p)
{
	return new StylusPoint(p.X, p.Y);
}
private void inkSignature_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
	inkSignature.CaptureMouse();
	m_strokeCurrent = new Stroke();
	m_strokeCurrent.StylusPoints.Add(GetStylusPoint(e.GetPosition(inkSignature)));
	m_strokeCurrent.DrawingAttributes.Color = Colors.Blue;
	inkSignature.Strokes.Add(m_strokeCurrent);
}
private void inkSignature_MouseMove(object sender, MouseEventArgs e)
{
	if (m_strokeCurrent != null)
		m_strokeCurrent.StylusPoints.Add(GetStylusPoint(e.GetPosition(inkSignature)));
}

To implement “Undo” and “Redo” feature, I use a stack of strokes to push/pop a stroke in history and then remove/add it on InkPresenter.

private void btnUndo_Click(object sender, RoutedEventArgs e)
{
	if (inkSignature.Strokes != null && inkSignature.Strokes.Count > 0)
	{
		m_stackRemovedStrokes.Push(inkSignature.Strokes.Last());
		inkSignature.Strokes.RemoveAt(inkSignature.Strokes.Count - 1);
	}
}

private void btnRedo_Click(object sender, RoutedEventArgs e)
{
	if (m_stackRemovedStrokes != null && m_stackRemovedStrokes.Count > 0)
	{
		inkSignature.Strokes.Add(m_stackRemovedStrokes.Pop());
	}
}

At last, I use WriteableBitmap to take a snapshot of InkPresenter and encode it to a PNG format file. In current version of Visual Studio for Windows Phone CTP there is still not a PngBitmapEncoder. Therefore I use a custom one from this blog http://geekswithblogs.net/braulio/archive/2009/07/12/export-canvas-to-png-and-save-it-in-your-local.aspx . You can find the source code of PNG Encoder in the archive at the end of this post, too.

private void btnSave_Click(object sender, RoutedEventArgs e)
{
	WriteableBitmap wbBitmap = new WriteableBitmap(inkSignature, new TranslateTransform());
	EditableImage eiImage = new EditableImage(wbBitmap.PixelWidth, wbBitmap.PixelHeight);

	try
	{
		for (int y = 0; y < wbBitmap.PixelHeight; ++y)
		{
			for (int x = 0; x < wbBitmap.PixelWidth; ++x)
			{
				int pixel = wbBitmap.Pixels[wbBitmap.PixelWidth * y + x];
				eiImage.SetPixel(x, y,
				(byte)((pixel >> 16) & 0xFF),
				(byte)((pixel >>  8 ) & 0xFF ) ,
				(byte)(pixel & 0xFF), (byte)((pixel >> 24) & 0xFF)
				);
			}
		}
	}
	catch (System.Security.SecurityException)
	{
		throw new Exception("Cannot print images from other domains");
	}

	// Save it to disk
	Stream streamPNG = eiImage.GetStream();
	StreamReader srPNG = new StreamReader(streamPNG);
	byte[] baBinaryData = new Byte[streamPNG.Length];
	long bytesRead = streamPNG.Read(baBinaryData, 0, (int)streamPNG.Length);

	IsolatedStorageFileStream isfStream = new IsolatedStorageFileStream("temp.png", FileMode.Create, IsolatedStorageFile.GetUserStoreForApplication());
	isfStream.Write(baBinaryData, 0, baBinaryData.Length);
	isfStream.Close();

	//Show to image
	isfStream = new IsolatedStorageFileStream("temp.png", FileMode.Open, IsolatedStorageFile.GetUserStoreForApplication());
	BitmapImage biImage = new BitmapImage();
	biImage.SetSource(isfStream);
	isfStream.Close();
	imgResult.Source = biImage;
}

You can download complete source code here “Windows Phone Sig Capture
Reference: http://www.nickharris.net/2010/03/silverlight-for-mobile-on-windows-phone-7-inkpresenter-fun/

Windows Phone – Audio recorder

Audio recorder is a typical application of a mobile phone. Man can use it to record audio from microphone and use it for his ring phone, store audio note or record evidence of crimes as in Hollywood films, etc… Therefore today I decide to write a small audio recorder which should run on any mobile phone using windows phone OS. The application is very small but helpful. You can get source code in the end of this post.

The core of this application is class Microphone of Microsoft.Xna.Framework.Audio which can be used by referencing to Microsoft.Xna.Framework. By declaring a microphone device, setting some predefined features, then I can handle the event BufferReady to record sound section of 1 second from microphone into an array of byte and append it to memory stream.

Microphone m_micDevice = Microphone.Default;

private void btnStart_Click(object sender, RoutedEventArgs e)
{
	...
	m_micDevice.BufferDuration = TimeSpan.FromMilliseconds(1000);
	m_baBuffer = new byte[m_micDevice.GetSampleSizeInBytes(m_micDevice.BufferDuration)];
	m_micDevice.BufferReady +=new EventHandler(m_Microphone_BufferReady);
	m_micDevice.Start();
}

void m_Microphone_BufferReady(object sender, EventArgs e)
{
	m_micDevice.GetData(m_baBuffer);
	...
	m_msAudio.Write(m_baBuffer,0, m_baBuffer.Length);
}

When the users click on Stop button, they will be asked for saving the memory stream to IsolateStorageFile

if (txtAudio.Text != "")
{
	IsolatedStorageFile isfData = IsolatedStorageFile.GetUserStoreForApplication();
	string strSource = txtAudio.Text;
	int nIndex = 0;
	while (isfData.FileExists(txtAudio.Text))
	{
		strSource = txtAudio.Text + nIndex.ToString().PadLeft(2, '0') + ".wav";
	}

	IsolatedStorageFileStream isfStream = new IsolatedStorageFileStream(txtAudio.Text, FileMode.Create, IsolatedStorageFile.GetUserStoreForApplication());
	isfStream.Write(m_msAudio.ToArray(), 0, m_msAudio.ToArray().Length);
	isfStream.Close();
}

So it’s very simple to write an audio recorder in Windows Phone 7. In my recorder I add some data visualizer to notify the user that the recording is going on. This visualizer uses first 100 value of audio data and shows them in a bar chart. These values will vary continuously and make visualizer animating.

<phoneNavigation:PhoneApplicationPage.Resources>
	<DataTemplate x:Key="template">
		<StackPanel Orientation="Horizontal" VerticalAlignment="Bottom">
			<Rectangle Height="{Binding}" Width="5" Fill="Blue" />
			<Rectangle Width="2" />
		</StackPanel>
	</DataTemplate>
</phoneNavigation:PhoneApplicationPage.Resources>

<ItemsControl x:Name="icBar" ItemsSource="{Binding Path=AudioData}"
	ItemTemplate="{StaticResource template}" Margin="0,6,0,114">
	<ItemsControl.ItemsPanel>
		<ItemsPanelTemplate>
			<StackPanel Orientation="Horizontal"/>
		</ItemsPanelTemplate>
	</ItemsControl.ItemsPanel>
</ItemsControl>

As you can see in order to update record progressing on GUI, I used data binding for ItemsSource property of ItemsControl and update this source each time when the event BufferReady is fired.

void m_Microphone_BufferReady(object sender, EventArgs e)
{
	...
	this.Dispatcher.BeginInvoke(() =>
		{
			vm.LoadAudioData(m_baBuffer);
			...
		}
		);
	...
}

The complete source code of this audio recorder you can download here “Windows Phone Audio Recorder“. If the archive is corrupted, see source code here
http://hintdesk.com/Web/Source/Windows%20Phone%20Audio%20Recorder/

Windows Phone – Passing arguments between pages and Input Scope

On my serie of posts for developing on Windows Phone, today I would like to illustrate how I can pass arguments to page when navigating to it and apply input scope to reduce the input panel for specific input data format. Let’s start with passing argument to page before opening it. If you are Windows Form/WPF developer, you may use a property to get data for form before opening or loading it. This data will be provided to some pre-processing tasks before showing form to user. For example

Form1 f1 = new Form1();
f1.DataToBeSet = "Hihihaha";
f1.Show();

However in Silverlight or Windows Phone, there is no Show() or ShowDialog() function anymore. I can only navigate between page like following.

NavigationService.Navigate(new Uri("/WebPage.xaml", UriKind.Relative));

So then how can I pass argument for page before showing it to users. It is pretty simple, you just need to add some query arguments in Uri and get them back when the new page is loading as following

//On calling Page
NavigationService.Navigate(new Uri("/BrowsePage.xaml?RSSSource="+strSource, UriKind.Relative));
...
//On the BrowsePage side
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
	try
	{
		...
			string strRSSSource = "";
			NavigationContext.QueryString.TryGetValue("RSSSource", out strRSSSource);
			vmDatabase.GetRssItems(strRSSSource);
		...
	}
	catch (Exception ex)
	{
		MessageBox.Show(ex.Message);
	}
}

So you can see how I can pass argument RSSSource to BrowserPage.xaml.

InputScope
The next section in this post I would like to discuss about InputScope and input panel. In windows phone the Software Input Panel can not be customized anymore. Although you can not customize the input panel but you can adjust its view according to input context. For example if you mark a TextBox as requiring a TelephoneNumber you will get a numerical keypad . There are a number of different contexts supported which you can read here http://msdn.microsoft.com/en-us/library/system.windows.input.inputscopenamevalue%28VS.96%29.aspx . Some of typical InputScopeNameValue are listed below
– Password
– TelephoneNumber
– EmailNameOrAddress
– Maps
– NameOrPhoneNumber
– Url

To apply the input context for the text box, you just need to set the InputScope property to one of listed valued in link above to get a matched panel.

<TextBox InputScope="Url" Text="http://vnexpress.net/RSS/GL/trang-chu.rss" x:Name="txtURL" Margin="0,50,0,0" Height="71" VerticalAlignment="Top">

The differences of input panel between with and without setting InputScope you can see in example below.

With InputScope Without InputScope

Update 07.04.2010: Remove some quotations cause of copyright.