Tag Archives: wp7

WP7: Tweetsharp and AgFx

Hello world!

WP7: Northern Lights WP7 Toolkit v0.0.1

In the previous months I posted some code examples that I used in my own Windows Phone applications.

After discovering some minor bugs and speed improvements I decided to put them into a toolkit library so they are easier to maintain.

I added the following posts / code examples into the toolkit:

The toolkit is called Northern Lights and hosted on codeplex. The project page is available HERE.

I’m looking forward on feedback and I will try to keep on updating the toolkit with new features while I keep developing my Windows Phone apps.

UPDATE: Currently (12/6/2011) Northern Lights is already at version v0.0.8. Make sure you check the codeplex homepage occasionally or use NuGet to get the latest updates.

WP7: LittleWatson Extended; Error reporting to HTTP endpoint

UPDATE 7 NOV 2011: Have a look at this post: WP7: Northern Lights WP7 Toolkit v0.0.1 for the latest version of this code example.

In the previous post I already mentioned the great talk of Jeff Wilcox on TechEd Australia (video here).

In his talk he mentions LittleWatson, a supporting class that Andy Pennell wrote. This class enables you to catch unhandled exceptions and let the user report these application errors to you, the developer.

A big downside of this class, in my opinion, is that it needs the user to send the report. I therefore took it up me to extend the class and add the possibility to automatically send the error report to an HTTP endpoint.

Here are the classes.

The main class.

LittleWatsonManager.c

namespace MyApp.Utils
{
    using System;
    using System.IO;
    using System.IO.IsolatedStorage;
    using System.Net;
    using System.Text;

    /// 
    /// LittleWatsonManager class.
    /// 
    /// 
    /// Text to user: Send application error reports automatically and anonymously to southernsun to help us improve the application.
    /// 
    public class LittleWatsonManager
    {
        #region Fields
        private static readonly LittleWatsonManager instance = new LittleWatsonManager();
        private const string Filename = "LittleWatson.txt";
        private const string SettingsFilename = "LittleWatsonSettings.txt";
        private bool allowAnonymousHttpReporting = true;
        #endregion

        #region Constructor
        /// 
        /// Initializes static members of the LittleWatsonManager class.
        /// 
        static LittleWatsonManager()
        {
        }

        /// 
        /// Prevents a default instance of the LittleWatsonManager class from being created.
        /// 
        private LittleWatsonManager()
        {
            this.allowAnonymousHttpReporting = this.GetSetting();
        }
        #endregion

        #region Properties
        /// 
        /// Gets DataManager instance.
        /// 
        public static LittleWatsonManager Instance
        {
            get
            {
                return LittleWatsonManager.instance;
            }
        }

        /// 
        /// Gets or sets a value indicating whether error reports are allowed to send anonymously to a http endpoint.
        /// 
        public bool AllowAnonymousHttpReporting
        {
            get
            {
                return this.allowAnonymousHttpReporting;
            }

            set
            {
                this.allowAnonymousHttpReporting = value;
                this.SetSetting(this.allowAnonymousHttpReporting);
            }
        }
        #endregion

        #region Public Methods
        /// 
        /// Report exception.
        /// 
        /// The exception to report.
        public static void SaveExceptionForReporting(Exception ex)
        {
            if (ex == null)
            {
                return;
            }

            try
            {
                using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    using (TextWriter output = new StreamWriter(store.OpenFile(Filename, FileMode.OpenOrCreate & FileMode.Truncate)))
                    {
                        output.WriteLine(Serializer.WriteFromObject(new ExceptionContainer() { Message = ex.Message, StackTrace = ex.StackTrace }));
                    }
                }
            }
            catch
            {
            }
        }

        /// 
        /// Check for previous logged exception.
        /// 
        /// Return the exception if found.
        public static ExceptionContainer GetPreviousException()
        {
            try
            {
                using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (store.FileExists(Filename))
                    {
                        using (TextReader reader = new StreamReader(store.OpenFile(Filename, FileMode.Open, FileAccess.Read, FileShare.None)))
                        {
                            string data = reader.ReadToEnd();

                            try
                            {
                                return Serializer.ReadToObject(data);
                            }
                            catch
                            {
                            }
                        }

                        store.DeleteFile(Filename);
                    }
                }
            }
            catch
            {
            }

