(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Windows 8 Metro: Win as one with search and share contracts

(1 votes)
Andrea Boschin
>
Andrea Boschin
Joined Nov 17, 2009
Articles:   91
Comments:   9
More Articles
0 comments   /   posted on Jul 17, 2012
Categories:   Windows 8 , General
Tweet

As I said in the very first article of this series, one of the pillars of metro guidelines says :"win as one". When you write an application, you know that it has a specific domain where it is focused and works in the best way.

There are other things that are not specific to the domain and, also if they collaborate to the success, are better achieved by others. It is not a totally new concept, given that the operating system itself is an example of this way of thinking. As an example, when you need to open a file on the filesystem, you use an operating system's api to ask for a dialog that is completely managed by the operating system and it returns to you the choosed file.

So now imagine you can extend this concept to a more wide field where to collaborating members are not only some kind of basic subsystem but a large pletora of applications that are installed side by side. Today if you write an application that manages a diet (to say something of very specific) and you need to share a tweet, you have to write your own logic inside you application to connect to twitter, to compose the message and to send it. This opens to a wide set of problems: what do you do if twitter changes its api? Are you sure your way to write a tweet is what the user prefers?

Definitely the user usually takes more than one twitter client than, when he feels comfortable, he would be happy to use always this client for tweeting. In metro this concept is exactly applied. If you have a good twitter client use it to share your twit also if they come from your diet app. Thanks to the search & share contract, apps have an exact "protocol" they can use to speak each other, taking advantage one of the other's services.

What is a contract?

imageIn these days you probably already read much more about Metro contracts. No matter of what you read, it is better I propose a brief introduction about the concept just before entering the analisys of the specific search and share. For real, there is not so much to say. As a developer, probably you are use to deal with interfaces, the ones that usually you implement to add a specific behavior to a class. Established that, there is not an interface for real to implement when you decide to accomplish a contract in Metro, the example apply perfectly.

Infact, when you decide to implement a search or share contract, you have to implement a number of events, properties and other, to enable the communication between you and another application (or with the underlying operating system), in a known way. There is no obligation, about tecnical issues nor about certification, that you implement anything if you do not need it for real. But when the need exists, you have a drawed path to reach the goal.

When you decide to implement the contract, the very first thing to do, is to open the manifest editor and go to the Declarations tab. Inside this tab you can add the contract you want to implement into the application. In the figure on the right side there is the entire list of available contracts. I don't want to make an annoying explanation about each item in the list. Please take note about the "Search" and "Share Target" contract but also about Background tasks, File Open and File Save and other interesting opportunities. There is also a small number of properties of configuration you are asked to fill to make the contract working, all of them included into the manifest editor, but what it does really matter is the code you have to write.

About search...

The search contract is probably the first stop when you need to make you app much more metro compliant. There are lot of application that can take advantage of a own source of information and usually these have a way to search something. Today, searching does not mean something of really complex, made of a lot of options, operators, attributes and so on, but as Google teached, when you search, you enter one or more terms and you expect all the thing are handled directly by the search engine instead of by you. This is the concept of search that Metro provide. When you app implement a search contract it has to expect to receive a single string and it needs to handle all the complexity by itself.

After you added the search contract in the manifest, there are two different context you have to deal with when implementing a search contract: 

  1. searches sent to your app when it is closed or suspended
  2. searches made from inside the application

It may seems a very subtle difference but it is important to consider these scenario separated. Infact, the first point needs also you take care of the activation of the app, with all the concerns about resume from a suspension or from scratch. When you search from inside the application instead, your application is already active, so you only need to navigate to the search page or stay in the current page if the search is targeted to it.

The first scenario is handled by the override of the OnSearchActivated method in the Application class: 

   1: protected override void OnSearchActivated(SearchActivatedEventArgs args)
   2: {
   3:     var rootFrame = new Frame();
   4:     this.EnsureInitialized(rootFrame, args);
   5:  
   6:     if (rootFrame.Content == null)
   7:     {
   8:         rootFrame.Navigate(typeof(SearchGridPageView), args.QueryText);
   9:     }
  10: }

As I've said when you get this event, you have to deal with activation. For this purpose I've the habit to include the activation logic in an EnsureInitialized method, I call from the OnLaunched event or from the search. The argument is the root frame where the navigation takes place.

   1: private async void EnsureInitialized(Frame rootFrame, IActivatedEventArgs args)
   2: {
   3:     SearchPane.GetForCurrentView().QuerySubmitted += App_QuerySubmitted;
   4:  
   5:     if (args.PreviousExecutionState == ApplicationExecutionState.Running)
   6:     {
   7:         Window.Current.Activate();
   8:         return;
   9:     }
  10:  
  11:     SuspensionManager.RegisterFrame(rootFrame, "AppFrame");
  12:  
  13:     if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
  14:     {
  15:         await SuspensionManager.RestoreAsync();
  16:     }
  17:  
  18:     Window.Current.Content = rootFrame;
  19:     Window.Current.Activate();
  20: }

In the very first row of this method there is the hook for the in-app activation of the search contract. Attaching the QuerySubmitted event to the SearchPane, leads to get this event every time the user activates the search while the application is in foreground.

   1: private void App_QuerySubmitted(SearchPane sender, SearchPaneQuerySubmittedEventArgs args)
   2: {
   3:     Frame rootFrame = Window.Current.Content as Frame;
   4:  
   5:     if (rootFrame != null)
   6:         rootFrame.Navigate(typeof(SearchGridPageView), args.QueryText);
   7: }

Both the events go to the same conclusion. They navigates to the Search page passing the submitted query as parameter.

No need to deeply indagate about how they handles the search. imageIn my case a web service is called but what does matter for sure it that the search contract automatically start the search page in both the scenario. On the right side you can see the result of the implementation on my user group app.

Another thing you can do with search is implementing search suggestions.  Whit the SuggestionRequested event the runtime request back to the application a small set of suggestions that apply to the query the user is writing.

So if the user write “silver”, in the event handler you probably return “silver”, “silverlight”, “silver plated”, “silverlight alive” and “silverlight rocks”. The way you handle the return of suggestions is completely up to you.

   1: private async void App_SuggestionsRequested(SearchPane sender, SearchPaneSuggestionsRequestedEventArgs args)
   2: {
   3:     try
   4:     {
   5:         XeDotNetApiClient client = new XeDotNetApiClient();
   6:         var suggestions = await client.GetSuggestions(args.QueryText);
   7:         args.Request.SearchSuggestionCollection.AppendQuerySuggestions(suggestions);
   8:     }
   9:     catch
  10:     {
  11:         // swallow exception gracefully and do not return any suggestion
  12:     }
  13: }

Something that really matter in this sample is that, if I get an exception from my query on the web service, I swallow it because I need to ensure that the user can continue to write his search string. He will not have the suggestions, but he has not to worry about an error message.

…and about share

Since the search contract has only a direction, the share is bidirectional. That means you can act as a share source, providing to your user the ability of sharing something from your app to another, or you can behave as share target. Being a target means that you accept shares coming from other apps. Before understanding how share works in both the ways, it is important to understand what you can share. Infact, it does not really matter how you communicate between two applications but the payload you transfer from one application to another. When you activate the share (CTRL+C > Share charm) you get a list of application that can receive your share. This list is different whit different kind of payloads. Here is the kind of shares available:

  • Plain text
  • Hyperlink
  • HTML fragment
  • Image
  • Generic File

Saying you want to share a link, the first thing to do is to get an instance of the DataTransferManager. This class is responsible about the share and exposes a set of events, properties and methods. After you get the instance related to your application you attach the DataRequested event. This event is raised when the user activates the search charm and means you have to prepare the payload to transfer. Here is the implementation of a link sharing from my user group app:

   1: public MeetingPageView()
   2: {
   3:     this.InitializeComponent();
   4:  
   5:     // omissis other initializations...
   6:  
   7:     DataTransferManager dtm = DataTransferManager.GetForCurrentView();
   8:     dtm.DataRequested += dtm_DataRequested;
   9: }
  10:  
  11: private void dtm_DataRequested(DataTransferManager sender, DataRequestedEventArgs e)
  12: {
  13:     MeetingDTO meeting = this.DefaultViewModel[MeetingProperty] as MeetingDTO;
  14:  
  15:     if (meeting != null)
  16:     {
  17:         DataRequest request = e.Request;
  18:         request.Data.Properties.Title = meeting.Title;
  19:         equest.Data.Properties.Description = meeting.Abstract;
  20:         request.Data.SetUri(meeting.SubscriptionLink);
  21:     }
  22:     else
  23:         e.Request.FailWithDisplayText("nothing to share at the moment");
  24: }

One thing to know is that share source is contextual to the page you are displaying. So in my case, when I enter the page that shows a meeting, I prepare to share the meeting link. My hope is that someone publish the link on twitter or sends it by mail to a friend. imageSo I prepare a small package including the title and abstract of the meeting and the link to the subscription page. But when I’m out of the page the application does not share anything. On the right side the share when it is published to my twitter client.

Implementing the share target contract is not so straightforward. First of all you have to go to the manifest editor and add the Share Target declaration. Then you have to fill a required field that asks for the type of share supported. The field is a textbox where you have to enter a fixed number of options. As an example if you need to receive a link you have to specify URI in this field. It is easy to find the right field because when you add the share target contract it become red. Above it a brief text explain the allowed values.

After the configuration your work is not end. In the Application file (App.xaml.cs) you have now a OnShareTargetActivated event to handle. The implementation of this event is equals to the implementation of OnSearchActivated because it have the same activation tasks to deal with:

   1: protected override void OnShareTargetActivated(SearchActivatedEventArgs args)
   2: {
   3:     var rootFrame = new Frame();
   4:     this.EnsureInitialized(rootFrame, args);
   5:  
   6:     if (rootFrame.Content == null)
   7:     {
   8:         rootFrame.Navigate(typeof(SharePageView), args.ShareOperation);
   9:     }
  10: }

Finally you have to provide a page that will be showed when the share contract is activated. This page has to be designed to fill an half page because it is injected into the sliding frame opened by the share contract. To understand the point please watch ar the previous image. The black part is the space available for the share target page. The ShareOperation object passed during the navigation to this page contains all the required parameters. In the property Data.Properties there are all the details about the shared content. You als have a number of methods (ReportError, ReportCompleted, ReportStarted, etc...) that enable you to comminicate back to the runtime.

Other contracts.

Search and Share are only the most common and useful contracts that a metro-style application can implement. There are a number of other contrats that partecipate in the win as one pillar. Some of them are made to receive, other are for provide information. In the next number I would like to investigate on another useful contract. I'm speaking about the Settings that enable a centralized way to handle a common problem in applications: the configuration.


Subscribe

Comments

No comments

Add Comment

Login to comment:
  *      *       

From this series