Missing Background Image on Panorama Control breaks ContextMenu and Causes Performance Issues in Windows Phone Applications

by Nick 2. February 2012 13:35

Warning: If you are using a background image for a Panorama control in your application, make sure you get the path correct. Here’s why:

Today I was working on a relatively complex application that uses the Panorama control on the main page of the application. It also has a context menu on items in a couple of the panes with the panorama control. Up until this afternoon everything seemed to work fine. However, as we get close to finishing the project I decided to go through and clean up the images that were included in the project (if you’ve ever worked with a designer or spent a lot of time in Blend you’ll understand why I have to do this step!). Now in most cases your application will continue to work even if the path to your images are wrong – you just won’t see an image where you expect to. This can make it a little hard to ensure you haven’t broken anything as part of the clean up process.

After doing the clean up I did notice that I’d broken a couple of image paths but figured that I’d fixed most of them. I did however also notice that the application was performing really slowly, like it would take a second or so to add a character into a textbox. As I mentioned this is a relatively complex application so I thought that I must have broken some logic in the background causing a perf issue. What I didn’t realise was that the perf issue was related to my image clean up.

Figuring I’d come back to sort out the perf issue later (I’ve been focussed on getting one part of the app finished today so head was in a different place in the app dev cycle) it wasn’t until I noticed another bug that I had to stop what I was doing and address the issue (although at the time I figured it was unrelated). It turned out that none of the the context menus (from the Silverlight toolkit) were working. OMG I thought, what have I done now…. I even thought I’d ping Jeff and see whether he had any gems of wisdom.

One of my golden rules is that I always use source control. Whether it is a simple project I’m working on by myself, or a large project, I always check in my code and frequently. The context menus had been working so I then spent the next hour trawling back through the source control history until I got back to a working version. I then walked forward gradually trying to isolate the issue. Eventually it came down to a missing “/” in the path to the background image for the Panorama control…. really…. yes, really!!! If you get the background image path wrong you will:

- Cause MAJOR performance issues in your application as the panorama control will continually look for the background image (well I’m guessing that’s what it’s doing but either way it’s a major UI performance drain)

- Prevent any ContextMenus from working on that page of your application

- There are probably other controls that may not work well.

Disclaimer: You might be thinking “why didn’t he notice the missing background image”…. in most case you’d be right as the background is usually hard to miss. In this particular instance the background image is very very subtle, just enough to give some visual feedback when the user pans. It looks great but you actually don’t notice it missing unless you’re looking for it.

Tags:

Mobile

Bookmark for Mobile App Design Guidelines

by Nick 2. February 2012 04:51

Tags:

Mobile

Framework Exceptions in Windows Phone

by Nick 6. January 2012 05:44

One of the switches that I often enable in Visual Studio is for it to notify my whenever an exception is thrown. This includes exceptions that are thrown internally by the .NET Framework. To enable this option, launch the Exceptions window from the Debug menu.

image

Select the Common Language Runtime Exceptions and check the Thrown checkbox. Click OK to apply this change

image

Now when you run your application you will see any exceptions that are raised, even if they are handles by you or the .NET Framework. Unfortunately sometimes this can be a bit of a drag because the .NET Framework does quite often throw exceptions, sometimes for legitimate reasons, sometimes not so. One such case is for both the CheckBox and RadioButton (actually the ToggleButton which is the base control is the source of this issue). After enabling Exceptions, add the following xaml to a new project.

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <CheckBox Content="Checkbox 1" />
</Grid>

Run your application and you will see the following, completely meaningless, exception being raised.

image

If you look at the call stack, apparently something is calling ToString on the ToggleButton control (ie the base control for the CheckBox you just added).

image

If you examine the ToString code, you’ll see something similar to the following, and you can see the reference to Resx.GetString. From the callstack this seems to be the issue.

public override string ToString()
{
    string text = base.ToString();
    string text2 = (base.Content ?? "").ToString();
    bool? isChecked = this.IsChecked;
    return string.Format(CultureInfo.InvariantCulture, Resx.GetString("ToggleButton_ToString_FormatString"), new object[]
    {
        text,
        text2,
        isChecked.HasValue ? isChecked.Value.ToString() : "null"
    });
}

Following this even further you eventually get to the internal Resx constructor where you can see that it attempts to load an assembly (see second line of the above callstack). This is the line that is failing, because the System.Windows.debug.resources assembly doesn’t exist for us developers Sad smile

internal Resx()
{
    Assembly assembly = base.GetType().Assembly;
    this.resources = new ResourceManager("System.Windows", assembly);
    string assemblyString = "System.Windows.debug.resources, Version=2.0.5.0, Culture=en-US, PublicKeyToken=7cec85d7bea7798e";
    try
    {
       Assembly assembly2 = Assembly.Load(assemblyString);
        this.debugResources = new ResourceManager("System.Windows.debug", assembly2);
    }
    catch (FileNotFoundException)
    {
    }
    this.fallbackResources = new ResourceManager("mscorlib", typeof(object).Assembly);
}

 

The work around for this is relatively simple. Create your own Checkbox class that inherits from CheckBox and override the ToString method.

public class MyCheckBox:CheckBox
{
    public override string ToString()
    {
        return string.Empty;
    }
}

Add this to your layout in place of the CheckBox

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <PhoneApp23:MyCheckBox Content="Checkbox 1" />
</Grid>

Run: Look, no Exceptions!

NOTE: Whilst the fact that the exception is thrown in the first place probably isn’t great coding, it has little, if any, effect on your app running in production. I’ve described this work around purely to get around the annoyance of the FileNotFoundException being picked up (the other way would be to specifically exclude this type of exception in the Exceptions dialog). Some ad-hoc testing indicates that this exception has an insignificant effect on performance or rendering times.

Tags:

Off Topic | Mobile

SocialViewer gets Windows Phone Mango treatment

by Nick 6. January 2012 04:50

As some of you may be aware I was involved in a project last year to enable developers to quickly build reading style applications. This started off as a simple template allowing users to pull in RSS data but exploded to be much more versatile. The template was made available at http://socialviewer.codeplex.com and is freely available for any Windows Phone developer to download and use.

Since the initial version we’ve made a number of enhancements to both functionality and the default look and feel. Over the festive season I started transitioning (and upgrading) the template to take advantage of the new Mango features. The configuration file (where you could previously only configure the feeds and lists for the application) now allows you to control layout, Ads and integration with various social networks.

I’m going to be doing a series of posts over the coming weeks on some of the new features of the Social Viewer template. We’ll start today with a recap of getting started with the template.

Step 1: Download the template

Go to http://socialviewer.codeplex.com and download the latest build from the Source Code tab. IMPORTANT: At this stage the latest version is still an Alpha release, which means that it’s probably not suitable for publishing apps. We’re hoping to get an official release out this month and would love any feedback you have.

Step 2: Unblock the Download

Gotta love Windows security – make sure you right-click the downloaded file, select Properties and then click the Unblock button.

image

Step 3: Extract the Download

Extract all the files from the changeset. There should be two files: a Zip file which is the template (don’t extract this file), and an OUT OF DATE word document that talks about the template (which for the time being you can ignore).

Step 4: Installing the Template

Copy the Zip file that was extracted out of the changeset (should be called BuiltToRoam.SocialViewer.Template.Zip) and place this file into your Visual Studio Templates folder. For example my templates folder is the following. Note that I added the “Silverlight for Windows Phone” sub-folder so that the template appears with all the other Windows Phone project templates.

C:\Users\Nick\Documents\Visual Studio 2010\Templates\ProjectTemplates\Visual C#\Silverlight for Windows Phone

Step 5: Run Visual Studio

Visual Studio should automatically pick up the new template even if it is already running. However, if you’re updating from a previous version of the SocialViewer template, you may need to restart Visual Studio for it to pick up the new version.

Step 6: Create a New SocialViewer Project

File –> New –> Project

Select the SocialViewer template and give the project a new name. The is currently a bug that means if you put a space in your project name you’re going to enter a word of pain. DO NOT PUT SPACES IN PROJECT NAME

