WPF + MVVM – a quick little status app (2 of 2)

This is part of a series – see this post for explanation of the context: http://robseder.wordpress.com/2011/01/16/wpf-mvvm-a-quick-little-status-app-1-of-2/

—–

Part 1 covered the actual app in WPF and using the MVVM. In this second post, I wanted to cover some of what I learned for the 3 pieces of functionality of this app: getting the current weather, reading my Twitter home "timeline", and checking for e-mail.

Get the weather from NOAA:
I wanted to show the current weather. So after a quick google on bing, I see that many people are using the web services from NOAA (National Oceanic and Atmospheric Administration). Seems like a good place to go to, to get the weather!

Well, the path I followed was actually to get the forecast, for a specific point, for today/tonight – which isn’t what I really wanted. I just wanted "current conditions". So first, if you want to include forecast information, this may help:

WSDL: http://www.weather.gov/forecasts/xml/SOAP_server/ndfdXMLserver.php?wsdl

I created a NON-WCF proxy because when I create a WCF proxy, I got encoding errors. The service gives us like ISO-1206-1 or something, and the client was expecting UTF-8. When I changed the proxy to use ISO-1206-1, I got an error. It turns out the basicHttpBinding only supports UTF8 and UTF16. So, out of laziness, I created an old-school proxy and that worked fine – I called the reference "NoaaWeatherServiceReference" – and here is some working code to get today’s forecast (oh, also here is the XSD schema for what is returned from the service which hopefully helps explain the XPath statements):

    using (ndfdXML client = new ndfdXML())

    {

        // Start with a zip code

        string latLong = client.LatLonListZipCode("06084");

 

        // Load the result into an XML Document

        XmlDocument document = new XmlDocument();

        document.LoadXml(latLong);

 

        // Go get the latitude and longitude for this ZIP code

        latLong =

            document.SelectSingleNode("dwml/latLonList").InnerText;

 

        string[] latLongParts = latLong.Split(‘,’);

        if (latLongParts.GetUpperBound(0) == 1)

        {

            // We found a valid lat/long – so extract them

            decimal latitude = decimal.Parse(latLongParts[0]);

            decimal longitude = decimal.Parse(latLongParts[1]);

 

            weatherParametersType parameters =

                new weatherParametersType();

 

            // We are requesting the "at a glance" produce, for

            // this lat/long, for right now

            string response =

                client.NDFDgen(latitude, longitude, "glance",

                    DateTime.Now, DateTime.Now, parameters);

 

            // The results are a XML document that has the details

            document = new XmlDocument();

            document.LoadXml(response);

 

            // Extract all the pieces that I need

            string creditLink =

                document.SelectSingleNode("dwml/head/source/credit").InnerText;

            string creditLogo =

                document.SelectSingleNode("dwml/head/source/credit-logo").InnerText;

            string temperature =

                document.SelectSingleNode("dwml/data/parameters/temperature/value").InnerText;

            string cloudiness =

                document.SelectSingleNode("dwml/data/parameters/cloud-amount/value").InnerText;

            string conditionsLogo =

                document.SelectSingleNode("dwml/data/parameters/conditions-icon/icon-link").InnerText;

 

            // Take their little 47×47 image/icon (like sunny,

            // rainy, mostly cloudy, etc) and show it

            this.image1.Source =

                new System.Windows.Media.Imaging.BitmapImage(

                    new Uri(conditionsLogo));

 

            // Show the temperature and add the "degree" symbol

            // after it – type: ALT+176

            this.tempLabel.Content = temperature + "°";

 

            // They include "cloudiness", which I assume to

            // a percentage.

            int cloudLevel = int.Parse(cloudiness);

 

            if (cloudLevel < 25)

            {

                this.cloudLabel.Content = "Clear";

                return;

            }

            else if (cloudLevel < 50)

            {

                this.cloudLabel.Content = "Mostly Sunny";

                return;

            }

            else if (cloudLevel < 75)

            {

                this.cloudLabel.Content = "Partly Sunny";

                return;

            }

            else

            {

                this.cloudLabel.Content = "Cloudy";

                return;

            }

        }

    }

Now, with that said, this isn’t actually what I needed. Also, as you can probably tell from above, I’m not doing an MVVM approach just yet either, I’m directly assigning variables to controls.

If I want current conditions, what should I use? *shrugs* I dunno, but NOAA does offer an RSS/XML feel for current conditions, for local airports – which is good enough for my needs:

http://www.weather.gov/xml/current_obs/

so as I wrap up the work on this little app, I’m going to create a Model, and then a ViewModel that consumes the XML for my local airport (probably BDL). Then (per Part 1 of this blog series), I will "bind" my UI elements to that model, so that the UI updates automagically.

