Click here to Skip to main content
15,881,757 members
Articles / Desktop Programming / WPF
Article

The Code Secrets behind My Health Assistant

28 May 2014CPOL12 min read 22.6K   15   2
My Health Assistant is an app that helps individuals manage and track their medication use through a simple interface. The app also features a health diary, a GPS-based pharmacy and ER locator, and personal health information.

This article is in the Product Showcase section for our sponsors at CodeProject. These articles are intended to provide you with information on products and services that we consider useful and of value to developers.

Many people take medication, sometimes multiple times per day, to help them stay healthy. Making sure meds are taken on time and in the right doses requires an individual to be vigilant and disciplined. Software developer Tim Corey saw a way to improve the error-prone process of tracking self-medication by using technology to provide an easy-to-use personal medication assistant, one that never forgot a dose and had a perfect memory. This idea led to his creation of My Health Assistant.

My Health Assistant is an app that helps individuals manage and track their medication use through a simple interface. The app also features a health diary, a GPS-based pharmacy and ER locator, and personal health information.

Image 1

Figure 1: The main menu of My Health Assistant showing the touch UI.

Corey developed My Health Assistant as an entry in the Intel® App Innovation Contest 2013 hosted by CodeProject in partnership with the Intel® Developer Zone, and the app went on to win in the Health Category. The app was initially built for Microsoft Windows* desktop, with the ultimate target of Windows-based tablets, such as the Lenovo ThinkPad* Tablet 2, Ultrabook™ 2 in 1s running Windows 8.1*, Windows Phone* 8, and other mobile platforms.

Corey’s central development goals were portability, usability, and security. To reach these goals, he had to overcome a number of challenges throughout development, including implementing cross-platform touch UI and securing sensitive medical data. This case study explores those challenges, the solutions Corey applied, and the resources he used.

Decisions and Challenges

Corey took a modular approach to building the app, working on each piece of functionality separately in C# and wiring them together with the XAML Windows Presentation Foundation (WPF) UI using the Model View ViewModel (MVVM) design pattern.

Choosing C#

Prior to making My Health Assistant, Corey had considerable experience working with the object-oriented .NET programming language C#. He chose C# as the main language for building the app (using Microsoft Visual Studio*) for a number of reasons, including Microsoft’s support behind it, which brings an entire ecosystem of tools, libraries, and other resources.

As a key approach to creating a cross-platform app, C# can also be taken into practically any environment: from PC, Linux*, or Apple Mac* to Apple iPhone* or Google Android*. An additional strength of C# is that security and encryption are incorporated deeply in the code, removing what otherwise could be a big hurdle, especially when dealing with sensitive data such as medical records.

Corey considered other language options such as VB.NET, another .NET language, and also Java*. However, in Corey’s opinion, none of them offered the same combination of familiarity and features that C# was able to provide.

C# Libraries

The Microsoft ecosystem around C# includes a large number of optimized libraries that Corey believes greatly simplify the coding process. In My Health Assistant, the app’s data is stored in XML files for ease of cross-platform portability, and because of the way the libraries have been incorporated into the programming framework, Corey was able to write one simple line of code and have all of that data taken care of.

In addition to the Microsoft libraries, many third-party libraries are available for C#. For the UI framework of My Health Assistant, Corey used the Caliburn.Micro, which enabled him to connect the app’s front- and back-end code using MVVM. This approach allowed flexibility when editing the UI, removing the need for comprehensive recoding after making any modifications.

WPF UI

To build the UI, Corey chose the Microsoft WPF system over Windows Forms because of its responsiveness to screen size changes, a vital ingredient of cross-platform development. With Windows 8 desktop and Windows Phone 8 both using WPF, Corey was quickly able to produce different versions of the app for each platform without major UI recoding.

In practice, the responsive WPF UI serves up elements in a particular number and size according to the available screen real estate. A full desktop view will display the full complement of buttons, whereas a mobile view will show only one or two with the others moved to a drop-down menu.

Overcoming Touch and Scrolling Problems

