As of last week I assumed management and content development for the Windows App Builder’s Blog, which I’m sure most of you follow already.

What I’d love to hear from you is this: what content would you like to see on the blog? What questions are you dying to have answered? What ‘inside stories’ from the Windows engineering team would you like to know? Let me know in the comments!


To close this series, I wanted to share a few additional bits related to webview, but not necessarily related to one another!

First, in the previous post I showed how to wrap some of the webview’s capture methods in a promise. The same thing is easy to do with invokeScriptAsync, where the promise is fulfilled with the value returned from the webview’s script:

functon callWebviewScript(webview, targetFunction, argsIn) {
return new WinJS.Promise(function (cd, ed) {
var op = webview.invokeScriptAsync(targetFunction, argsIn);

        op.oncomplete = function (args) {;
//Return value from the invoked function (always a string) is in args.target.result
cd(args.target.result);
};

op.onerror = function (e) { ed(e); };
op.start();
});
}

This kind of structure helps you remember the necessary call to start at the end.

With invokeScriptAsync, be aware that the result you pass back must be a string, otherwise you’ll get an empty string.

In the callWebviewScript function above, note that targetFunction can be set to “eval” and argsIn to whatever script string you want to evaluate–meaning that the webview doesn’t necessarily have to have a named function inside it. Just remember that the result of that eval must again be a string.

The next little tip is just knowing that webview cache cookies and other content just like the browser, even if you remove the webview from the DOM and let it get garbage collected. There aren’t facilities to clear information that’s cached in the webview, though you might be able to achieve this through the Windows.Web.HttpClient API instead.