Using TweetSharp to get Tweets:
First, I’m not going to get all high-and-mighty here and I’m going to try to bite my tongue about TweetSharp – but seriously, this very frustrating. Why use it? Because it’s the de facto standard for connecting to Twitter from .NET, and everyone in the world uses it. With that said though, to know me is to know how I have many issues with open source software. Most notably is my quote of: "There is a big difference between writing an application and releasing a product". TweetSharp is a textbook example of this. Some developers had some fun engineering (over-engineering) the API, then walked away when it was time to document it. Frustrating.

Right off the bat, it’s not entirely their fault. Before last year, Twitter used to have a very easy API. You passed in the username/password of the user and then you could interact with Twitter. This required that applications have users’ credentials though, which is not ideal.

So Twitter changed to an OAuth authentication model. This is where the application gets a "request token" from Twitter, sends you over to Twitter (via browser) to "allow the application", you go back in the application and input the authorization code – and the application then obtains an AccessToken from Twitter that it uses from then on.

This way, the application can get at some of your data, without you giving them your credentials, and you always have complete control. You can revoke that access at any time from: http://twitter.com/settings/connections

This is a much better, much more secure way of doing things.

So what went wrong? Two things, really. First is documentation. There is virtually none. The limited sample code that is there, simply doesn’t work! More importantly though is there are somewhat complicated concepts where I need to know how the API is working. For example, for this OAuth piece – and there are simply a few paragraphs written up on it. There is also NO component documentation and NO intellisense (descriptions and explanations) – very frustrating.

image

The second issue is that the exceptions have very little detail and don’t seem to be designed very logically. I was getting something like a TwitterSendException when I was having problems with OAuth. I looked just now and via intellisense I can’t find a single Exception – and since there is no component documentation, I can’t tell you what the actual exception was.

I found using this API to be immensely frustrating, for avoidable reasons. This just further underscores with me, the importance of user and component documentation when you release a "product". This also re-motivates me to finish working on my own .NET implementation of the Twitter API.

Anyhow, knowing what I know now, with the sample code below, I could’ve had this up and running in :15 minutes.

So – how DO you use the new OAuth model with TweetSharp?