Any app for portable devices, whether for smartphones, tablets, or Ultrabook devices, needs effective touch controls. While the desktop app works well with a mouse, Corey specifically designed it for touch, ensuring that simple actions such as scrolling through a menu worked well with a single finger. He even disabled the scroll bar to encourage finger scrolling.

The biggest hurdle that Corey faced during development was implementing the menu scrolling in the touch UI. The app needed to be told precisely the screen orientation and the size of the available screen real estate for menus and other elements; otherwise the app would assume that more space was available, rendering key elements such as menu buttons invisible and hence useless.

To enable touch scrolling in WPF, Corey added an attribute to the ScrollViewer that indicates the PanningMode, as in the code snippet below.

<ScrollViewer Grid.Row="2" HorizontalScrollBarVisibility="Disabled" 
              VerticalScrollBarVisibility="Hidden" HorizontalAlignment="Center"
              PanningMode="VerticalOnly" Margin="0,0,0,0">

GPS Locator

One main feature of My Health Assistant is its ability to help users find the closest pharmacy or emergency room wherever they are. This feature uses the device’s GPS functionality and the Google Maps* API combined with relevant location data pulled in through an API to serve up accurate and relevant information on maps.

Image 2

Figure 2: Google Maps* API integration lets users easily locate their nearest pharmacy or ER.

The code below is the class that holds the GPS code, which is responsible for acquiring the coordinates and raising an event once they have been located. It is an asynchronous transaction, which means that the app continues running normally while the coordinates are located.

public class GPSLocator
{
    public GeoCoordinateWatcher _geolocator { get; set; }

    public GPSLocator()
    {
        // Initializes the class when this class is loaded
        _geolocator = new GeoCoordinateWatcher();
    }

    // Asynchronously loads the current location into the private variables and
    // then alerts the user by raising an event
    public void LoadLocation()
    {
        try
        {
            _geolocator = new GeoCoordinateWatcher(GeoPositionAccuracy.Default);
        }
        catch (Exception)
        {
        }
    }
}

The following section of code calls the GPSLocator class and serves up the coordinates asynchronously. This code also provides the option of continuously acquiring new GPS coordinates, but, in the case of My Health Assistant, Corey assumed that the user would be stationary and hence would need only one set of coordinates. However, the GPS service could be left running to provide continually updated coordinates.

// Initializes the GPS
gps = new GPSLocator();

// Loads the watcher into the public property
gps.LoadLocation();

// Wires up the code that will be fired when the GPS coordinates are found.
// Finding the coordinates takes a couple seconds, so even though this code
// is here, it won't get fired right away. Instead, it will happen at the end
// of the process.
gps._geolocator.PositionChanged += (sensor, changed) =>
{
    // This code uses an internal structure to save the coordinates
    currentPosition = new Position();
    currentPosition.Latitude = changed.Position.Location.Latitude;
    currentPosition.Longitude = changed.Position.Location.Longitude;

    // This notifies my front-end that a couple of my properties have changed
    NotifyOfPropertyChange(() => CurrentLatitude);
    NotifyOfPropertyChange(() => CurrentLongitude);

    // A check is fired here to be sure that the position is correct (not zero).
    // If it is correct, we stop the GPS service (since it will continue to give
    // us new GPS coordinates as it finds them, which isn't what we need). If
    // the latitude or longitude are zero, we keep the GPS service running until
    // we find the correct location.
    if (currentPosition.Latitude != 0 && currentPosition.Longitude != 0)
    {
        gps._geolocator.Stop();
        LoadPharmacies(); 
    }
};

// This is where we actually kick off the locator. If you do not run this line,
// nothing will happen.
gps._geolocator.Start();

API Integration

For serving up local information on pharmacies and hospitals, Corey knew that choosing the right API was critical, because My Health Assistant had to be able to provide accurate information anywhere in the world, not just the United States. Corey considered several potentially good APIs, including the Walgreens and GoodRx APIs, but had to discount them because they didn’t work outside the United States. Corey ultimately chose the Factual Global Places API database, which contains global information. He was able to test its effectiveness while at a conference in Spain: a request for the nearest pharmacies produced a list of places within a couple of miles of his location.