image

Step 7: Run

That’s it…. out of the box you should literally be able to just run the newly created project. Please do not simply create a new project and publish it via Marketplace. That’s not the intent!

Here are some screen shots of the latest build:

image image image image

Main panorama: What’s new - vertical list of new feed items; Recent – hubtiles with images from Flickr; link list to pivot page, website (ie built to roam) and the about page.

image image image

On demand pivot (sources are only downloaded when the pivot is accessed). Reading page illustrating two different layouts: The default; WebBrowser for RSS feed items

image image

The Settings and About pages. Note the long list of social providers that can be used within your applications (See the configuration file) and the variety of data sources that you can consume (again, see configuration file).

Tags:

Announcements | Mobile

Sydney Hobart Yacht Race and the Lack of Sailing Apps

by Nick 27. December 2011 17:25

Recently I enjoyed watching the highlight from the Perth 2011 ISAF Sailing World Championships but was disappointed in the lack of mobile accessibility for results and information about the event. Whilst the website was functional, and actually looked quite good if you were viewing it on a desktop/laptop computer, it left a lot to be desired when accessing it via a mobile device. It seems surprising that given the push by the sailing fraternity to raise the profile of the sport that there was no consideration given to building an application for any of the mobile platforms.

The second failure of the sailing community is this year's Rolex Sydney Hobart Yacht Race. Personally I’m not a big fan of the design of the website but I do like the Yacht Tracker system they’re using. Unfortunately again there are no apps available for any of the mobile platforms as far as I’m aware.

Yesterday after watching the start to the Sydney Hobart on TV (yes, even though I live in Sydney I actually think you get a better view of the start via TV that fighting the crowds at either of the heads) I decided that I would put together a quick Windows Phone application using the data exposed by Twitter, the RSS feed and the data feeds that Yacht Tracker uses. I haven’t bothered to submit the app to Marketplace as the race would be over before it gets approved. Unfortunately this means that only users with an unlocked/developer Windows Phone can use the app.

My apologies if you can’t run the app, send me an email via the website and I’ll see what I can do to get you a trial version. I’ve also included the following screenshots so you can see what you’re missing (remember, this is less than 8 hours of work!)

sh1 s2 s3 s4

 

Updated 28/12/2011 00:20: Now supports background agent support for updating primary live tile with race leaders.

Updated 28/12/2011 11:13: Added multi-tile support, boat details, full page map support and about page.

Tags:

Mobile | Off Topic

Nokia Windows Phone Competition

by Nick 17. November 2011 02:56

I just realised that I haven’t posted for a while on this blog. I have however been posting across at both Visual Studio Magazine (Mobile Corner) and on BuildMobile (Windows Phone). A full list of postings and books is available here

 

image        image

The core purpose of this post is to let you know, or remind you, that we’re currently running a competition where you can win a Nokia Windows Phone by answering a question and tweeting a link. The full details and conditions for entry are outlined in the post, Win a Nokia Windows Phone. So far we’re posted four questions, with more to come. Don’t worry if you haven’t answered any so far, you can go back and answer the existing questions as entries will stay open until the competition closes.

Question 1: List a navigation API and provide a short example of how you might use it within your application?

Question 2: Other than the controls that ship with the Windows Phone SDK, what controls do you use in your application, and how do they make your application rock?

Question 3: Discuss one feature, or design, of an application you’ve worked on (doesn’t have to be published) that makes it distinctly Windows Phone?

Question 4: List an API (or a set of APIs) that allow developers to build Windows Phone applications that integrate with either device hardware or into the core platform (e.g. integration into one of the hubs)?

Tags:

Announcements | Mobile

Windows Phone Mango Feature Update

by Nick 29. September 2011 20:14

Over at BuildMobile.com I posted about all the awesomeness that is Windows Phone 7.5 (aka the Mango update). There are a couple of features that I either overlooked or were announced after that post went to air:

- Firstly, if I wasn’t clear: The final version of the Windows Phone SDK 7.1 is now available and can be downloaded via the Web Platform Installer (WebPI).

- Not only is the Microsoft Advertising Ad Control included within the SDK, it’s also now available in 11 countries, including Australia.

- There is now a web version of the Marketplace allowing you to purchase and invoke the download of applications directly to your phone.

- Windows Phone 7.5 supports Internet Sharing (ie tethering). Unfortunately this won’t immediately be available on all devices across all carriers but the potential is there – complaining to your telco might affect their decision to roll it out….

- The UserVoice site for Windows Phone has reopened for continual feedback on new product features.

Tags:

Announcements

Working with Many-to-Many Relationships in OData via WCF Data Services

by Nick 25. September 2011 05:37

The WCF Data Service implementation of OData makes for fantastic demo-ware, meaning you’re able to quickly knock up sample applications where you publish simple data base schemas and consume them in a Windows Phone, Silverlight or WPF application. However as soon as you start to do anything moderately complex the model seems to break. In my previous post “Windows Phone LINQ to SQL and the INotifyPropertyChanged and INotifyPropertyChanging Interfaces” I highlighted where the strongly typed client proxy classes fail when you start to Expand a number of entity relationships in a Select statement. In this post we’re going to take a quick look at working with Many-to-Many relationships and the failings specifically around updates.

Let’s work with a simple database schema where we have a table of Movies, and each movie can be assigned to multiple Genres. Of course, many different movies may be assigned to a single Genre, hence we have a many-to-many relationship. In our database schema this is represented using a linking table, MovieGenre, which has two columns representing the MovieId and the GenreId. The primary key is both columns.

In our entity model consumes this joining table, simply exposing the relationship as a collection on both Genre and Movie, as illustrated by the many-to-many association in the following entity model image.

image

For the most part working with this data is simple. We can query the movie data in exactly the same way that we do normally, for example:

var movies = from m in movieEntities.Expand(“Genres”)
                        select m;

The issues start to arise when you want to modify the genres that are associated with a movie. Adding a Genre is easy (newGenre is an existing Genre retrieved from the Genre table):

movie.Genres.Add(newGenre);

This is handled correctly by the client library and the appropriate POST is made to WCF Data Services to add the Genre to the Genres collection on the Movie (and hence to the MovieGenres table). What doesn’t work is removing a Genre. For example the following does NOT work:

movie.Genres.Remove(newGenre);

In fact, when you run this the request that is sent back to WCF Data Service attempts to delete the actual Genre from the database (luckily when I discovered this I hadn’t permitted write access to the table I was working with so this failed).

The work around is to call the AddLink and RemoveLink methods to add the appropriate links between the entities. Unfortunately these methods again require the use of string literals:

AddLink(movie, “Genres”, newGenre);

RemoveLink(movie, “Genres”, newGenre);

We can again use the same trick we used in the previous post to create a helper method that can ensure we have strongly typed code. The following codes actually replaces the existing related entities with a new set by iterating through and either adding or removing the appropriate entities.

public static void UpdateRelatedEntities<TSource, TTarget>(this DataServiceContext entities,
    TSource source,
    Expression<Func<DataServiceCollection<TTarget>>> collectionProperty,
    IEnumerable<TTarget> newRelatedEntities) {
    var memberExpression = collectionProperty.Body as MemberExpression;
    string collectionName = memberExpression.Member.Name;
    var sourceProperty = source.GetType().GetProperty(collectionName).GetValue(source, null) as IEnumerable<TTarget>;
    foreach (var genre in sourceProperty) {
        if (!newRelatedEntities.Contains(genre)) {
            //movie.Genres.Remove(genre);
            entities.DeleteLink(source, collectionName, genre);
        }
    }
    //movie.Genres.Clear();
    foreach (var newGenre in newRelatedEntities){
        if (!sourceProperty.Contains(newGenre)) {
            //movie.Genres.Add(newGenre);
            entities.AddLink(source, collectionName, newGenre);
        }
    }
}

The following demonstrates using this code:

movieEntities.UpdateRelatedEntities(movie, () => movie.Genres, newGenres);