Lastly, the webview has a characteristic that if you overlay another element on a webview that has 100% opacity, the webview will go black as it fails “layer candidacy” as it’s called. The same thing happens with an overlay element with 0% opacity. (See http://msdn.microsoft.com/en-us/library/jj680148(v=vs.85).aspx under “Unsupported Scenarios” section for the description of scenarios where it fails layer candidacy.)

If, however, the overlay element’s style has an opacity of anything other than 1 or 0, like 0.9999 or 0.0001, then the webview is happy. It’s an odd behavior, but a good one to know about.


One of the key feature of the webview that really sets it apart from the iframe is the ability to capture its content, something that you simply cannot do with an iframe. There are three ways this can happen.

First is the src attribute. Once the webview has navigated (and fired its MSWebViewNavigationCompleted event), src will contain a URI to the content as the webview sees it. For web content, this will be an http[s] URI, which can be opened in a browser. Local content (loaded from strings or app data files) will start with ms-local-web, which can be rendered into another webview using navigateToLocalStream. Be aware that while navigation is happening prior to MSWebViewNavigationCompleted, the state of the src property is indeterminate; use the uri property in those handlers instead.

Second is the webview’s captureSelectedContentToDataPackageAsync method, which reflects whatever selection the user has made in the webview directly. The fact that a data package is part of this API suggests its primary use: the share contract. From a user’s perspective, any web content you’re displaying in the app is really part of the app. So if they make a selection there and invoke the Share charm, they’ll expect that their selected data is what gets shared, and this method lets you obtain the HTML for that selection. Of course, you can use this anytime you want the selected content—the Share charm is just one of the potential scenarios.

As with invokeScriptAsync, the return value from captureSelectedContentToDataPackage­Async is again a DOM-ish object with a start method that you must call to perform the operation. If you want to wrap this in a promise, you can use a structure like this:

function captureWebviewSelectionAsync(webview) {
//Wrap the capture method in a promise
return new WinJS.Promise(function (cd, ed) {
var op = webview.captureSelectedContentToDataPackageAsync();
op.oncomplete = function (args) { cd(args.target.result); };
op.onerror = function (e) { ed(e); };
op.start();
});
In this case, the result you care about us args.target.result in the oncomplete event, which is a Windows.ApplicationModel.DataTransfer.­DataPackage object as used with the Share charm. Calling its getView method will produce a DataPackageView whose availableFormats object tells you what it contains. You can then use the appropriate get* methods like getHtmlFormatAsync to retrieve the selection data itself. Note that if there is no selection, args.target.result will be null, so you’ll need to guard against that. Here’s some code, for instance, to copy the selection from one webview to another, using the function above:

function captureSelection() {
var source = document.getElementById(“webviewSource”);
var promise = captureWebviewSelectionAsync(source);

//Navigate the output webview to the selection, or show an error
var output = document.getElementById(“webviewOutput”);

promise.then(function (dataPackage) {
if (dataPackage == null) { throw “No selection”; }

var view = dataPackage.getView();
return view.getHtmlFormatAsync();
}).done(function (text) {
output.navigateToString(text);
}, function (e) {
output.navigateToString(“Error: ” + e.message);
});
}

Note that the captured selection is an HTML clipboard format that includes the extra information at the top before the HTML from the webview. If you need to extract just the straight HTML, you’ll need to strip off this prefix text up to <!DOCTYPE html>.

Generally speaking, captureSelectedContentToDataPackageAsync will produce the formats AnsiText, Text, HTML Format, Rich Text Format, and msSourceUrl, but not a bitmap. For this you need to use the third method, capturePreviewToBlobAsync, which again has a start method and complete/error events. The results of this capture (in args.target.result within the complete handler) is a blob object for whatever content is contained within the webview’s display area. Here’s another function to wrap that operation in a promise:

function captureWebviewBitmapAsync(webview) {
return new WinJS.Promise(function (cd, ed) {
var op = webview.capturePreviewToBlobAsync();

op.oncomplete = function (args) {
var ras = Windows.Storage.Streams.RandomAccessStreamReference;
var bitmapStream = ras.createFromStream(args.target.result.msDetachStream());
cd(bitmapStream);
};

op.onerror = function (e) { ed(e); };
op.start();
});
}

The result of the promise generated by this function is a blob, which you can use for a number of purposes. If you want to display it in an img element, you can use URL.createObjectURL on this blob directly. This means you can easily load some chunk of HTML in an offscreen webview (make sure the display style is not “none”) and then capture a blob and display the results in an img. Besides preventing interactivity, you can also animate that image much more efficiently than a full webview, applying 3D CSS transforms, for instance. You can also stream the blob in an upload, save it to a file, and so forth.

For other purposes, like the Share charm, you can call this blob’s msDetachStream method, which conveniently produces exactly what you need to provide to a data package’s setBitmap method. This is demonstrated in scenario 7 of the SDK’s HTML Webview control sample (though the sample neglects to use the datarequested event’s deferral correctly).

In part 3 I’ll share a few other closing bits on webview.


In Windows 8, an app written in JavaScript could host other HTML content–both local (in-package) and remote (http[s])–in an iframe element. In Windows 8.1, an iframe is still completely supported for in-package content (ms-appx-[web] URIs), but only supports secure remote content (https). For general hosting of web content (http), apps should be using the x-ms-webview element.

In this post and the few that follow, I wanted to share a few details about migrating code from iframe to webview. For a broader discussion of hosting web content, refer to Chapter 4 of my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition. Also see What’s New in Webview for Windows 8.1 on the Windows App Builder’s blog.

For in-package content using ms-appx-[web] URIs, you can continue to use iframes. If you want to switch to webview for any reason with ms-appx-web (you can’t use webview with ms-appx), then you should be able to make a straight change from <iframe src=”…”> to <x-ms-webview src=”…”>. Then you need to change any uses of postMessage to communicate with the iframe to webview.invokeScriptAsync (to call methods in the webview’s script) and the MSWebViewScriptNotify event (to pick up events raised from within the webview using window.external.notify).

For web content, the story gets a little trickier. If you try to direct an iframe in Windows 8.1 to an http:// address, you’ll get a message like this:

APPHOST9625: Unable to navigate to: ‘http://www.mysite.com/’. An iframe attempted to navigate to a URI that is not included in the ApplicationContentUriRules for this app. Use a x-ms-webview element to view the URI instead, or add the URI to the ApplicationContentUriRules section of the package manifest so that the iframe can navigate to it. (In Visual Studio, add this URI to the Content URIs tab of the Manifest Designer.)

Like it says, an iframe in 8.1 must use https for remote content, and then the app’s manifest must have an entry for that site on its Content URIs tab. So if you can use https:// for that site, then you can continue to host it in an iframe. If not, then, you need to use a webview.

With a webview, however, window.external.notify (to raise events from the webview) will work only when src is set to an https:// (and you have a content URI in the manifest) or ms-appx-web::// URI, but not http://.

That said, the webview element can be loaded from alternate source, which is one of the reasons why we have it. For one, you can also use ms-appdata URIs for a webview, either through its src attribute or its navigate method. This makes it possible to download content from the web or generate it dynamically, then render it within the webview. The caveat, however, is that with ms-appdata URIs, you can use invokeScriptAsync but not window.external.notify.

That said, if you load content into a webview using its navigateToString or navigateToLocalStreamUri methods, then you can also use window.external.notify. This means that if you want to load up downloaded or dynamically-generated content into a webview and have it raise events, then you either need to load the content into a memory string and call navigateToString, or you create a local stream provider (refer to the HTML webview control sample for a demonstration, as well as http://blogs.windows.com/windows/b/appbuilder/archive/2013/07/17/what-s-new-in-webview-in-windows-8-1.aspx).

In part 2 we’ll look at capturing webview content.


Another migration issue that I ran into with Windows 8.1 came out when changing the Windows 8 snapped view for the Here My Am! app in my book to variable view sizing. In particular, when I sized the view down to a width below 500px, I noticed that the fonts for h1, h2, and h3 elements no longer adjusted automatically with Windows 8.1/WinJS 2.0.

The reason for this is that the following block of CSS that is present in the WinJS 1.0 ui-dark.css/ui-light.css stylesheets was removed for WinJS 2.0:

@media screen and (-ms-view-state: snapped) { 
    h1, .win-type-xx-large {
        font-size:2 0pt;
        font-weight: 200;
        line-height: 1.2;
    }

    h2, .win-type-x-large {
        font-size: 11pt;
        font-weight: 600;
        line-height: 1.3636;
    }

    h3, .win-type-x-large {
        font-size: 11pt;
        font-weight: 400;
        line-height: 1.3636;
    }

    h1.win-type-ellipsis, .win-type-xx-large.win-type-ellipsis {
        line-height: 1.5;
    }

    h2.win-type-ellipsis, .win-type-x-large.win-type-ellipsis {
        line-height: 1.3636;
    }
}

Technically speaking, the WinJS told me that this was not a breaking change, per se, because the media query for -ms-view-state: snapped no longer fires at all in Windows 8.1, so that chunk of CSS wasn't be used. This is why it was deemed dead code and removed altogether. Personally, I didn't understand why they didn't change the media query to detect a similar view, but it's easy enough to rectify manually. Just copy these styles into your own CSS and place them inside a media query for < 500px narrow views, if you support them. That is, replace the @media in the code above with this:

@media screen and (orientation: portrait) and (max-width: 499px)

 

 


When I migrated one of my apps to Windows 8.1, I found that some code I had attached to the body.onload event wasn’t getting called. Windows.UI.WebUI.WebUIApplication.onactivated is the last thing that gets fired from the app host on startup (barring WinJS.Application.onready which is just queued after that but not related to a DOM event).

It turns out that there was a breaking change between Windows 8/WinJS 1.0 and Windows 8.1/WinJS2.0, which changed the exact sequence of the Windows and WinJS events (a sequence which was never specified nor guaranteed in the first place).

Note first that document.body.onload generally won’t work—one should use window.onload instead.

In WinJS 1.0, window.onload fires after WinJS.Application.onactivated. In WinJS 2.0 it fires before all the WinJS.Application events (including loaded, activated, and ready). My Win8 code was adding a window.onload handler within WinJS.Application.onactivatedt seeing it any longer. So I simply needed to move the addEventListener call earlier in the code and everything worked, but it took me a while to figure it out the cause.

 


In Windows 8 and WinJS 1.0 it was exceedingly difficult (though not impossible) to create a custom layout object for the WinJS ListView control. As such it was one area of WinJS that I never investigated!

What made it difficult was that the ListView worked entirely on absolute CSS positioning, which meant that the location of every item had to be individually calculated within the layout object’s code. This also caused various performance issues as well.

For WinJS 2.0 on Windows 8.1, the WinJS team rewrote the layout engine of the ListView to leverage relative positioning such that it could let the app host’s layout engine do most of the work. This both improved performance and made it orders of magnitude more easy to write a custom layout. In fact, the simplest layout object hardly needs to do anything at all, because the default behavior (via flexbox with block elements) produces a simple vertical layout just like you get when throwing a bunch of div’s into an HTML page.

From there you can start to build out other behaviors you want:

  • Non-vertical layouts
  • Virtualization support
  • Keyboard support
  • Drag and drop general visual indicators
  • Drag and drop reordering and positional indicators
  • Animations

To explore all the details, I have 17 pages in Appendix B, “WinJS Extras” in my free ebook’s second edition (preview), which includes a minimal vertical layout, a minimal horizontal layout, two dimensional layouts, and non-linear layouts. For the latter I borrowed some code from Mike Mastrangelo of the WinJS team that was used in a short demo at //build 2013. The appendix also includes a short video demonstration of this.

B-3 (circle layout)


As a short follow-up to the previous series of posts, I wanted to share a bit on detecting where a view is on the screen. That is, a view can be placed anywhere on the screen, which could be on either edge or in the middle (which is demonstrated in the Getting Around tutorial video.) For any given view, the ApplicationView object for which you obtain with Windows.UI.ViewManagement.ApplicationView.getForCurrentView(), its adjacentToLeftDisplayEdge and adjacentToRightDisplayEdge properties tell you whether the view is placed along a screen edge. If, also, the view is full screen, the isFullScreen property will be true as will both adjacent* properties.

It follows, then, that !isFullScreen && !adjacentToLeftDisplayEdge && !adjacentToRightDisplayEdge means that a view is split and is centrally placed.

 

 


It came to our attention recently through this forum post that there is a small bug in the Repeater control of WinJS 2.0 (Windows 8.1). I wanted to summarize my response to that post here.

The bug occurs when you change either the template or data property of the repeater and results in an enigmatic “NotFoundError” deep down inside the ui.js file of WinJS. It specifically occurs when the repeater’s template is set to a separately-declared WinJS.Binding.Template control. What happens is that when you change either template or data property of the control, it disposes its prior contents before rendering the new, but ends up trying to remove the same child node twice (hence the error).

Fortunately there are three ways to work around the bug:

  1. Instead of using a separately-declared template, place the template directly inline as the child of the repeater element.
  2. For a separately-declared template, use data-win-options=”{ extractChild: ‘true’ }” on the same element as data-win-control=”WinJS.Binding.Template”.
  3. Recreate the Repeater control whenever you change the template or data source (which is a more expensive operation of course).

See the original forum post for applicable code snippets.

[Correction: #2 above originally had WinJS.UI.Repeater, which should be WinJS.Binding.Template as modified.]


In Part 1 we learned about creating views, and in Part 2 saw the ProjectionManager API. Now we turn to the Windows.UI.ViewManagement.ApplicationViewSwitcher class, something that’s again demonstrated in the Multiple views sample in the SDK.

Those methods of that class—with some verbose names!—are:

  • switchAsync(id): (3 overloads) Switches to a given view in the same space as the calling one. If the target view is already visible elsewhere on the screen, however, this will simply change focus to that view. A value from ApplicationViewSwitchingOptions can be used to customize the transition: default (standard transition), skipAnimation (no transition), or consolidateViews (closes the calling view).
  • tryShowAsStandaloneAsync: (3 overloads) Shows the new view adjacent to the calling view, with options from ViewSizePreference to determine the desired state of the new and calling views: default, useLess, useHalf, useMore, useMinimum, useNone. You can experiment with these variations in scenario 1 of the sample.
  • disableShowingMainViewOnActivation: Prevents the primary view of the app from showing on subsequent activations, that is, when it’s appropriate to activate the app directly into a secondary view. Showing the primary view is always the default unless this method is called. This is shown in scenario 2 of the sample, with the actual call in js/default.js.
  • prepareForCustomAnimatedSwitchAsync: Tells the views whether to run default animations on a switch; by specifying the skipAnimation option, you can attach a completed handler to this promise to perform custom animations. See scenario 3 of the sample.

Because the details and variations of these APIs get rather complex, I’ll leave it to you to explore the sample directly. Note that in the main scenarios of the app you won’t find any calls to switchAsync—these are made in the secondary view (js/secondaryView.js) to switch back to the primary view. In each case the view closes itself as part of the switch, which isn’t a required behavior, of course.

To show the basic switching behavior, then, I’ve made a few small modifications to a copy of this sample in my second edition companion content. First, the button in the secondary view to switch back to the main view does not close the secondary one. Then I’ve added a button to scenario 1 of the main app to call switchAsync on the selected secondary view. You’ll see how it brings that view up in the same space as the original one, rather than alongside. If you switch back from the secondary view, the main view will appear in that space.

Things get interesting when you create an adjacent view first, then in the main app switch to a secondary view in the space. Then you’ll see two secondary views at once. If you switch to the main app in either, you’ll then find that the Switch to Main View button in the other secondary view just changes focus to the already-visible app. Otherwise you’d be seeing the main app view twice, which would be very confusing!

In summary, I hope that you’ll find some excellent scenarios in which to use multiple views. Given the newness of the feature, you have an opportunity right now for your app to really stand out if you’re an early adopter.