Image 3

Figure 3: Users can store their personal doctor, pharmacy, and insurance information in the app.

Security Choices

Alongside portability, Corey cites security as the second key pillar of the application. The app’s default setting is to store data locally, which presents a relatively low security risk. However, during testing Corey found that users wanted the ability to access the stored data when using the app on different devices, which implied a cloud-based data storage solution and hence increased risk.

For cloud backup of the XML data files, rather than implement a complex API-driven solution into the app itself, Corey took the simpler route of adding cloud-based save options into the File Explorer view alongside local options. This approach made data backup intuitive while relying on encryption and the user’s own trust in the service of their choice for the security of the data, whether that be Microsoft SkyDrive*, Dropbox*, Box.net, or another service. The screen shot below shows how the cloud-storage save options appear in File Explorer view.

Image 4

Figure 4: Users can back up data locally or directly to their chosen cloud service using File Explorer.

Saving Data

Initially, Corey had difficulties with the backup functionality, having started down the path of implementing a complex storage and retrieval mechanism for the XML files. However, a friend gave him a simple yet powerful piece of code, which immediately solved the problem.

All of the app’s data is saved to XML files that are then loaded at runtime to repopulate the application with the data. Below is the code that actually saves the data.

// This method saves the data to a XML file from a class instance that
// has been passed into this method. It uses generics (the T
// that is all over in here) so that any type can be used to pass
// data into this method to be saved.
public static void SaveData<T>(string filePath, T data)
{
    // Wraps the FileStream call in a using statement in order to ensure that the
    // resources used will be closed properly when they are no longer needed.
    // This file is created in read/write mode.
    using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite))
    {
        // This uses the XmlSerializer to convert our data into XML format
        XmlSerializer xs = new XmlSerializer(typeof(T));
        xs.Serialize(fs, data);
        fs.Close();
    }
}

Below is the code that subsequently loads the data.

// This method loads the data from a XML file and converts it back into
// an instance of the passed in class or object. It uses generics (the T
// that is all over in here) so that any type can be passed in as the
// type to load.
public static T LoadData<T>(string filePath)
{
    // This is what we are going to return. We initialize it to the default
    // value for T, which as a class is null. This way we can return the
    // output even if the file does not exist and we do not load anything.
    T output = default(T);

    // Checks first to be sure that the file exists. If not, don't do anything.
    if (File.Exists(filePath))
    {
        // Wraps the FileStream call in a using statement in order to ensure that the
        // resources used will be closed properly when they are no longer needed.
        // This opens the file in read-only mode.
        using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
        {
            // This uses the XmlSerializer to convert the XML document back into
            // the class format.
            XmlSerializer xs = new XmlSerializer(typeof(T));
            output = (T)xs.Deserialize(fs);
            fs.Close();
        }
    }

    // Returns the class instance, loaded with the data if the file was found
    return output;
}

The way the code works in the app is simple. First, the following command is used to save data stored in a class to an XML file on disk.

_records = Data.LoadData<RecordsPanelViewModel>(filePath);

The command below is used to load the data back into the class from the XML file, for example, when the application is launched and it needs to bring the data back in.

Data.SaveData<RecordsPanelViewModel>(FilePath, this);

Testing

Corey’s first line of attack when testing the app during the early stages of development was to come up with as many bad inputs as he could think of. At various stages he handed the app to several friends and family members, which proved to be an effective way to identify issues from an unbiased user perspective and refine the UI and overall usability.

The modular development approach made the process of rearranging the UI particularly quick and straightforward, allowing rapid iteration in response to feedback Corey received.

A number of UI bugs caused problems, particularly with the scrolling, which initially didn’t work in the way that Corey had expected. Another bug he fixed occurred with the medication dosage counter. For example, a 6-hour countdown for one medication would then cause the countdown for the subsequent medication to begin at 5 hours 59 minutes instead of 6 hours.

