It’s a Small World, After All, part 1 – VB, C#, and GPS (Matt Gertz)

It’s a Small World, After All, part 1 – VB, C#, and GPS (Matt Gertz)

Rate This
  • Comments 8

The title of this blog post has sort of a triple meaning for me.  First of all, I just got back from a long (and wonderful!) cruise in the Caribbean, followed by a stop at Disney World.  And of course, if you go to Disney World with kids, it’s the law that you have to go through the “It’s a Small World” ride, thereby having the accompanying song burned into your brain for the next six months.

Secondly, “It’s a Small World” also reminds me of how new technologies are bringing us closer together.  It’s amazing to me that I can do a full presentation in (for example) Asia without having to leaving my office.   For such scenarios to work, a number of different technologies have got to work together, and that’s tricky to get right.  In this blog post, I’m going to be working with both VB and C# and, while they are not really different technologies in the same sense as a phone and a video camera, it’s nevertheless very cool that you can work with both to achieve a cohesive “whole.”

Finally, in the third sense, “It’s a Small World” came to mind while I was working on my latest hobby project (which I’ll detail in this blog) – a GPS program for my Smartphone.  Verizon Wireless recently unlocked the GPS functionality for the Samsung Saga (one of which I own), and since I’ve always been really interested in GPS ever since it was made available to civilians, this was a great opportunity for me to jump in and try my hand at the GPS.  So, in this two-part series, I’ll be walking you through a GPS app which uses both C# and VB to support its functionality.

“Um, isn’t there already a GPS sample for Smartphones?”

Well, yes, and you can get it from the Windows Mobile 6 SDK, which you can download from here.  In fact, that’s where I’m going to start this project.  (This will work on both VS2005 and VS2008, by the way.)

If you download the SDK and run the C# “GPS” sample (not the “Mobile GPS” sample; that’s for C++), you’ll see that it’s pretty simple.  There are two pieces; a class library which wraps the native GPS calls into managed calls, and a mobile application which displays the GPS info.  The UI for the mobile application is very simple – there is a menu item to exit the program, another menu item pair to start and stop the GPS, and a label.  The GPS device generates the events, the UI handles them and appends the text to the label – that’s it.  It’s a good place to start, but it really doesn’t fit a daily usage bill.  I want my GPS program to always display at-a-glance:

·         The state of the GPS

·         The number of satellites in the solution and how many of those are available

·         Latitude and longitude – both in DMS and DM format (since many geocaching sites are given in DM format)

·         My altitude

·         My velocity (speed and direction)

·         A way to specify a target location, and the distance/direction to it.

That’s a lot to cram onto a phone screen – but I managed to fit it in. (I plan on adding more later, particularly a way to mark locations and cache them to use as targets later, but this is a good start.)  Here’s how I went about it:

Creating a new GPS UI

First, open the GPS sample solution (if you haven’t already) and remove the “GpsSample” project.  (You might want to create a copy of the solution first, so that you have the old one for reference.)  We’re going to write a new, better UI that uses VB instead, so add a VB Smart Device Project to the solution, calling it “VBGPS” or something like that.  (Right-click the solution, choose “Add Project,” “Visual Basic,” “Smart Device,” type in “VBGPS” for the name, and press OK.)  In the next dialog, select the “Device Application” project type and press “OK.”

In the new project, you’re going to need to add a reference to the Microsoft.WindowsMobile.Samples.Location class library, so right-click the VBGPS project and choose “Add Reference…”  Navigate to the “Projects” tab and select the aforementioned class library, then press “OK.”  Now, you are hooked up to the C# code which wraps the native GPS functionality.

Double-click “Form1” in the VBGPS” project to bring up the form (if it’s not visible already).  In the property grid, change the FormFactor property to match the form factor of your phone.  (I have a square screen on my phone, so I’ve chosen “Windows Mobile 6 Professional Square.”)  You can also change the Text of the form to something like “VBGPS” so that the title of the window doesn’t say “Form1.”

Now, we’ll start adding controls.  First, we’ll add the menu controls.  These will be the same as the C# version of the sample.  The menu control already exists by default, so click on the left-hand bottom menu and change its text to “Exit” in the property grid – you can also give the control a better name (e.g. “ExitMenuItem”) if you like.  Click on the right-hand bottom menu and change its text to “GPS,”  again changing the control name if you like (e.g. “GPSMenuItem”).  With “GPS” still selected, click the “Type Here” above it and add two menu items – “Start GPS” and “Stop GPS.”  While each is selected, change their control names to something more intuitive in the property grid (e.g. “StartGPSMenuItem” and “StopGPSMenuItem” respectively).