Would love to think that I’ve missed an obvious way to achieve the same thing – if I have, please drop me an email and I’ll update the post. Apologies as comments are currently disabled.

Tags:

Mobile

Windows Phone LINQ to SQL and the INotifyPropertyChanged and INotifyPropertyChanging Interfaces

by Nick 4. September 2011 08:30

With Windows Phone 7.5 (aka Mango) we finally get access to SQL Server Compact  via LINQ to SQL from within our applications. In my previous post Change Tracking with SQL Server Compact (LINQ to SQL) on Windows PhoneI demonstrated that you can make use of some additional functionality of SSCE that wouldn’t otherwise be accessible via the managed apis. One of the other things I encourage developers to do is to use Visual Studio or SQL Server Management Studio to actually create their database structure, then use the SqlMetal command line tool to generate the necessary class files. The main reason I do this is because there is an awful lot of repetitive code that you should implement for each table and column in your database. Not only is this a waste of your time to write, it’s also very likely you’ll insert an error or two and then waste countless hours trying to debug your code.

Ok, onto the topic of this post. Lets start with a minimal entity called Movie with appropriate attributes for use with LINQ to SQL:

[Table]
public class Movie {
    [Column(IsPrimaryKey = true,
        IsDbGenerated = true,
        DbType = "INT NOT NULL Identity",
        CanBeNull = false,
        AutoSync = AutoSync.OnInsert)]
    public int MovieId { get; set; }

    [Column]
    public string  Name { get; set; }
 
    [Column]
    public int Year { get; set; }
}

Unlike most examples that you may have seen, the Movie class doesn’t implement either INotifyPropertyChanged or INotifyPropertyChanging. You might be thinking that these interfaces are required for LINQ to SQL to work properly. This is a complete fallacy, LINQ to SQL will work fine with this minimal implementation.

Here’s a couple of points about these two interfaces:

INotifyPropertyChanged

- You only need to implement this interface if you data class is going to change and you want those changes to propagate through to the UI.

- This interface is not required for LINQ to SQL to work

- This interface is not required to do read-once data binding

- If you’re going to be reading data from the data base and simply displaying it on the screen then you can, and should, leave this interface off as it just adds unnecessary clutter (KISS!)

INotifyPropertyChanging

- This interface is not required for LINQ to SQL to work

- This interface is not used by data binding

- You should however implement this interface to facilitate better memory management from LINQ to SQL

Here is an extract from the MSDN documentation around this interface.

http://msdn.microsoft.com/en-us/library/hh286406(v=VS.92).aspx#BKMK_MinimizingMemoryUsage

image

Let’s see this in action:

We’ll start with the Movie class without the INotifyPropertyChanging interface. The database is currently populated with a single Movie. We’ll run the following code and use the performance analysis tool in Visual Studio to examine what the impact is on memory.

var ms = dc.Movies.ToArray();

Thread.Sleep(5000);

Console.WriteLine("*******************************************");
Console.WriteLine("                We're done!!!");
Console.WriteLine("*******************************************");

From the Debug menu load the Windows Phone Performance Analysis

image

Select Memory profiling and click the Launch Application link

image

Note that in the code above we were using Console.WriteLine to indicate when the 5 second sleep has finished. One of the issues with the performance tool is that because the debugger is not attached you can’t see anything in the Visual Studio Output window. However, the emulator does have a console window which you can enable so that you can see the Console.Writeline output.

Follow this link to enable the console window on your computer: Windows Phone 7 Console Window on 64 bit machine

image

Now, lets drill into the performance report. Note that you can see two instances of the Movie class have been loaded into memory. This is consistent with what the MSDN documentation said would happen.

image

Now, let’s implement INotifyPropertyChanging. For example the Name property now looks like this

private string name;
[Column]
public string Name
{
    get { return name; }
    set
    {
        if (name == value) return;
        NotifyPropertyChanging("Name");
        name = value;
    }
}

And, let’s change our code so that in addition to loading the Movie into memory, we’ll change the Name property after five seconds.

var ms = dc.Movies.ToArray();

Thread.Sleep(5000);

ms.First().Name = "Changed name";
Thread.Sleep(5000);

Console.WriteLine("*******************************************");
Console.WriteLine("                We're done!!!");
Console.WriteLine("*******************************************");

Now when we run the performance analysis we should see that for the first five seconds there is one Movie in memory. Then when we change the Name property a second instance is created to track the original and changed entities. This is hard to see in the performance tool as the Movie entity is tiny. However, thanks to John from Soul Solutions for pointing out this simple but effective trick I added a 10Mb byte array to the Movie class. This makes it much easier to see when each Movie instance is created in the performance analysis.

private byte[] bytes = new byte[1024*1024*10];

The output:

image

In the first time block there is a single Movie instance, as expected. Then when we modify the Name property (at approx 15 time marker) another 10Mb block is allocated to a second Movie instance. Of course, don’t forget to remove the 10Mb byte array when you are finished debugging your memory management!

This illustrates how important it is to implement INotifyPropertyChanging if you have a large number or large entities in your LINQ to SQL database.

Tags:

Mobile

Change Tracking with SQL Server Compact (LINQ to SQL) on Windows Phone

by Nick 14. August 2011 11:31

As you are probably aware the Mango update for Windows Phone (ie Windows Phone OS 7.1) will include support for LINQ to SQL. According to Microsoft you shouldn’t need to know or care that this is SQL Server Compact (SQL CE) since they’ve limited access to the database to a set of managed wrappers, namely LINQ to SQL. Unfortunately this only represents a subset of the true capabilities of SQL CE. One of the sorely missing features is of course synchronization, as there is no support for Merge Replication, RDA or Sync Framework. In fact, even doing your own synchronization logic is crippled by the fact that you can’t tap into the native change tracking that SQL CE offers….. well, not using a supported set of APIs.

If we work on the assumption that what ships with Windows Phone OS 7.1 is actually SQL CE v3.5+ (to indicate that there have been some changes to the database engine to support Windows Phone but that the majority of core SQL CE functionality is there), then we can assume that the change tracking functionality, which is part of the SQL CE db engine, is available in the underlying database engine. It’s just not accessible via the managed interface (ie LINQ to SQL). However, if you’ve ever taken a look at change tracking in SQL CE you’d have noticed that all the information relating to  change tacking is maintained in private tables (__sysOCSTrackedObjects, __sysOCSDeletedRows and __sysTxCommitSequence) and private columns in the tracked tables (__sysChangeTxBsn. __sysInsertTxBsn, __sysTrackingContext). So in theory if change tracking was enabled (we’ll cover that in a minute) all we need to do in order to retrieve this information is to map these tables and columns. For the purpose of this post I’m just going to map the __sysChangeTxBsn column in the Person table. The bulk of this code was generated using the SqlMetal tool, although you might also want to try out Erik’s SQL Server Compact Toolbox . The bolded code was manually added to map the __sysChangeTxBsn column (the property ChangeTx is read only as you don’t want to modify this directly).

[global::System.Data.Linq.Mapping.TableAttribute()]
public partial class Person : INotifyPropertyChanging, INotifyPropertyChanged
{    
    private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
   
    private System.Guid _Id;
   private string _Name;

    private int? _ChangeTx;

    #region Extensibility Method Definitions
    partial void OnLoaded();
    partial void OnValidate(System.Data.Linq.ChangeAction action);
    partial void OnCreated();
    partial void OnIdChanging(System.Guid value);
    partial void OnIdChanged();
    partial void OnNameChanging(string value);
    partial void OnNameChanged();
    #endregion
   
    public Person() {
        OnCreated();
    }
   
    [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Id", DbType="UniqueIdentifier NOT NULL", IsPrimaryKey=true)]
    public System.Guid Id {
        get {
            return this._Id;
        }
        set {
            if ((this._Id != value)) {
                this.OnIdChanging(value);
                this.SendPropertyChanging();
                this._Id = value;
                this.SendPropertyChanged("Id");
                this.OnIdChanged();
            }
        }
    }
   
    [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Name", DbType="NVarChar(100)")]
    public string Name {
        get {
            return this._Name;
        }
        set {
            if ((this._Name != value))
            {
                this.OnNameChanging(value);
                this.SendPropertyChanging();
                this._Name = value;
                this.SendPropertyChanged("Name");
                this.OnNameChanged();
            }
        }
    }

   [global::System.Data.Linq.Mapping.ColumnAttribute(
                 
Storage = "_ChangeTx",
                  Name="__sysChangeTxBsn", 
                  DbType = "bigint")]
    public int? ChangeTx {
        get {
            return this._ChangeTx;
        }
    }

    public event PropertyChangingEventHandler PropertyChanging; 
    public event PropertyChangedEventHandler PropertyChanged;
   
    protected virtual void SendPropertyChanging() {
        if ((this.PropertyChanging != null)) {
            this.PropertyChanging(this, emptyChangingEventArgs);
        }
    }
   
    protected virtual void SendPropertyChanged(String propertyName) {
        if ((this.PropertyChanged != null)) {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Now that you’ve mapped this column you actually need to enable change tracking. As I’ve indicated previously there is no way to do this via LINQ to SQL, which means that if you want to enable change tracking you’ll need to create your database on a platform that supports ADO.NET which includes the API to enable change tracking. For me this was a simple Windows Forms application.

Create your SQL Server Compact database using the designer tools in either SQL Management Studio or Visual Studio (my database is called ChangeData.sdf). Next, add this database file to a newly created Windows Forms application and set the Build Action to Content, and make sure that it is copied to the output directory. Add a button to the form and then in the Click event handler add the following code – this opens a connection to the database file in the executing directory and enables change tracking on the Person table.

private void button1_Click(object sender, EventArgs e) {
    var cnstr = "Data Source=" +
                Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace("file:\\","") +
               
\\ChangeData.sdf;Persist Security Info=False;;

   var connection = new SqlCeConnection(cnstr);
    connection.Open();

    var tracker = new SqlCeChangeTracking(connection);
    tracker.EnableTracking("Person", TrackingKeyType.PrimaryKey, TrackingOptions.All);
}

Run the Windows Forms application and click the button. You’re done, change tracking is enabled on your database file. Copy this database file (make sure you get the one that is in the executing folder for the Windows Forms application) into your Windows Phone application. Again make sure the Build property is set to Content.

Ok, now to write some code against your change tracking database. The first thing we’re going to do is to copy the database that is packaged with the application from the installation folder (appdata, where it’s read only) into isolated storage (isostore, where it’s read-write). The following code then iterates through all the Person entities (I created a few sample records in the database via Visual Studio) and modifies the Name property.

void MainPage_Loaded(object sender, RoutedEventArgs e) {
    CopyReferenceDatabase("ChangeData.sdf", "ChangeData.sdf");
 
   string ReferenceDBConnectionString = "Data Source=isostore:/ChangeData.sdf";
   using (var db = new ChangeData(ReferenceDBConnectionString)) {
        var people = from person in db.Persons
                               select person;
        foreach (var p in people) {
           p.Name = p.Name + " updated";
        }
        db.SubmitChanges();
    }
}

public static void CopyReferenceDatabase(string referenceDatabaseName,
                                                                               string isolatedStorageDatabaseName) {
    var iso = IsolatedStorageFile.GetUserStoreForApplication();
    if (iso.FileExists(isolatedStorageDatabaseName)) return;

    using (var input = Application.GetResourceStream(
            new Uri(referenceDatabaseName, UriKind.Relative)).Stream) {
        using (var output = iso.CreateFile(isolatedStorageDatabaseName)) {
            var readBuffer = new byte[4096];
            int bytesRead;

            while ((bytesRead = input.Read(readBuffer, 0, readBuffer.Length)) > 0) {
                output.Write(readBuffer, 0, bytesRead);
            }
        }
    }
}

Now to see the effects of change tracking. If you set a break point in the for loop you can take a look at the ChangeTx property on the Person entities. If you run the application a few times you’ll see that this value changes each time you update the Person entities.

image

If you want to interrogate the database file further you can export the file from either the emulator or a real device using the Isolated Storage Explorer Tool:

c:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.1\Tools\IsolatedStorageExplorerTool>ISETool.exe ts xd 3abccaef-453b-4345-8f0e-7861873ddbf9 c:\temp\emu_export

The parameters for ISETool.exe are:
ts – Take snapshot (copy entire isolated storage files/folders for the specified application)
xd – Emulator (use de for actual, unlocked, device)
3abccaef-453b-4345-8f0e-7861873ddbf9 – The guid of the specified application. This is the ProductID attribute from the WMAppManifest.xml file for your application.
c:\temp\emu_export – The folder on the host computer that you want to push the snapshot too. I recommend specifying an empty folder.

Once you’ve exported the database file you can then connect to it via Visual Studio or SQL Management Studio. Note that in the designer both the private tables and private columns are hidden. However, if you run a SQL statement (eg select * from Person) you’ll see the private columns.

And that’s essentially it – you’ve got a change tracking enabled database which you can use to synchronize data back to a server. You might like to combine this with OData, in which case you might want to take a look at how I implement change tracking on the server side using WCF Data Services and Entity Framework (http://nicksnettravels.builttoroam.com/post/2010/08/03/OData-Synchronization-with-WCF-Data-Services.aspx).

Tags:

Mobile

Removing Strings in INotifyPropertyChanged and OData Expands

by Nick 6. August 2011 19:14

Ok, so this is a rather random post but I wanted to jot down a couple of scenarios where I often see string literals in code.

Scenario 1: String Literals in INotifyPropertyChanged Implementation

The first is the default implementation of INotifyPropertyChanged. If you’ve done data binding (for example with WPF or Silverlight) you’ll be familiar with this interface – when a source property value changes if you raise the PropertyChanged event the UI gets an opportunity to refresh. The standard implementation looks a bit like this:

public class MainPageViewModel: INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private string _Title;
    public string Title
    {
        get { return _Title; }
        set
        {
            if (Title == value) return;
            _Title = value;
            RaisePropertyChanged("Title");
        }
    }
    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

I always cringe when I see this because the name of the property, ie “Title” is passed into the RaisePropertyChanged method. This wouldn’t be so bad except this code block gets repeated over and over and over again – for pretty much any property you end up data binding to. Having string literals littered through your code is BAD as it makes your code incredibly brittle and susceptible to errors (for example you change the property name, without changing the string literal). A while ago I adopted the following version of the RaisePropertyChanged method which accepts an Expression and extracts the property name:

public void RaisePropertyChanged<TValue>(Expression<Func<TValue>> propertySelector)
{
    var memberExpression = propertySelector.Body as MemberExpression;
    if (memberExpression != null)
    {
        RaisePropertyChanged(memberExpression.Member.Name);
    }
}

The only change you need to make in your properties is to use a lambda expression instead of the string literal, for example:

private string _Title;
public string Title
{
    get { return _Title; }
    set
    {
        if (Title == value) return;
        _Title = value;
        RaisePropertyChanged(()=>Title);
    }
}

Scenario 2: String Literals in the Expands method for OData

Let’s set the scene – you’re connecting to an OData source using either the desktop or the new WP7 (Mango) OData client library. Your code might look something similar to the following – the important thing to note is that we’re writing a strongly typed LINQ statement to retrieve the list of customers.

NorthwindEntities entities = new NorthwindEntities(new Uri("http://services.odata.org/Northwind/Northwind.svc"));
private void LoadCustomers()
{
    var customerQuery = from c in entities.Customers
                                           select c;

    var customers = new DataServiceCollection<Customer>();
    customers.LoadCompleted += LoadCustomersCompleted;
    customers.LoadAsync(customerQuery);
}

private void LoadCustomersCompleted(object sender, LoadCompletedEventArgs e)
{
    var customers = sender as DataServiceCollection<Customer>;
    foreach (var customer in customers)
    {
        var name = customer.CompanyName;
    }
}

By default the LINQ expression will only retrieve the Customer objects themselves. If you wanted to not only retrieve the Customer but also their corresponding Orders then you’d have to change the LINQ to use the Expand method:

var customerQuery = from c in entities.Customers.Expand("Orders")
                                       select c;

Now, if you wanted to be more adventurous you could extend this to include the OrderDetails (for each Order) and subsequent Product (1 for each OrderDetails record) and Category (each Product belongs to a category).

var customerQuery = from c in entities.Customers.Expand("Orders/OrderDetails/Product/Category")
                                       select c;


The Order is also connected to the Shipper and Employee tables, so you might want to also bring back the relevant data from those tables too:

var customerQuery = from c in entities.Customers
                                                                     .Expand("Orders/OrderDetails/Product/Category")
                                                                     .Expand("Orders/Employee")
                                                                     .Expand("Orders/Shipper")
                                       select c;

The result is that you have a number of string literals defining which relationships you want to traverse and bring back. Note that you only need to do this if you want to eager load this information. If you want your application to lazy load the related data you don’t require the Expand method.

The work around for this isn’t as easy as the RaisePropertyChanged method used to eliminate string literals for the INotifyPropertyChanged scenario. However, we essentially use the same technique – we replace the string literal with an expression that makes the necessary traverses. For example:

var customerQuery = from c in entities.Customers
                                    .Expand(c=>c.Orders[0].Order_Details[0].Product.Category)
                                    .Expand(c => c.Orders[0].Employee)
                                    .Expand(c => c.Orders[0].Shipper)
                    select c;

You’ll notice that in this case where we traverse from Orders to Order_Details we need to specify an array index. This can actually be any number as it is completely ignored – it’s just required so that we can reference the Order_Details property which exists on an individual Order object.

Ok, but how is this going to work? Well we’ve simply created another extension method for the DataServiceQuery class, also called Expand but accepts an Expression instead of a string literal. This method expands out the Expression and converts it to a string, which is passed into the original Expand method. I’m not going to step through the following code – it essentially traverses the Expression tree looking for MemberAccess nodes (ie property accessors) which it adds to the expand string. It also detect Call nodes (which typically corresponds to the array index accessor eg get_item( 0 )) which is skipped to move on to the next node in the tree via the Object property.

public static class ODataExtensions
{
    public static DataServiceQuery<TElement> Expand<TElement, TValue>(this DataServiceQuery<TElement> query, Expression<Func<TElement, TValue>> expansion)
    {
        var expand = new StringBuilder();
        var expression = expansion.Body as Expression;
        while (expression.NodeType != ExpressionType.Parameter)
        {
            if (expression.NodeType == ExpressionType.MemberAccess)
            {
                if (expand.Length > 0)
                {
                    expand.Insert(0, "/");
                }
                var mex = (expression as MemberExpression);
                expand.Insert(0, mex.Member.Name);
                expression = mex.Expression;
            }
            else if (expression.NodeType == ExpressionType.Call)
            {
                var method = (expression as System.Linq.Expressions.MethodCallExpression);
                if (method != null)
                {
                    expression = method.Object as MemberExpression;
                }
            }
        }
        return query.Expand(expand.ToString());
    }
}

And there you have it – you can now effectively remove string literals from the Expand method. Be warned though: using the Expand method can result in a large quantity of data being retrieved from the server in one hit. Alternatively if the server has paging enabled you will need to ensure that you traverse any Continuations throughout the object graph (more on that in a subsequent post).

Tags:

Posting Twitter Status Update from Windows Phone

by Nick 28. July 2011 13:25

In my post Twitter in a Windows Phone 7 App across at BuildMobile I covered how to authenticate against Twitter using OAuth1. What I didn’t cover in that post is how you can then use the Access Token to interact with the Twitter API. In this post I’ll show you how you can post a status update to Twitter from your Windows Phone application.

Let’s start by creating a TextBox and a Button. Obviously the TextBox will be for the status to be posted and the Button will be to submit the Tweet:

<TextBox x:Name="TweetText"/>
<Button Content="Tweet" Click="TweetClick" />

Some points/gotchas to note:

* The user will have had to been authenticated prior to hitting this button or the post will fail.
* Twitter does not allow the same message to be repeatedly posted – if during testing you attempt to post the same test message, don’t be surprised if it fails!
* If your Windows Phone application is going to post messages to Twitter it need to request Read/Write (and potentially Direct Message) permissions to the users Twitter account.  This is not enabled by default when you create the application in Twitter, you have to go to the Settings tab and adjust the Application Type eg

image

Essentially in order to post a status update to Twitter you simply need to create the appropriately formed HttpWebRequest. The request will be a POST and you’ll include “status=<new status>” as the body of the POST. The url of the request will be “statuses/update.json” off the Twitter base API url. Note that the .json indicates that the response will come back as json. You can specify .xml if you’d prefer an XML response.

private void TweetClick(object sender, RoutedEventArgs e) {
    var requestParameters = new Dictionary<string, string>();
    var body = "status=" + UrlEncode(this.TweetText.Text);
    requestParameters[OAuthPostBodyKey] = body;

    var postUrl = "http://api.twitter.com/1/statuses/update.json";
    var request = CreateRequest("POST", postUrl, requestParameters);
    request.BeginGetRequestStream(reqresult => {
        var req = reqresult.AsyncState as HttpWebRequest;
        using (var strm = req.EndGetRequestStream(reqresult))
        using (var writer = new StreamWriter(strm)) {
            writer.Write(body);
        }
        req.BeginGetResponse(result => {
            try {
                var req2 = result.AsyncState as HttpWebRequest;
                if (req2 == null) throw new ArgumentNullException("result", "Request parameter is null");
                using (var resp = req.EndGetResponse(result))
                using (var strm = resp.GetResponseStream())
                using (var reader = new StreamReader(strm)) {
                    var responseText = reader.ReadToEnd();

                  Dispatcher.BeginInvoke(() => MessageBox.Show("Tweeted!"));
                }
            }
            catch {
                Dispatcher.BeginInvoke(() => MessageBox.Show("Unable to tweet"));
            }
        }, req);
    }, request);
}

If you followed the code on my BuildMobile post there are a couple of changes we need to make in order to support posting. The first enables us to pass in a Dictionary into the CreateRequest method. This is so that we can specify the body of the post which needs to be used in the calculation of the signature.

private WebRequest CreateRequest(string httpMethod, string requestUrl, IDictionary<string, string> requestParameters = null) {
    if (requestParameters == null) {
        requestParameters = new Dictionary<string, string>();
    }
    var secret = "";
    if (!string.IsNullOrEmpty(token)) {
        requestParameters[OAuthTokenKey] = token;
    secret = tokenSecret;
    }
    if (!string.IsNullOrEmpty(pin)) {
        requestParameters[OAuthVerifierKey] = pin;
    }
    var url = new Uri(requestUrl);
    var normalizedUrl = requestUrl;
    if (!string.IsNullOrEmpty(url.Query)) {
    normalizedUrl = requestUrl.Replace(url.Query, "");
    }
    var signature = GenerateSignature(httpMethod, normalizedUrl, url.Query, requestParameters, secret);
    requestParameters[OAuthSignatureKey] = UrlEncode(signature);

    var request = WebRequest.CreateHttp(normalizedUrl);
    request.Method = httpMethod;
    request.Headers[HttpRequestHeader.Authorization] = GenerateAuthorizationHeader(requestParameters);
    return request;
}

The other change we need to make is to the GenerateAuthorizationHeader method. Because the status itself will be passed as a form parameter in the body of the POST it shouldn’t be included in the authorization header. As such we need to modify the method to ignore any parameters that do not begin with “oauth_”.

public static string GenerateAuthorizationHeader(IDictionary<string, string> requestParameters)
{     var paras = new StringBuilder();     foreach (var param in requestParameters)     {
        if (!param.Key.StartsWith("oauth_")) continue;
        if (paras.Length > 0) paras.Append(",");         paras.Append(param.Key + "=\"" + param.Value + "\"");     }     return "OAuth " + paras;
}

 

And there we have it. Code that will enable your Windows Phone application to post to Twitter.

Tags:

Mobile

Windows Live Id Authentication using Messenger Connect for Windows Phone

by Nick 24. July 2011 10:26

If you saw my post on BuildMobile.com you would have seen that I used the “token” response_type when initially specifying the login url. This works well for getting a short lived access token but the wl.offline_access scope is effectively ignored – the expiry time comes back as 3600. This is completely useless for a Windows Phone application where you don’t want to have to reprompt the user every time they run the application. Luckily, despite the failings in the current implementation, there is a work around which involves a few more steps and uses the “code” response_type.

I’m not going to step through the entire process, instead, here is the code that you should be able to use – it’s based on my post Using Windows Live ID in a WP7 Appon BuildMobile.com so should be easy enough to follow.

Notes:
- You need to replace both <your_client_id> and <your_client_secret> with the corresponding values for your application
- In a production application you should not have the client id or client secret in plain text in your application - ideally this should be held on a server and only exposed to the client app over ssl and never persisted in the client app. 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Windows;
using System.Windows.Navigation;

namespace WLTest
{
    public partial class MainPage
    {
        // Constructor
        public MainPage()
        {
            InitializeComponent();
        }

        private void AuthenticateClick(object sender, RoutedEventArgs e)
        {
            var uriParams = new Dictionary<string, string>()
                                {
                                    {"client_id", "<your_client_id>"},
                                    {"response_type", "code"},
                                    {"scope", "wl.signin,wl.basic,wl.offline_access"},
                                    {"redirect_uri", "https://oauth.live.com/desktop"},
                                    {"display", "touch"}
                                };
            StringBuilder urlBuilder = new StringBuilder();
            foreach (var current in uriParams)
            {
                if (urlBuilder.Length > 0)
                {
                    urlBuilder.Append("&");
                }
                var encoded = HttpUtility.UrlEncode(current.Value);
                urlBuilder.AppendFormat("{0}={1}", current.Key, encoded);
            }
            var loginUrl = "https://oauth.live.com/authorize?" + urlBuilder.ToString();

            AuthenticationBrowser.Navigate(new Uri(loginUrl));
            AuthenticationBrowser.Visibility = Visibility.Visible;


        }

        public string AccessToken { get; set; }
        public string RefreshToken { get; set; }

        private void BrowserNavigated(object sender, NavigationEventArgs e)
        {
            if (e.Uri.AbsoluteUri.ToLower().Contains("https://oauth.live.com/desktop"))
            {
                var query = (from pair in e.Uri.Query.Trim('?').Split('&')
                             let bits = pair.Split('=')
                             where bits.Length == 2
                             select new KeyValuePair<string, string>(bits[0], bits[1])).ToArray();

                var code =
                    query.Where(kvp => kvp.Key == "code").Select(kvp => HttpUtility.UrlDecode(kvp.Value)).FirstOrDefault
                        ();
                if (string.IsNullOrEmpty(code))
                {
                    var error =
                        query.Where(kvp => kvp.Key == "error").Select(kvp => HttpUtility.UrlDecode(kvp.Value)).
                            FirstOrDefault();
                    var error_desc =
                        query.Where(kvp => kvp.Key == "error_description").Select(
                            kvp => HttpUtility.UrlDecode(kvp.Value)).FirstOrDefault();
                    MessageBox.Show("Error: " + error + "\n" + error_desc);
                    AuthenticationBrowser.Visibility = System.Windows.Visibility.Collapsed;
                    return;
                }

                var uriParams = new Dictionary<string, string>()
                                    {
                                        {"client_id", "<your_client_id>"},
                                        {"client_secret", "<your_client_secret>"},
                                        {"redirect_uri", "https://oauth.live.com/desktop"},
                                        {"code", HttpUtility.UrlEncode(code)},
                                        {"grant_type", "authorization_code"}
                                    };
                StringBuilder urlBuilder = new StringBuilder();
                foreach (var current in uriParams)
                {
                    if (urlBuilder.Length > 0)
                    {
                        urlBuilder.Append("&");
                    }
                    var encoded = HttpUtility.UrlEncode(current.Value);
                    urlBuilder.AppendFormat("{0}={1}", current.Key, encoded);
                }
                var tokenUri = "https://oauth.live.com/token?" + urlBuilder.ToString();

                var request = HttpWebRequest.CreateHttp(tokenUri);
                request.BeginGetResponse(result =>
                                             {
                                                 var req = result.AsyncState as HttpWebRequest;
                                                 using (var resp = req.EndGetResponse(result))

                                                 using (var strm = resp.GetResponseStream())
                                                 {
                                                     var serializer =
                                                         new DataContractJsonSerializer(
                                                             typeof (WindowsLiveAuthorizationCode));
                                                     var authorization =
                                                         serializer.ReadObject(strm) as WindowsLiveAuthorizationCode;
                                                     AccessToken = authorization.AccessToken;
                                                     RefreshToken = authorization.RefreshToken;

                                                     this.Dispatcher.BeginInvoke(() => MessageBox.Show(
                                                         "Access granted"));
                                                     RequestUserProfile();
                                                 }
                                             }, request);

                AuthenticationBrowser.Visibility = System.Windows.Visibility.Collapsed;
            }
          
        }

 


        private void RequestUserProfile()
        {
            var profileUrl = string.Format("https://apis.live.net/v5.0/me?access_token={0}",
                                           HttpUtility.UrlEncode(AccessToken));
            var request = HttpWebRequest.Create(new Uri(profileUrl));
            request.Method = "GET";
            request.BeginGetResponse(result =>
                                         {
                                             try
                                             {
                                                 var resp = (result.AsyncState as HttpWebRequest).EndGetResponse(result);
                                                 using (var strm = resp.GetResponseStream())
                                                 {
                                                     var serializer =
                                                         new DataContractJsonSerializer(typeof (WindowsLiveProfile));
                                                     var profile =
                                                         serializer.ReadObject(strm) as WindowsLiveProfile;
                                                     this.Dispatcher.BeginInvoke((Action<WindowsLiveProfile>) ((user) => {
                                                                            this.UserIdText.Text = user.Id;
                                                                            this.UserNameText.Text = user.Name;
                                                                                                                   }),
                                                                                 profile);
                                                 }
                                             }
                                             catch (Exception ex)
                                             {
                                                 this.Dispatcher.BeginInvoke(() =>
                                                                             MessageBox.Show("Unable to attain profile information"));
                                             }
                                         }, request);
        }

        [DataContract]
        public class WindowsLiveProfile
        {
            [DataMember(Name = "id")]
            public string Id { get; set; }

            [DataMember(Name = "name")]
            public string Name { get; set; }
        }

        [DataContract]
        public class WindowsLiveAuthorizationCode
        {
            [DataMember(Name = "access_token")]
            public string AccessToken { get; set; }

            [DataMember(Name = "refresh_token")]
            public string RefreshToken { get; set; }

            [DataMember(Name = "scope")]
            public string Scope { get; set; }

            [DataMember(Name = "token_type")]
            public string TokenType { get; set; }

            [DataMember(Name = "expires_in")]
            public string ExpiresIn { get; set; }
        }

        private void RefreshTokenClick(object sender, RoutedEventArgs e)
        {

            var uriParams = new Dictionary<string, string>()
                                {
                                    {"client_id", "<your_client_id>"},
                                    {"client_secret", "<your_client_secret>"},
                                    {"redirect_uri", "https://oauth.live.com/desktop"},
                                    {"refresh_token", RefreshToken},
                                    {"grant_type", "refresh_token"}
                                };
            StringBuilder urlBuilder = new StringBuilder();
            foreach (var current in uriParams)
            {
                if (urlBuilder.Length > 0)
                {
                    urlBuilder.Append("&");
                }
                var encoded = HttpUtility.UrlEncode(current.Value);
                urlBuilder.AppendFormat("{0}={1}", current.Key, encoded);
            }
            var tokenUri = "https://oauth.live.com/token?" + urlBuilder.ToString();

            var request = HttpWebRequest.CreateHttp(tokenUri);
            request.BeginGetResponse(result =>
                                         {
                                             var req = result.AsyncState as HttpWebRequest;
                                             using (var resp = req.EndGetResponse(result))

                                             using (var strm = resp.GetResponseStream())
                                             {
                                                 var serializer =
                                                     new DataContractJsonSerializer(
                                                         typeof (WindowsLiveAuthorizationCode));
                                                 var authorization =
                                                     serializer.ReadObject(strm) as WindowsLiveAuthorizationCode;
                                                 AccessToken = authorization.AccessToken;
                                                 RefreshToken = authorization.RefreshToken;

                                                 this.Dispatcher.BeginInvoke(() => MessageBox.Show(
                                                     "Token refreshed"));
                                             }
                                         }, request);
        }
    }
}

Tags:

Mobile

Windows Phone Mango Push Notification Changes

by Nick 23. June 2011 12:12

The basic concept of push notifications for Windows Phone doesn’t change for Mango. There are still three types (Push Notifications in Windows Phone), tile, toast and raw, and your application still needs to register with the Microsoft Push Notification Service in order to make use of this feature. The differences are that there are extensions to the service, which both plug holes in the initial implementation and provide support for multiple tiles and the back of tiles. Let’s go through the different types of notifications and I’ll point out some of the differences.

Toast Notifications

One of the issues with toast notifications in WP7 was that when the user clicked on the toast they were taken into the application as if they’d clicked on the application from the apps list. There was no arguments or information passed into the application to indicate where it had been launched from. This made it rather painful to do anything useful with toast notifications, other than trying to encourage users back into the application.

In Mango there is an additional (optional) Param element that can be specified in the toast message payload. This is essentially a navigation uri, or a query string, that is passed into the application. The payload message structure looks similar to the following:

string toastMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                                              "<wp:Notification xmlns:wp=\"WPNotification\">" + 
                                                  "<wp:Toast>" +
                                                      "<wp:Text1>First String</wp:Text1>" +
                                                      "<wp:Text2>Second String</wp:Text2>" + 
                                                      "<wp:Param>/PageTwo.xaml</wp:Param>" + 
 
                                                 "</wp:Toast>" + 
                                              "</wp:Notification>";

The bold line illustrates the new Param element. In this case if the user taps on this toast, they will be taken directly to PageTwo.xaml. Note that PageTwo.xaml will be the first page in the back stack, bypassing the normal entry point for the application.

The Param element can include a query string either in addition to, or instead of, a navigation uri. For example:

     /PageTwo.xaml?query1=value1
     ?query2=value2

In both cases the query key-value pairs can be extracted via the NavigationContext for the page that is navigated to. In the first case, this would be PageTwo.xaml, whereas in the second (where there is no page defined in the Param element) the page would be the default launch page for the application.

 

Tile Notifications

Live tiles get a major uplift in Mango. You still of course have the front tile which consists of the background image, a badge/count in the top right corner and the title. With Mango you can use the back of the tile as well. The back of the tile also has a background image and a title but also has a content attribute which can be used to display more text content (you can’t modify the font size or layout though). If that wasn’t enough, Mango also gives you the ability to pin multiple tiles for your application to the Start (in much the same way as web pages and contacts can be pinned to the Start today).

The new message structure for sending a tile notification is:

string tileMessage =     "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                                          "<wp:Notification xmlns:wp=\"WPNotification\">" +
                                              "<wp:Tile Id=\"{0}\">" +
                                                  "<wp:BackgroundImage>Background.png</wp:BackgroundImage>" +
                                                  "<wp:Count>6</wp:Count>" + 
                                                  "<wp:Title>My App Title</wp:Title>" + 
                                                  "<wp:BackTitle>Back Tile Title</wp:BackTitle>" + 
                                                  "<wp:BackContent>Some additional content to be displayed</wp:BackContent>" + 
                                                  "<wp:BackBackgroundImage>BackOfTileImage.png</wp:BackBackgroundImage>" +
                                              "</wp:Tile> " +
                                         "</wp:Notification>";

The new elements are:

BackTitle – The title to be displayed on the back of the tile

BackContent – The additional content to be displayed on back of the tile

BackBackgroundImage – The image 173x173 to use on the back of the tile. Can be local (as in this example) or from a remote server, assuming you specify a url that is on one of the permitted domains (see setup of push notifications)

Id – The Id attribute of the Tile element is used to identify tiles in the case where there are multiple tiles pinned to the Start for a given app. The default tile (which is created when the user pins the app from the apps list, as per current version of wp7) has an Id of “” or you can omit this attribute. The Id of a tile also determines the page, and corresponding query string, to be navigated to when the user clicks on the tile.

Raw Notifications

I’m not aware of any changes to raw notifications themselves. However, the service that is used to send notifications can register for a callback when the device becomes inactive – particularly if you are sending large numbers of raw messages out to a phone. More information is available via MSDN on Setting up a Callback Registration Request

Tags:

Mobile

Build Mobile: Windows Phone Push Notifications

by Nick 20. June 2011 18:49

The fourth part of a series on Windows Phone Push Notifications has been posted on Build Mobile. If you’re interested, check out the direct links below:

- Push Notifications in Windows Phone
- WP7 Push Notifications Part 2

In the Mobile Corner at the Visual Studio Magazine my latest article on using some of the system styles in Windows Phone has been published:

Style Your Application with Windows Phone 7 System Resources

A full listing of articles on both Build Mobile and Visual Studio Magazine is available here

Tags:

Announcements | Mobile

Windows Phone Developer Training: Are You Interested?

by Nick 3. June 2011 06:21

We’re looking to run Windows Phone developer training….. but need to know whether you’re interested! Complete the super-quick survey http://bit.ly/remixwp7 to let us know what you want. The quicker you respond, the quicker we’ll make a decision on when, where and how to run developer training.

Note: We’ve purposely dropped the “7” from the Windows Phone title in light of the announcement/SDK for Mango. If you’re skilled with WP7 but want to learn the ins and outs of Mango then make sure you add this to the comments so we can focus on that.

Tags:

Facelift for the Windows Phone 7 Social Viewer Template

by Nick 29. May 2011 17:11

I’ve just checked in the latest Social Viewer template to http://socialviewer.codeplex.com which among a number of bug fixes (including the fix to get Twitter auth working again) also includes a significant facelift.

Previously you defined a number of sources (RSS, Atom, Facebook, Twitter or your own) then grouped them into lists. These lists would then appear in a panorama. This sort of worked for a small number of lists, say 2-4, but you ended up with list after list after list – probably a pivot would be better suited to displaying all the lists.

I’m not a big fan of using a pivot as the starting point for an application so I wanted to craft a better experience that demonstrated the use of both the panorama and pivot controls, as well as using more of the features of the social viewer template itself. What we ended up with is a template that is broken into a main panorama which shows highlights, a more detailed pivot showing a number of on-demand lists and then of course the reading page. Let’s walk through them.

Main Panorama

The main panorama is essentially the launching point for the application. The first pane includes a vertical list of items that is entitled “what’s new” in keeping with the what’s new lists across the core platform. This is a simple list allowing you to easily scan the list for items of interest. Items appear dimmed/greyed to indicate it has been read.

Next we have the recent panorama item where we introduce a bit of a change in pace – you’ll again notice this across the core platform, where you’ll see the panorama broken up into different styles of panes. Here we’re including items that have got images/enclosures to add some life to the panorama.

Lastly we have a pane that is sort of a menu in that it links to different parts of the application. As the template can take advantage of trial mode, there is also a Purchase menu item that is not visible here, linking the user off to the Marketplace link for the application to be purchased.

image image image image

On-Demand Pivot

Clicking on the Developers link on the main panorama navigates the user to a page which contains the pivot control. The control is data bound to a collection of lists. As the user flicks between the lists, the corresponding feeds are downloaded and processed on-demand (as you can see in the second image with the progress bar still visible).

image image image image

Reading

Clicking on any of the items from any of the lists on the Main Panorama or the On-Demand Pivot will navigate the user to the reading page. Here the full post is available, along with any links (see second image) that the user may want to navigate to. The user can also opt to share the post via Facebook, email the post or tweet a reference to the post.

image image image

Settings and About

The last two pages of the template are the Settings and About pages. These are provided out of the box to allow the user to manage their Facebook and Twitter credentials, and to list relevant information about the sources of the data for the application. The About page also provides a mechanism whereby the user can review (via Marketplace) or provide feedback (via email) on the application.

image image

I would love feedback on the latest version of the social viewer template – http://socialviewer.codeplex.com (the updated version is still on the source code tab until I finish testing the updates)

Tags:

Mobile

Display PDFs in your Windows Phone 7 application

by Nick 14. May 2011 18:18

I just downloaded and was experimenting with the CTP of ComponentOne Studio for Windows Phone. Normally I don’t give these third party control libraries much interest as they’re usually expensive and generally just add another dependency to your application that you have no control over. This is particularly true with mobile applications where performance is so critical.

Anyhow, the one thing that caught my eye in this control library is that there is a PDF Viewer control. There is full documentation available online, including details of this control. What interested me was the following:

View and Save PDF Files

The C1PdfViewer control can be used to view and save PDF files on the Windows Phone device. C1PdfViewer has no external dependency on Adobe Reader to view or save files. Content is parsed and rendered as native XAML elements.

That it awesome that they’re rendering the PDF as native XAML.

After downloading the CTP I was a little lost to start with as I couldn’t see the controls appear within Visual Studio. However they ship a cool Sample Explorer which I was able to use to play around with their samples.

image

After taking a look at the sample I decided to see how easy it was to create a simple application that displays a PDF. Rather than loading the PDF from a resource within the application which is what the sample demonstrates, I decided to load a test PDF located at  http://www.blhr.org/media/documents/test.pdf

The XAML for MainPage is:

<phone:PhoneApplicationPage
    x:Class="PDFViewer.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:c1Pdf="clr-namespace:C1.Phone.PdfViewer;assembly=C1.Phone.PdfViewer"
    Loaded="MainPageLoaded">

    <Grid>
            <c1Pdf:C1PdfViewer x:Name="pdfViewer"
                               ViewMode="FitWidth"
                               Visibility="Collapsed" />
    </Grid>
</phone:PhoneApplicationPage>

And the code to load the PDF is quite simple:

public partial class MainPage : PhoneApplicationPage {
    public MainPage() {
        InitializeComponent();
    }
    private WebClient wc = new WebClient();
    private void MainPageLoaded(object sender, RoutedEventArgs e) {
        wc.OpenReadCompleted += WcOpenReadCompleted;
        wc.OpenReadAsync(new Uri(http://www.blhr.org/media/documents/test.pdf));
    }
    void WcOpenReadCompleted(object sender, OpenReadCompletedEventArgs e) {
        pdfViewer.LoadDocument(e.Result);
        pdfViewer.Visibility = Visibility.Visible;
    }
}

Running this displays the pdf in the application:

image

Warning: There are some definite limitations with this control that are outlined both on the website and within the documentation. For example my first test PDF didn’t render (http://www.education.gov.yk.ca/pdf/pdf-test.pdf) throwing an exception relating to do with the compression format. The guidance is currently to only use this control for rendering PDFs that you have control over.

 

Tags:

Mobile

Windows Phone 7 Social Viewer Template v2 Published

by Nick 3. May 2011 14:58

If you want to jump start your Windows Phone application development, why not download the Social Viewer template available at http://socialviewer.codeplex.com. You can use this to simplify the creation of apps that read data from Facebook, Twitter, Blogs and any other data feed you might have.

The template integrates directly into Visual Studio 2010 (which you’ll need to do Windows Phone 7 development – http://create.msdn.com) and appears in the New Project dialog.

image

Like most Visual Studio 2010, once you’ve created a new project you can simply hit F5 to run it in the emulator or on a device. Pan left-right to see the different lists. Select an item to read the full item.

image  image  image  image

You can easily change the feeds and the lists that are displayed to the user in the configuration.xaml file. Add api keys for Facebook, Twitter and AdGACto allow the user to post to Facebook or Twitter, and to integrate Ads into your application.

Get the Social Viewer Template v2 from http://socialviewer.codeplex.com today and get building your WP7 applications.

Tags:

Mobile | Announcements

Fix: Calling WCF Methods with Parameters Synchronously on Windows Phone 7

by Nick 27. April 2011 05:33

In my previous post, Tidying up Windows Phone 7 + WCF Synchronous Programming Model I proposed a base class called SyncClientBase which would make it much easier to call WCF services from a background thread in a synchronous fashion. Unfortunately, whilst this works well for methods that don’t take any arguments, it completely fails when trying to call methods which take parameters. The first step in the Invoke method was to pull apart the Expression parameter in order to determine the async method that was to be invoked and the corresponding event that would be raised.  ie:

    public TResult Invoke<TResult>(Expression<Action> asyncMethod) 
                          where TResult : AsyncCompletedEventArgs {
        var memberExpression = asyncMethod.Body as MethodCallExpression;
        var method = memberExpression.Method;
        var eventName = method.Name.Replace("Async","Completed");
        var evt = Proxy.GetType().GetEvent(eventName);

 

This works, however, when it comes to actually invoking the async method I was passing in an empty object array – clearly not correct when the method takes parameters. On the desktop we could pull the Expression apart even more and actually pull out the arguments that were supplied in the expression tree. However, after playing around a bit I couldn’t work out how to do this without the .NET CF raising a FieldAccessException or two.

I decided to rework the Invoke method a little: Instead of accepting an Action Expression, it now takes a Func<Delegate> Expression (ie a function that returns the method we want to invoke). It also accepts an array of objects which will be the parameters to the async method to be invoked. The full Invoke method looks like:

public TResult Invoke<TResult>(Expression<Func<Delegate>> asyncMethod, params object[] args) where TResult : AsyncCompletedEventArgs
{     var body = asyncMethod.Body as UnaryExpression;     var operand = body.Operand as MethodCallExpression;     var arg = operand.Arguments[2] as ConstantExpression;     var method = arg.Value as MethodInfo;     var eventName = method.Name.Replace("Async", "Completed");     var evt = Proxy.GetType().GetEvent(eventName);     lock (downloadLock)     {         TResult data = default(TResult);         EventHandler<TResult> handler = (s, e) =>         {             data = e;             downloadWait.Set();         };         evt.AddEventHandler(Proxy, handler);         method.Invoke(Proxy, args);         downloadWait.WaitOne();         evt.RemoveEventHandler(Proxy, handler);         if (data.Error != null)         {             throw data.Error;         }         return data;     }
}
 

The proxy class structure still looks very similar. The only difference is that instead of a method call eg Proxy.Step1Async() we’re passing a delegate eg (Action)Proxy.Step1Async. For methods that require a parameter (eg Step3) the parameters are passed in as additional parameters to the Invoke method.

public class Service1Proxy : SyncClientBase<Services.Service1Client, ServiceTester.Services.IService1>, DemoService.IService1 {     public int Step1() {
        return base.Invoke<Step1CompletedEventArgs>(() => (Action)Proxy.Step1Async).Result;
    }
 
    public int Step2() {
        return base.Invoke<Step2CompletedEventArgs>(() => (Action)Proxy.Step2Async).Result;
    }
 
    public int Step3(int xyz) {
        return base.Invoke<Step3CompletedEventArgs>(() => (Action<int>)Proxy.Step3Async,xyz).Result;
    }
}
 

The only issue with this structure is that there is not validation that the correct number and type of parameters are passed into the Invoke method. Other than that, it’s still quite an elegant wrapper.

Tags:

Mobile

Powered by BlogEngine.NET 2.0.0.36

Automotive Theme by Car Leasing Experts

 

Page List