One of the great things about Windows Presentation Foundation (WPF) is that it uses Extensible Application Markup Language (XAML). XAML is an XML based language that graphic artists can easily create using Microsoft Expression Blend. A programmer can then write the program logic in any .NET language such as C# or Visual Basic.
A big advantage to this separation beyond separation of tasks during initial development is that a non-programmer can change what the application looks like without knowing a programming language; only the XAML needs to be modified.
I built a simple test application in WPF using Visual Studio 2008 that reads data from a GPS and displays the position in latitude and longitude. Also displayed is the date and time which is updated from a timer. A screen shot of the application appears below:
This solution is rather simple and its structure is shown below. The two items of interest are Window1.xaml and NMEAParse.cs As an aside, Window1.xaml.cs is just as generated by Visual Studio, it contains no additional code.
Here is Window1.xaml:
___________________________________________________________
<Window x:Class="NMEATest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:NMEATest="clr-namespace:NMEATest"
Title="Window1" Height="300" Width="300">
<Grid>
<StackPanel Orientation="Vertical">
<TextBox Height="34" Width="Auto" Text="{Binding Mode=OneWay, Path=FormattedDateTime}"
TextWrapping="Wrap" x:Name="DispValue" Foreground="#FFE61919" FontSize="18">
<TextBox.DataContext>
<NMEATest:UpdatingDtTm/>
</TextBox.DataContext>
</TextBox>
<TextBox x:Name="lat" Text="{Binding Mode=OneWay, Path=GPSPosition}">
<TextBox.DataContext>
<NMEATest:UpdateGPS/>
</TextBox.DataContext>
</TextBox>
</StackPanel>
</Grid>
</Window>
________________________________________________________
This interface is rather simple, consisting primarily of two text boxes, one for the date-time and the other for the position. The key to getting the data into these textboxes is data binding. In this case the binding mode is set to OneWay since we are only displaying data, not updating it. The Path specifies the variable name being referenced (FormattedDateTime & GPSPosition) while the DataContext specifies the namespace and class name (NMEATest:UpdatingDtTm & NMEATest:UpdateGPS).
Below is NMEAParse.cs, the C# code that provides the data that is displayed in the XAML. Since this code is in a different class, it will run on a separate thread and thus the user interface will remain responsive even if the c# code is heavily loaded.
The key to implementing the data binding is to implement the
INotifyPropertyChanged interface, providing a PropertyChangedEventHandler and then calling PropertyChanged when there is new data to be displayed.
________________________________________________________
using System;
using System.IO.Ports;
using System.ComponentModel;
using System.Windows.Threading;
namespace NMEATest
{
public class UpdateGPS : INotifyPropertyChanged
{
private string _GPSPosition = "lost";
private bool bPortOpen = false;
private SerialPort port;
private string serBuff = "";
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public string GPSPosition
{
get { return _GPSPosition; }
}
public UpdateGPS()
{
port = new SerialPort("COM5", 4800);
port.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
port.Open();
bPortOpen = true;
//port.Close();
}
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
bool newData = false;
serBuff = serBuff + port.ReadExisting();
while (serBuff.Contains("\r\n"))
{
int i = serBuff.IndexOf("\r\n");
string line = serBuff.Substring(0,i);
if (‘$’ == line[0])
{
string[] tok = line.Split(‘,’);
switch (tok[0])
{
case "$GPAPB": // Auto Pilot B sentence
break;
case "$GPBOD": // Bearing Origin to Destination
break;
case "$GPBWC": // Bearing using Great Circle route
break;
case "$GPGGA": // Fix information
if (tok[1].Length > 0)
{
_GPSPosition =
tok[2].Substring(0,2) + " " + tok[2].Substring(2, tok[2].Length-2) + " " +tok[3] +
" – " +
tok[4].Substring(0,3) + " " + tok[4].Substring(3, tok[4].Length-3) +
" " + tok[5];
}
else
{
_GPSPosition = line;
}
newData = true;
break;
case "$GPGLL": // Lat/Lon data
break;
case "$GPGSA": // Overall Satellite data
break;
case "$GPGSV": // Detailed Satellite data
//_GPSPosition = line;
//newData = true;
break;
case "$GPRMB": // recommended navigation data for gps
break;
case "$GPRMC": // recommended minimum data for gps
break;
case "$GPRTE": // route message
break;
case "$GPVTG": // Vector track an Speed over the Ground
break;
case "$GPXTE": // measured cross track error
break;
case "$PGRME":
break;
case "$PGRMM":
break;
case "$PGRMZ":
break;
default:
//string un = line;
//Console.WriteLine(un);
break;
}
}
serBuff = serBuff.Substring(i+2);
}
//_GPSPosition = serBuff;
if (newData)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("GPSPosition"));
}
}
}
}
public class UpdatingDtTm : INotifyPropertyChanged
{
private string _someText = "Foo";
private DispatcherTimer tmr = new DispatcherTimer();
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public string FormattedDateTime
{
get { return _someText; }
}
public UpdatingDtTm()
{
tmr.Interval = TimeSpan.FromMilliseconds(10);
tmr.Tick += new EventHandler(tmr_Tick);
tmr.Start();
}
void tmr_Tick(object sender, EventArgs e)
{
_someText = DateTime.Now.ToString();
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("FormattedDateTime"));
}
}
}
_________________________________________________________________
If you are not familiar with WPF development, you may be surprised if you open this project in Visual Studio. The design interface will update the data in real-time in design mode; even before you run the program! This is to be expected.
One other thing, I used a Microsoft GPS puck on a USB port for testing. This is the one that ships with some versions of Streets & Trips. On my computer it installed on Comm port 5 and runs on 4800 baud. If your GPS uses a different port or speed, you will need to change the line that opens the serial port. If you don’t have a GPS don’t worry, the date and time will still update.