Now, we’ll add the labels and such.  For the following functionality, I’ll drag out two labels – one for the name of the GPS property, and one next to it to contain its value:

·         Status

·         Satellites

·         Lat (i.e., latitude)

·         Long (i.e., longitude)

·         Alt (i.e., altitude)

·         Vel (i.e., velocity)

Status and Satellites should share a line (for a total of four labels on one line); the others have a line each to themselves.  All of them should be set to 8pt font in order to fit on the screen, and the value labels should have meaningful names set for them in the property grid.  (I used “StatusLabel,” “SattLabel,” “LatLabel,” “LongLabel,” “AltLabel,” and “VelLabel.)

We also want to be able to specify a target location.  So, farther down on the form, we’ll drag out a label and change its text to “TargetLat:”.  To the right of it, we add three small text boxes – these will hold degrees, minutes, and seconds.  Finally, to the right of those, we’ll drag out a small combo box.  In the property grid, make sure its Type is “DropDownList,” and in the Items collection, add “N” and “S” as possibilities.  All of these controls should be set to 8pt fonts using the property grid, and all of the text and combo boxes should have meaningful names.  (I chose “LatDegTB,” “LatMinTB,” “LatSecTB,” and LatPosCB.)

Now, we’ll select the entire line of controls and, while holding down the “Control” key, drag a new copy of them below the existing ones.  These will be the longitude target controls, so make the appropriate name and text changes in the copies, and also change the copied combo box items to “E” and “W.”

Finally, we’ll add two more label pairs below that like we did at the beginning.  One will be for “Distance” and the other for “Direction” – make sure that the value labels have good names like “DistLabel” and “DirLabel.”

That’s the UI.  It just barely fits on the screen, and it we want to add any more functionality, we’d have to bring up another window.  But this will get us going, so let’s write some code.

The C# code

First, let’s make a few changes in the class library.  As I mentioned above, we’re going to keep the class library as a C# endeavor.  There’s no reason why it couldn’t be changed to VB code, but since it’s just a bunch of wrappers and they all work pretty well, there’s no need to invest the time to change the code.  However, there are a couple of helper methods that I want to either change or add, and they (will) exist in the DegreesMinutesSeconds helper class defined in DegreesMinutesSeconds.cs.  (We’ll get back to VB code shortly.)

DegreesMinutesSeconds is a class that allows for easy manipulation of latitude and longitude data.  Not only does it store a particular coordinate, but it gives you two ways to interact with the data – as pure degrees (e.g., -82.673529) or as degrees/minutes/seconds (e.g., -82° 40’ 24.7044”).  In this model, North and East are positive values; West and South are negative values.

This is all well and good, but I like to go geocaching, and in geocaching the coordinates are often described in terms of degrees and minutes (e.g., -82° 40.41174’).  I would like to therefore support that format as well.  Currently, the members of this class are:

uint degrees;               /// In VB, this would be Private degrees As UInteger

uint minutes;               /// In VB, this would be Private minutes As UInteger

double seconds;      /// In VB, this would be Private seconds As Double

and each of these have properties associated with them; for example:

        public uint Degrees /// VB: ReadOnly Property Degs() As UInteger

        {

            get { return degrees; } /// VB:  Get: Return degrees: End Get

        }

 

However, we want to be able to express coordinates that have a fractional minute, and so we add the following:

        double dminutes;

        public double DMinutes

        {

            get { return dminutes; }

        }

 

Now we have to use it.  There are already a couple of constructors, and we’ll need to fix them up to set dminutes  -- additions are underlined.  The code is close enough to VB that I won’t bother providing a helpful translation in the blog – just remember that “(uint) x” is equivalent to “CUInt(x)”, etc:

public DegreesMinutesSeconds(double decimalDegrees)

        {

            isPositive = (decimalDegrees > 0);

            degrees = (uint)Math.Abs(decimalDegrees);

            dminutes = (Math.Abs(decimalDegrees) - Math.Abs((double)degrees)) * 60.0;

            minutes = (uint)dminutes;

            seconds = (dminutes - (double)minutes) * 60.0;

        }

 