Corey describes the debugging process as rough and long-winded, in contrast to the more satisfying process of actually building the app itself, but he has yet to encounter an issue for which he couldn’t find a solution.

Next Steps

At the time of this writing, Corey is targeting a summer 2014 release for My Health Assistant. The initial launch will be for Windows 8 Desktop, followed by Windows Phone 8 and then other mobile platforms, including iOS and Android. Post-launch, Corey looks forward to gathering user feedback and using that feedback to iterate and improve the app over time.

Another feature that Corey is investigating is the integration of a medication lookup API to provide users with information about particular pharmaceutical drugs and where to find them at the best prices. GoodRx is an example of an API that provides this within the United States, but the search is ongoing for a solution that is effective worldwide.

Conclusion

Knowledge Growth

While Corey had worked with XAML prior to My Health Assistant, most of it was on a much simpler level. Working on the app allowed him to significantly grow his XAML knowledge and learn how to design and build better apps for the future.

In addition to XAML, Corey also greatly expanded his working knowledge of Caliburn.Micro, the framework that Rob Eisenberg created for WPF and XAML. Despite a relatively steep learning curve, Corey considers the knowledge he has gained invaluable in terms of how to make things work within the disconnected framework environment.

Key Learnings

When giving advice to his software development students, Corey emphasizes the need for good planning—an approach to development that has been reinforced through his work on My Health Assistant. The experience taught him that more time spent in the design phase means less in development and debugging.

During the design process, which involved a lot of iterating on paper, Corey frequently threw things out. Casting aside ideas and features would have been more difficult to do in the development stage resulting in wasted time. Iterating during the preliminary design phase proved to be much more efficient.

Corey also learned the danger of making technical assumptions about how things would work and not testing them prior to coding them into the app. A number of times during development, Corey found that certain things, such as scrolling, didn’t behave in the way he expected, resulting in having to discard the code. Corey recommends building small applications throughout the development phase to test assumptions regarding specific functionality and behavior.

About the Developer

Tim Corey began his career as a software developer and IT professional in the late 90s, with roles as a programmer and IT director. In 2011 Corey graduated from South University with a bachelor’s degree in IT and Database Administration. Subsequent roles have included lead programming analyst for an insurance group. Corey is currently the lead technical consultant at Epicross consulting firm, his own business, which aims to help organizations achieve greater IT efficiency through the optimization of their existing technology. He also teaches software development.

Helpful Resources

Corey relies heavily on a variety of external resources to find solutions to problems. Chief among these is Pluralsight, which offers subscription-based video training. Corey often watches 3 or 4 hours of video at a time when he’s learning a new topic or working to improve existing skills.

For specific problems, Corey often visits CodeProject and searches for answers among its vast collection of articles, in its tips and tricks section, or by asking a question on the forums. Corey’s other go-to resource is Stack Overflow, which he considers to be the Wikipedia of software developers, and where almost any question imaginable has already been answered, often multiple times.

Corey occasionally uses Microsoft documentation, and also regularly turns to Twitter for support. He either throws questions out to his Twitter followers or directs queries to specific individuals, such as Rob Eisenberg, whom Corey cites as a particularly big help when called upon.

Intel® Developer Zone offers tools and how-to information for cross-platform app development, platform and technology information, code samples, and peer expertise to help developers innovate and succeed. Join our communities for the Internet of Things, Android, Intel® RealSense™ Technology and Windows to download tools, access dev kits, share ideas with like-minded developers, and participate in hackathons, contests, roadshows, and local events.

Related articles:

Intel and the Intel logo are trademarks of Intel Corporation in the U.S. and/or other countries.
*Other names and brands may be claimed as the property of others.
Copyright © 2014. Intel Corporation. All rights reserved.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionReally a great job Pin
HardikPatel.SE31-May-14 18:20
professionalHardikPatel.SE31-May-14 18:20 
QuestionGood work! Pin
Volynsky Alex28-May-14 9:48
professionalVolynsky Alex28-May-14 9:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.