            return null;
        }

        /// 
        /// Send error report (exception) to HTTP endpoint.
        /// 
        /// Exception to send.
        public void SendExceptionToHttpEndpoint(ExceptionContainer exception)
        {
            if (!this.AllowAnonymousHttpReporting)
            {
                return;
            }

            try
            {
                string uri = "http://www.yourwebsite.com/data/post.php";

                HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(uri);
                webRequest.Method = "POST";
                webRequest.ContentType = "application/x-www-form-urlencoded";

                webRequest.BeginGetRequestStream(
                    r =>
                    {
                        try
                        {
                            HttpWebRequest request1 = (HttpWebRequest)r.AsyncState;
                            Stream postStream = request1.EndGetRequestStream(r);

                            string info = string.Format("{0}{1}{2}", exception.Message, Environment.NewLine, exception.StackTrace);

                            string postData = "&exception=" + HttpUtility.UrlEncode(info);
                            byte[] byteArray = Encoding.UTF8.GetBytes(postData);

                            postStream.Write(byteArray, 0, byteArray.Length);
                            postStream.Close();

                            request1.BeginGetResponse(
                                s =>
                                {
                                    try
                                    {
                                        HttpWebRequest request2 = (HttpWebRequest)s.AsyncState;
                                        HttpWebResponse response = (HttpWebResponse)request2.EndGetResponse(s);

                                        Stream streamResponse = response.GetResponseStream();
                                        StreamReader streamReader = new StreamReader(streamResponse);
                                        string response2 = streamReader.ReadToEnd();
                                        streamResponse.Close();
                                        streamReader.Close();
                                        response.Close();
                                    }
                                    catch
                                    {
                                    }
                                },
                            request1);
                        }
                        catch
                        {
                        }
                    },
                webRequest);
            }
            catch
            {
            }
        }
        #endregion

        #region Private Methods
        private bool GetSetting()
        {
            try
            {
                using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    using (TextReader reader = new StreamReader(store.OpenFile(SettingsFilename, FileMode.OpenOrCreate, FileAccess.Read, FileShare.None)))
                    {
                        string content = reader.ReadToEnd();

                        if (!string.IsNullOrEmpty(content))
                        {
                            try
                            {
                                return Serializer.ReadToObject(content);
                            }
                            catch
                            {
                            }
                        }
                    }
                }
            }
            catch
            {
            }

            return true;
        }

        private void SetSetting(bool value)
        {
            try
            {
                using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    using (TextWriter output = new StreamWriter(store.OpenFile(SettingsFilename, FileMode.OpenOrCreate & FileMode.Truncate, FileAccess.Write, FileShare.None)))
                    {
                        try
                        {
                            output.WriteLine(Serializer.WriteFromObject(value));
                        }
                        catch
                        {
                        }
                    }
                }
            }
            catch
            {
            }
        }
        #endregion
    }
}

We make our own ExceptionContainer that can be serialized.

ExceptionContainer.c

namespace MyApp.Utils
{
    using System;
    using System.IO;
    using System.IO.IsolatedStorage;
    using System.Net;
    using System.Text;

    /// 
    /// ExceptionContainer class.
    /// 
    public class ExceptionContainer
    {
        /// 
        /// Gets or sets the message.
        /// 
        public string Message { get; set; }

        /// 
        /// Gets or sets the stacktrace.
        /// 
        public string StackTrace { get; set; }
    }
}

The supporting Serializer class.

Serializer.c

namespace MyApp.Utils
{
    using System.IO;
    using System.Runtime.Serialization.Json;
    using System.Text;

    /// 
    /// Serializer class.
    /// 
    public class Serializer
    {
        /// 
        /// Serialize object.
        /// 
        /// The Object type.
        /// The object to serialize.
        /// The serialized object.
        public static string WriteFromObject(T obj)
        {
            MemoryStream ms = new MemoryStream();
            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
            ser.WriteObject(ms, obj);
            byte[] json = ms.ToArray();
            ms.Close();
            return Encoding.UTF8.GetString(json, 0, json.Length);
        }

        /// 
        /// Deserialize object.
        /// 
        /// The object type.
        /// The serialized object.
        /// The deserialized object.
        public static T ReadToObject(string json)
        {
            MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json));
            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
            T obj = (T)ser.ReadObject(ms);
            ms.Close();
            return obj;
        }
    }
}

The HTTP endpoint that processes the error report, a PHP file.


You hook the manager with the UnhandledExceptionHandler of your App.xaml.cs

LittleWatsonManager.SaveExceptionForReporting(e.ExceptionObject as Exception);

And then add some code in your MainPage to report previous errors to your website:

            ExceptionContainer exception = LittleWatsonManager.GetPreviousException();

            if (exception != null)
            {
                if (LittleWatsonManager.Instance.AllowAnonymousHttpReporting)
                {
                    LittleWatsonManager.Instance.SendExceptionToHttpEndpoint(exception);
                }
                else
                {
                    // show popup.
                    this.notification.Show("Unhandled exception found", new SolidColorBrush(Colors.Red), null);
                }
            }