Well, first let’s start with dealing with the tokens. Here’s basically how this works (this took DAYS to glean, from many many sources, by the way):

  • Setting up Twitter
  • You need to register you app with http://dev.twitter.com – they give you a ConsumerKey and ConsumerSecret (this is what "clientInfo" is, below)
  • New User
    • Call TwitterService.GetRequestToken()
    • Call TwitterService.GetAuthorizationUri(requestToken)
    • Pop open a new browser that shows the user an application authorization screen. They can click ‘Allow’ or ‘Deny’. If they click Allow, they are given a verifier ID (a 6-7 digit number)
    • Call TwitterService.GetAccessToken(requestToken, verifier)
    • Persist the 4 properties from this access token (token, secret, username, userid)
  • Existing User
    • Retrieve the 4 properties for the access token, create a new instance of OAuthAccessToken, assign the properties

    I have that functionality in a function called GetOAuthAccessToken – then here is my main logic for initializing and updating tweets:

        public void LoadLatestTweets()

        {

            TwitterService service = new TwitterService(clientInfo);

     

            // GetOAuthAccessToken() figures out if the current user authorized

            // this app. If it didn’t, they it does "Step 2" and "Step 3" from

            // the sample code. That is, GetAuthorizationUri, pop browser, then

            // take the results of the requestToken and verifier and get an access token.

            //

            // If the current user HAS authorized the app then create a new

            // instance of OAuthAccessToken and populate the 4 properties

            // with the values that were retrieved when the user DID authorize the app.

            OAuthAccessToken accessToken = GetOAuthAccessToken(service);

     

            if (accessToken == null)

            {

                // User was prompted to authenticate and chose cancel

                return;

            }

     

            service.AuthenticateWith(clientInfo.ConsumerKey, clientInfo.ConsumerSecret,

                accessToken.Token, accessToken.TokenSecret);

     

            IEnumerable<TwitterStatus> statuses;

     

            if (!initiallyLoaded)

            {

                // We are initially loading the app, get the last 50 tweets

                statuses = service.ListTweetsOnHomeTimeline(50);

                initiallyLoaded = true;

            }

            else

            {

                // This is a subsequent call, get all the tweets since the

                // last one we retrieved.

                statuses = service.ListTweetsOnHomeTimelineSince(lastTweet);

            }

     

            // Doing an order by so that lastTweet will be the last chronological

            // tweet. Otherwise, the service returns the data with the oldest

            // stuff first. In this case, _tweets is an order ObservableCollection

            // so the sort doesn’t otherwise matter here.

            foreach (TwitterStatus status in statuses.OrderBy(tweet => tweet.CreatedDate.ToLocalTime()))

            {

                _tweets.Add(new Tweet

                {

                    From = status.User.Name,

                    FromImage = new Uri(status.User.ProfileImageUrl),

                    Text = status.Text,

                    TweetDate = status.CreatedDate.ToLocalTime()

                });

                lastTweet = status.Id;

            }

     

        } 

    That’s basically it. I am still trying to figure out how to elegantly show a WPF-style modal dialog to prompt for that "Verifier", but I don’t quite have that working just yet.

    Using CSharpMail for using POP3 to get mail:
    Lastly is the mail component. I tried a few implementations and ended up using C#Mail. This too had very, very little documentation (haha!). However, it was easy enough to use and work with. In fact, here’s all the relevant code:

        public void CheckForNewMail()

        {

            using (Pop3Client client = new Pop3Client())

            {

                client.ServerName = "pop3.live.com";

                client.Port = 995;

                client.Ssl = true;

                client.UserName = "awesome-duhveloper@hotmail.com";

                client.Password = "SuperSecretPass11!";

                client.Authenticate();

     

     

                List<List.Result> items = client.ExecuteList();

     

                foreach (List.Result item in items)

                {

                    Pop3Message message = client.ExecuteTop(item.MailIndex, 10);

                    if (!CheckForExistingMail(item.MailIndex))

                    {

                        mailItems.Add(new MailItem()

                        {

                            From = message.From,

                            Body = message.BodyText,

                            Subject = message.Subject,

                            ItemDate = message.Date.ToLocalTime(),

                            MailId = item.MailIndex

                        });

                    }

                }

            }

        }

     

        private bool CheckForExistingMail(long mailId)

        {

            foreach (MailItem item in mailItems)

            {

                if (item.MailId.Equals(mailId))

                    return true;

            }

            return false;

        } 

    This too uses a SortedObservableCollection (that is what "mailItems" is) so that it automatically sorts by latest-first.

    One caveat here, I let this application run for a little while, checking e-mail every :15 minutes. As you can see, I create and close a connection every time I connect. For some reason, when I connected, I would NOT pick up some new e-mails.

    This may be a problem with the API, but I more suspect hotmail. I say that because in the past several days, my Droid X phone stopped "notifying" me of new e-mails and when I explicitly when to check, it would say there are no new messages. My phone uses POP3 too. Today though, both the phone and the app seem to be back to normal.

    ObservableCollection vs SortedObservableCollection:
    Lastly, a nice little class to use with MVVM is ObservableCollection. This supports the idea of notifying consumers of changes to the collection, much the same way that INotifyPropertyChanged does for individual data structures.

    But how to sort them? Well, you’re going to say "Seder, you idiot, this an implementation of IEnumberable, you do just do an OrderBy". I don’t care for the attitude, but you are partially right. The problem is that OrderBy returns an OrderEnumerable which is not directly castable/convertible to ObservableCollection – so although you can use it, the result is not the same as the input.

    So I googled about this on Bing, and found an implementation of "SortedObservableCollection" which I used – I only changed the int to DateTime, for my needs. What does this mean? This means that I can declare my collections like this:

        public SortedObservableCollection<MailItem> MailItems

        {

            get { return mailItems; }

        } private SortedObservableCollection<MailItem> mailItems =

            new SortedObservableCollection<MailItem>(item => item.ItemDate);

    and like this:

        public SortedObservableCollection<Tweet> Tweets

        {

            get { return tweets; }

        } private SortedObservableCollection<Tweet> tweets =

            new SortedObservableCollection<Tweet>(tweet => tweet.TweetDate);

    The significance of this is that I define how I want tweets and mail items sorted here, and from now on when new items are added, they will be sorted using this lambda expression.

    Summary:
    I feel I finally broke through the ice with WPF and have a better grasp of MVVM too. I have a barely-working application right now. So for now, I have my status app that can sit in my little MIMO monitor.

    As far as next steps:

    • Switch over the RSS/XML feed for "current conditions" from NOAA, instead of using todays forecast.
    • Make the weather stuff MVVM, instead of coding things by hand
    • Create a popup for the authorization for twitter, instead of hard-coding the values.

    Then, if it does prove useful, I’ll likely add a settings screen that lets you set up multiple e-mail accounts, show your twitter mentions (which is probably the other "important" feature) and get more unit tests written for this.

    Tagged with: , ,
    Posted in Uncategorized, WPF and MVVM
    One comment on “WPF + MVVM – a quick little status app (2 of 2)
    1. […] been continuing to dabble with my little status app (described here and here). I wanted to write down some of the lessons learned so far. Also, I think I have a decent plan for […]

      Like

    Leave a Reply

    Fill in your details below or click an icon to log in:

    WordPress.com Logo

    You are commenting using your WordPress.com account. Log Out / Change )

    Twitter picture

    You are commenting using your Twitter account. Log Out / Change )

    Facebook photo

    You are commenting using your Facebook account. Log Out / Change )

    Google+ photo

    You are commenting using your Google+ account. Log Out / Change )

    Connecting to %s

    Archives
    Categories

    Enter your email address to follow this blog and receive notifications of new posts by email.

    Join 2 other followers

    %d bloggers like this: