Although having the user run your app directly
from the start screen or the list of apps is interesting, it would be
useful if there were other ways to invoke your application. In fact, you
might want other apps to be able to launch your app. For example, if
you’ve created a Twitter application, you might want to be able to have
other apps launch your app (instead of just being able to share with the
“official” Twitter integration). Luckily, that is supported.
There are two ways of automatically launching your app:
• Launching from a custom protocol
• Launching from a file association
Launching from a custom protocol involves
allowing other apps to launch using a special method and a shared
protocol name. Launching from a file association involves registering a
custom file extension with the phone to enable your users to launch your
application when they tap on a file from an email, SkyDrive, or
documents. Let’s see how each of these works.
Using a Custom Protocol
A custom protocol is simply a specially formatted URI that contains a special moniker (that is, myapp:someotherdata?and=querystring
). The special moniker is used to allow other apps to launch your application using the LaunchUriAsync
on the Launcher
class:
async void secondQuery_Click(object sender, RoutedEventArgs e)
{
await Launcher.LaunchUriAsync(
new Uri("funwithassociations:showsecond?id=foo"));
}
This allows one application to launch another
with data included so that the other app knows what action to perform.
The moniker is what is before the colon (:); what is after is completely
up to you. Monikers have to follow these simple rules:
• Must be a name of 2–39 characters
• Can contain numbers, lowercase letters, periods (.), and hyphens (-)
• Cannot be one of the reserved URIs (as documented at http://shawnw.me/WPReservedFiles
)
To register a protocol, you must open the
WMAppManifest.xml in the text editor (right-clicking and selecting “View
Code” works well). The built-in application manifest editor cannot be
used to add protocols. Just after the Tokens
element, create a new element called Extensions
. Inside the new element, add an element called Protocol,
like so:
<Deployment xmlns="..."
AppPlatformVersion="8.0">
<App ...>
...
<Tokens>
...
</Tokens>
<Extensions>
<Protocol Name="funwithassociations"
NavUriFragment="encodedLaunchUri=%s"
TaskID="_default" />
</Extensions>
...
</App>
</Deployment>
The Protocol
element’s Name
attribute is the name of the moniker (adhering to the rules listed previously). The NavUriFragment
and TaskID
must be the same as this example. Now that you have that, you will need to listen for launching with the new URI.
To facilitate listening for the launching URI, you will need a class that derives from the UriMapperBase
class. This class will get called as the navigation framework gets notified of the navigation using the protocol URI, like so:
public class AssociationMapper : UriMapperBase
{
}
In this new class, you need to override the MapUri
method. This is the method that will be called as the navigation
framework attempts to navigate. In this case, you’ll look for your URI
and return a URI that maps it to the resulting page in your application,
like so:
public override Uri MapUri(Uri uri)
{
var decodedUri = HttpUtility.UrlDecode(uri.ToString()).ToLower();
if (decodedUri.Contains("funwithassociations:showfirst"))
{
return new Uri("/ShowFirst.xaml", UriKind.Relative);
}
else
{
// Let normal navigation happen
return uri;
}
}
The first thing to do is to decode the URI as it
comes to the navigation framework URI encoded. After you have done
that, you can search to see whether the URI contains your moniker.
Remember that anything after your protocol (and the colon) is data
specific to your application. In this example, we are specifying that if
the characters after the protocol moniker say “showfirst,” then we can
change the navigation to go to the FirstPage.xaml
file. If
it doesn’t match what we expect, we simply let the original URI pass
through to the navigation framework. In this way, you can handle
multiple types of uses for the protocol and data:
public override Uri MapUri(Uri uri)
{
var decodedUri = HttpUtility.UrlDecode(uri.ToString()).ToLower();
if (decodedUri.Contains("funwithassociations:showfirst"))
{
return new Uri("/ShowFirst.xaml", UriKind.Relative);
}
else if (decodedUri.Contains("funwithassociations:showsecond"))
{
var split = decodedUri.Split(new char[] { '?' },
StringSplitOptions.RemoveEmptyEntries);
if (split.Count() > 2) // There is a questionmark after protocol
{
// Add query string
return new Uri(string.Concat("/ShowSecond.xaml?", split[2]),
UriKind.Relative);
}
else
{
return new Uri("/ShowSecond.xaml?", UriKind.Relative);
}
}
else
{
// Let normal navigation happen
return uri;
}
}
In this example, the code is looking for the showsecond
command after the moniker to do something different. In this case, the
user could have passed in a query string with useful data for us to
start the application with. Notice that the code uses split
to separate the URI at the question mark. This is common if you’re
using a query string, but be careful because the decoded URI that is
actually launched looks like this (the second question mark and colon
were URI-encoded to protect the URI):
/protocol?encodedlaunchuri=funwithassociations:showsecond?id=foo
This means that if you want to split off the
query string, you will need to manipulate the entire string correctly.
The example does this by splitting on the question mark and just taking
the entire third part of the split to post-pend to the page URI.
Now that we have the handler, we need to register it with the phone. You should open the standard App
class file (usually App.xaml.cs). Near the bottom of the file is a private method called InitializePhoneApplication
. Just before the handling of the NavigationFailed
method, add a new line that assigns the UriMapper
to a new instance of your UriMapperBase
derived class (AssociationMapper
in this case):
private void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
return;
// ...
RootFrame = new PhoneApplicationFrame();
RootFrame.Navigated += CompleteInitializePhoneApplication;
// Add my mapper
RootFrame.UriMapper = new AssociationMapper();
// Handle navigation failures
RootFrame.NavigationFailed += RootFrame_NavigationFailed;
// Handle reset requests for clearing the backstack
RootFrame.Navigated += CheckForResetNavigation;
// Ensure we don't initialize again
phoneApplicationInitialized = true;
}
The non-bold version of the code should already exist—you’re just adding your new URI mapper to the RootFrame
object so you can intercept the navigation calls. At this point, you
should be able to call your app from another app by calling the
following:
await Launcher.LaunchUriAsync(
new Uri("funwithassociations:showsecond?id=foo"));