To summary what happens. An unhanled exception happens and is caught by your apps unhandled exception handler which contains the SaveExceptionForReporting() method to save the exception. The next time the user starts your application the code in your MainPage.xaml.cs will check if there was any exception that needs to be reporting. Depending on the settings this exception will be pushed to a HTTP endpoint as a POST request with a variable ‘exception’ that contains the original exception message and stacktrace. In my example the message is then e-mailed to my personal e-mailaddress. If the user opt-outs on this, you can still show him a popup and ask him to send the error report. You should allow the user to change these settings. This can achieved by changing the AllowAnonymousHttpReporting boolean of the manager.

You should make a settings page that lets the user select the option to “Send application error reports automatically and anonymously to ‘companyname’ to help us improve the application”.

So that’s it. Hope you like it!

Let me know if you have any questions.

WP7: Notify user of new Application Version

UPDATE 7 NOV 2011: Have a look at this post: WP7: Northern Lights WP7 Toolkit v0.0.1 for the latest version of this code example.

Inspired by the great talk of Jeff Wilcox on TechEd Australia (video here) I dediced to implement a Application Version checker for my application before I publish my first version to the marketplace.

Jeff Wilcox is the maker of 4Th & Mayor, a successful Windows Phone 7 application. In his talk on TechEd Australia he mentioned some tips&tricks for future application developers. One of those tips is to build a version checker in your app so you can notify the users when there is a new version available for your app.

He has a lot of users that are running an old version of his app and he has no way to reach out to them and inform them that there is a new version with a lot of new features.

The following code should solve this problem.

I implemented the AppVersionManager class which gets the latest version number from a text-file online and compares this with the version number stored in your WMAppManifest.xml.

AppVersionManager.c

namespace MyApp.Utils
{
    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.IO;
    using System.Net;
    using System.Threading;

    /// 
    /// AppVersionManager class.
    /// 
    public class AppVersionManager
    {
        #region Fields
        private static string uri = "http://www.myapp.com/data/appversion.txt";
        #endregion

        #region Public methods
        /// 
        /// Check for new version of app and execute action when new version is available.
        /// 
        /// The action to execute when there is a new version available.
        public static void CheckForNewVersion(Action action)
        {
            BackgroundWorker bgw = new BackgroundWorker();
            bgw.DoWork += (sender, e) =>
            {
                Version appVersion = GetVersion();

                DoWebRequest(AppVersionManager.uri, appVersion, action);
            };

            bgw.RunWorkerAsync();
        }
        #endregion

        #region Private methods
        private static Version GetVersion()
        {
            string version = StateManager.Get("AppVersion");

            if (string.IsNullOrEmpty(version))
            {
                version = Utils.General.GetVersion();
                StateManager.Set("AppVersion", version);
            }

            try
            {
                return new Version(version);
            }
            catch
            {
            }

            return default(Version);
        }

        private static void DoWebRequest(string uri, Version appVersion, Action action)
        {
            string id = "appversionrequest";

            if (action == null)
            {
              return;
            }

            Timer t = null;
            int timeout = 60; // in seconds

            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
                request.Accept = "*/*";
                request.AllowAutoRedirect = true;
                
                // disable caching.
                request.Headers["Cache-Control"] = "no-cache";
                request.Headers["Pragma"] = "no-cache";

                t = new Timer(
                    state =>
                    {
                        if (string.Compare(state.ToString(), id, StringComparison.InvariantCultureIgnoreCase) == 0)
                        {
                            Debug.WriteLine(string.Format("Timeout reached for connection [{0}], aborting download.", id));

                            request.Abort();
                            t.Dispose();
                        }
                    },
                    id,
                    timeout * 1000,
                    0);

                request.BeginGetResponse(
                    r =>
                    {
                        try
                        {
                            if (t != null)
                            {
                                t.Dispose();
                            }

                            var httpRequest = (HttpWebRequest)r.AsyncState;
                            var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);

                            if (httpResponse.StatusCode == HttpStatusCode.OK)
                            {
                                using (StreamReader reader = new StreamReader(httpResponse.GetResponseStream()))
                                {
                                    string response = reader.ReadToEnd();

                                    try
                                    {
                                        Version currentVersion = new Version(response);

                                        if (appVersion < currentVersion)
                                        {
                                            action();
                                        }
                                    }
                                    catch
                                    {
                                    }
                                }
                            }
                            else
                            {
                                Debug.WriteLine(string.Format("Error occured accessing endpoint: {0} [{1}]", httpResponse.StatusCode, uri));
                            }
                        }
                        catch
                        {
                            Debug.WriteLine(string.Format("Error occured accessing endpoint: [{0}]", uri));
                        }
                    },
                    request);
            }
            catch
            {
            }
        }

        #endregion
    }
}