public DegreesMinutesSeconds(bool isPositive, uint degrees, uint minutes, double seconds)

        {

            this.isPositive = isPositive;

            this.degrees = degrees;

            this.minutes = minutes;

            this.seconds = seconds;

            this.dminutes = minutes + seconds / 60.0;

        }

 

I also need to add an entirely new constructor to take decimal minutes as an argument:

public DegreesMinutesSeconds(bool isPositive, uint degrees, double dminutes)

        {

            this.isPositive = isPositive;

            this.degrees = degrees;

            this.dminutes = dminutes;

            minutes = (uint)dminutes;

            seconds = (dminutes - (double)minutes) * 60.0;

        }

 

So, that’s the “read” changes that we need to make; now, we need to update the “write” routine.  There is a “ToString” routine already, but I don’t like it because it doesn’t use the degree character (°) and it only prints out in DMS format – I want DM as well.  So, I’ll create two new methods:

        public string DMSString()

        {

            return degrees + "° " + minutes + "' " +

seconds.ToString("##0.00") + "\"";

        }

        public string DMString()

        {

            return degrees + "° " + dminutes.ToString("##0.00") + "' ";

        }

 

These are nearly identical to their VB counterparts – replace “+” with “&”, get rid of the semicolons and braces, rearrange the signature, and you’re pretty much there.  Note that we’re making good use of the formatting ability of “ToString” – the format “##0.00” (identical to what we’d use if writing this in VB) means that we only want 2 decimal places, and the we don’t want to see leading zeroes in the tens’ or hundreds’ place.  (We can’t have more than four digits left of the decimal place, since coordinates only go up to 180°.)

Finally, change ToString() to return the DMSString() function instead of the code already there, and we’re done with the C# code changes.  C# code does not compile automatically, so we’ll right-click the class library project and choose “Build” – the class library is now ready for use to use.

Back to VB

It’s time to write the support code for our UI, so right-click on some unused part of the VBGPS form and choose “View Code.” 

We need to add some members.  First, we need to create an object that represents the GPS device.  The class library exposes one, and creating it is pretty easy:

    Dim vbgps As Gps = New Gps() ' The GPS device

 

The GPS device will be sending us events, and we need a way to handle those.  We can use an EventHandler to do this:

    Private updateDataHandler As EventHandler ' Handle GPS events

 

There are two kinds of events – “state” events (is the GPS on, etc.) and “position” events (where are we, how fast as we going, etc.).  We will cache this information whenever it is sent to us for later reference:

    Dim device As GpsDeviceState ' Passed to us by GPS device events

    Dim position As GpsPosition ' Passed to us by GPS device events

 

(This is all identical to the original sample, except translated to VB.)  I also want to be able to cache the target location where I’m heading to, and I can leverage the DegreesMinutesSeconds class for this:

    Dim TargetLatitude As DegreesMinutesSeconds ' Where do we want to go?

    Dim TargetLongitude As DegreesMinutesSeconds ' Where do we want to go?

 

Now let’s start with the menu events, because that’s where everything begins or ends.  These are all simple – we start or stop the GPS device and update a few menu items:

    Private Sub startGpsMenuItem_Click(ByVal sender As Object, ByVal e As EventArgs) Handles StartGPSMenuItem.Click

        If Not vbgps.Opened Then

            vbgps.Open()

        End If

 

        StartGPSMenuItem.Enabled = False

        StopGPSMenuItem.Enabled = True

    End Sub

 

    Private Sub stopGpsMenuItem_Click(ByVal sender As Object, ByVal e As EventArgs) Handles StopGPSMenuItem.Click

        If vbgps.Opened Then

            vbgps.Close()

        End If

 

        StartGPSMenuItem.Enabled = True

        StopGPSMenuItem.Enabled = False

        ResetValues()

    End Sub

 

    Private Sub exitMenuItem_Click(ByVal sender As Object, ByVal e As EventArgs) Handles ExitMenuItem.Click, MyBase.Closed

        If vbgps.Opened Then

            vbgps.Close()

        End If

        Close()

    End Sub

 