the Utils.General.GetVersion() implementation

        public static string GetVersion()
        {
            Uri manifest = new Uri("WMAppManifest.xml", UriKind.Relative);
            var si = Application.GetResourceStream(manifest);
            if (si != null)
            {
                using (StreamReader sr = new StreamReader(si.Stream))
                {
                    bool haveApp = false;
                    while (!sr.EndOfStream)
                    {
                        string line = sr.ReadLine();
                        if (!haveApp)
                        {
                            int i = line.IndexOf("AppPlatformVersion=\"", StringComparison.InvariantCulture);
                            if (i >= 0)
                            {
                                haveApp = true;
                                line = line.Substring(i + 20);
                                int z = line.IndexOf("\"");
                                if (z >= 0)
                                {
                                    // if you're interested in the app plat version at all                      
                                    // AppPlatformVersion = line.Substring(0, z);                    
                                }
                            }
                        }

                        int y = line.IndexOf("Version=\"", StringComparison.InvariantCulture);
                        if (y >= 0)
                        {
                            int z = line.IndexOf("\"", y + 9, StringComparison.InvariantCulture);
                            if (z >= 0)
                            {
                                // We have the version, no need to read on.                    
                                return line.Substring(y + 9, z - y - 9);
                            }
                        }
                    }
                }
            }

            return "Unknown";
        }

The contents of the appversion.txt file

1.1.0.0

So know you can call AppVersionManager from your MainPage.xaml to check for a new version and execute whatever you want to notify the user of your new version of the application.

            AppVersionManager.CheckForNewVersion(() =>
            {
                // notify user of new version.
            });

Probably the best way is to show a popup and ask the user if he wants to upgrade and then navigate him to the MarketPlace.

Alright, that's all. I hope you like it! Feedback is welcome.

UPDATE: added 'if (action == null)' in the top of DoWebRequest method to skip webrequest when there is no action specified. Thanks to @peSHIr.

WP7: How to add an EULA page to your application

I’m busy working on a Windows Phone 7 Application and ran into the problem that I want to show the user an End User License Agreement (EULA) or Disclaimer page on start-up.

I want the disclaimer to show up before the MainPage is loaded and when accepted, will redirect the user to the MainPage and makes sure that the user is not able to navigate back to the disclaimer.

Here is the solution I came up with

First our supporting class, the DisclaimerManager.cs Singleton. It uses the IsolatedStorage to check if the user already accepted the disclaimer or not. When the user accepts the disclaimer a file is created in the IsolatedStorage. The IsAccepted boolean lets you know if the user already accepted the disclaimer.

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO.IsolatedStorage;
using System.IO;

namespace Classes
{
    public class DisclaimerManager
    {
        #region Fields
        private static readonly DisclaimerManager instance = new DisclaimerManager();
        private const string directory = "disclaimer";
        private const string filename = "shown.txt";
        #endregion

        #region Constructor
        static DisclaimerManager()
        {
        }

        private DisclaimerManager()
        {
            this.Check();
        }
        #endregion

        #region Properties
        public static DisclaimerManager Instance
        {
            get
            {
                return DisclaimerManager.instance;
            }
        }

        public bool IsAccepted { get; private set; }
        #endregion

        #region Private methods
        private void Check()
        {
            IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication();

            string path = string.Format(@"{0}\{1}", DisclaimerManager.directory, DisclaimerManager.filename);

            if (storage.FileExists(path))
            {
                this.IsAccepted = true;
            }
        }

        public void Accept()
        {
            IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication();

            string path = string.Format(@"{0}\{1}", DisclaimerManager.directory, DisclaimerManager.filename);

            if (!storage.DirectoryExists(DisclaimerManager.directory))
            {
                storage.CreateDirectory(DisclaimerManager.directory);
            }

            if (!storage.FileExists(path))
            {
                string value = "true";

                using (IsolatedStorageFileStream storageFile = storage.CreateFile(path))
                {
                    StreamWriter sw = new StreamWriter(storageFile);
                    sw.Write(value);
                    sw.Close();
                }

                this.IsAccepted = true;
            }
        }
        #endregion
    }
}

Then we need to update App.xaml.cs to make sure the Disclaimer Page is shown before the MainPage is loaded. We do this by attaching an event handler to the Navigating event of the RootFrame.

In App.xaml.cs, in the InitializePhoneApplication() method, after the RootFrame is initialized, add the following:

RootFrame.Navigating += this.RootFrame_Navigating;

Now we implement the event handler. It will check if the Disclaimer is accepted. If not, the DisclaimerPage is loaded. The event handler will detach itself, making sure it will not be called on further navigation in the app.

        private void RootFrame_Navigating(object sender, NavigatingCancelEventArgs e)
        {
            if (!DisclaimerManager.Instance.IsAccepted)
            {
                e.Cancel = true;

                RootFrame.Dispatcher.BeginInvoke(() =>
                    {
                        RootFrame.Navigate(new Uri("/Pages/DisclaimerPage.xaml", UriKind.Relative));
                    }
                );
            }

            RootFrame.Navigating -= this.RootFrame_Navigating;
        }

The DisclaimerPage.xaml



    
    
        
            
            
        

        
        
            
            
        

        
        
    
 
    
        
            
        
    

The Disclaimer.xaml.cs, It will navigate to the MainPage if the user accepts the Disclaimer.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;

namespace Pages
{
    public partial class DisclaimerPage : PhoneApplicationPage
    {
        public DisclaimerPage()
        {
            InitializeComponent();
        }

        private void ApplicationBarIconButtonClick(object sender, EventArgs e)
        {
            ApplicationBarIconButton button = sender as ApplicationBarIconButton;

            if (button != null)
            {
                if (string.Compare(button.Text, "accept", StringComparison.InvariantCultureIgnoreCase) == 0)
                {
                    DisclaimerManager.Instance.Accept();
                    this.NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
                }
            }
        }
    }
}

Now we need to add code in the MainPage.xaml.cs to make sure the user will quit the application when navigating back, instead of getting back to the Disclaimer. Therefore we overwrite the OnNavigated method with the following code:

        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            if (this.NavigationService.BackStack.Count() == 1)
            {
                try
                {
                    var a = this.NavigationService.BackStack.ElementAt(0);

                    if (a.Source.ToString().Contains("/Pages/DisclaimerPage.xaml") == true)
                    {
                        this.NavigationService.RemoveBackEntry();
                    }
                }
                catch (Exception ex)
                {
                    ex.PrintDebug();
                }
            }

            base.OnNavigatedTo(e);
        }

The count of the BackStack should be one, we are just arriving from the DisclaimerPage. We will check if the previous page is actual the DisclaimerPage and then remove this page from the stack. Note that in this case our DisclaimerPage.xaml is located in the Pages folder!

That’s all! Hope you like it!

References:
This post, Redirecting an initial navigation, by Peter Torr helped me create this solution.

WP7: PhoneApplicationService.Current.State made easy

UPDATE 7 NOV 2011: Have a look at this post: WP7: Northern Lights WP7 Toolkit v0.0.1 for the latest version of this code example.

I was reading “Programming Windows Phone 7”, a free Microsoft book (as PDF) from Charles Petzold and came across some code on why and how to access the PhoneApplicationService.Current.State (chapter 6, “Page State” paragraph) and thought about writing the following code to improve the usability.
Hope you like it:

    public class StateManager
    {
        private static PhoneApplicationService appService = PhoneApplicationService.Current;

        public static void Set<T>(string name, T value)
        {
            appService.State[name] = value;
        }

        public static T Get<T>(string name)
        {
            T result = default(T);

            if (appService.State.ContainsKey(name))
            {
                result = (T)appService.State[name];
            }

            return result;
        }
    }

It can then be invoked as follows:

Set a value

StateManager.Set<MyObject>("MyApplicationObject", obj);

Get a value

MyObject obj = StateManager.Get<MyObject>("MyApplicationObject");

Hope you like it.

Windows Phone and the WriteableBitMapEx

I was investigating the use of the WriteableBitmap class and came across a great extension pack WriteableBitMapEx.

This also pointed me to a great demo written by Bill Reiss, explaining Blitting and Blending of images with the WriteableBitmap class. His original example was written in Silverlight and I was interested to see how easy it would be to port it to the Windows Phone and see how the performance would be.

Well it was really easy! I didn’t need to rewrite any code and hadn’t any problems with performance (in the emulator – Sadly enough i don’t have an actual Windows Phone yet)!

Here is the result:

Particle Windows Phone demo from Bjorn Kuiper on Vimeo.

Try it out yourself, here is the sourcecode of the Windows Phone solution:

Download the sourcecode.

Windows Phone development made easy! I love it that I don’t need to learn a new programming language to make my own phone apps!