Note how the Closed and Exit events are both handled by the same method, which simply shuts down the GPS and exits the program.  The other two methods also have to update the menu items to indicate whether stopping or starting is possible.  (Again, these are identical in the C# versions in the sample.)

Next thing to do is to create the handlers for the GPS events.  These are very trivial, and a straight port from the C# example:

    Protected Sub gps_LocationChanged(ByVal sender As Object, ByVal args As LocationChangedEventArgs)

        position = args.Position

        Invoke(updateDataHandler)

    End Sub

 

    Protected Sub gps_DeviceStateChanged(ByVal sender As Object, ByVal args As DeviceStateChangedEventArgs)

        device = args.DeviceState

        Invoke(updateDataHandler)

    End Sub

 

Basically, we’re just caching the information that the GPS device is sending us, and then invoking our overall handler (which we defined above) to deal with the information.  We could call a helper method directly instead of using Invoke, but this gives us more wiggle room – we might want to re-route the events elsewhere to a different handler in certain situations, and this setup allows us the leeway to do that programmatically rather than hard-coded.

Of course, I haven’t turned on the handlers yet.  I do that in the Form1_Load handler (which you can add by double-clicking on an empty area on the form).  The relevant lines are:

        AddHandler vbgps.DeviceStateChanged, New DeviceStateChangedEventHandler(AddressOf gps_DeviceStateChanged)

        AddHandler vbgps.LocationChanged, New LocationChangedEventHandler(AddressOf gps_LocationChanged)

 

Now the GPS events will be received.  I also need to define the updateDataHandler – I’ll point it to a function called UpdateData (which I haven’t written yet)

        updateDataHandler = New EventHandler(AddressOf UpdateData)

 

While I’m in Form1_Load, I might as well initialize the various fields and target data:

        ResetValues()

        StopGPSMenuItem.Enabled = False ' The C# sample forgets to do this

 

        ' Zero out the text boxes and set the target to 0 degrees N and E.

        Me.LatDegTB.Text = "0"

        Me.LatMinTB.Text = "0"

        Me.LatSecTB.Text = "0"

        Me.LongDegTB.Text = "0"

        Me.LongMinTB.Text = "0"

        Me.LongSecTB.Text = "0"

        Me.LatPosCB.SelectedIndex = 0

        Me.LongPosCB.SelectedIndex = 0

        TargetLatitude = New DegreesMinutesSeconds(False, 0, 0, 0.0)

        TargetLongitude = New DegreesMinutesSeconds(False, 0, 0, 0.0)

 

ResetValues() is a function which does initialization that I might have to do more than once – for example, when stopping the GPS, I need to reset the feedback to the user to indicate that no data is available.  It looks like this:

    Private Sub ResetValues()

        Me.Text = "VBGPS"

        Me.StatusLabel.Text = "Off"

        Me.AltLabel.Text = "---"

        Me.VelLabel.Text = "---"

        Me.LatLabel.Text = "---"

        Me.LongLabel.Text = "---"

        Me.SatLabel.Text = "Unavailable"

        Me.DistLabel.Text = "---"

        Me.DirLabel.Text = "---"

    End Sub

 

That’s it for this post.  In tomorrow’s concluding post, I’ll be adding all of the “meat” of the functionality (updating fields, calculating paths and distances) – all using VB, of course!

‘Til next time,

  --Matt--*

Leave a Comment
  • Please add 5 and 5 and type the answer here:
  • Post
  • Will i still be able to view and compile C# and VB2008 code together, even though i have only the express editions?

  • You can do it, but it's slightly more complicated.  In Express, you'd need to compile the C# components in C# Express, and then use the results as assembly references to your project in VB Express.

    --Matt--*

  • (And to answer the overall question -- you won't be able to have both VB and C# projects open in the same shell.)

    --Matt--*

  • Thanks! Oh, and as a blogging idea, i think something that would be good would be a sorta outlook-ish program. Importing and exporting email? And not just on the gmail smtp server, either...all of em, cause i've tried it before and the other servers are pesky and will deny you :)

  • I'll give it some thought.  I'm actually in the middle of porting my Euchre game to WPF, and so that's likely to be my next topic, assuming that I can get everything working -- there's definitely some learning curves to surmount.

    --Matt--*

  • you provide code samples will work on my project, I hope thanks

  • so many things you'd always prefer vbteam won ...

  • So....is there anything put together to make this do something...or am I missing something?

    Dana

Page 1 of 1 (8 items)