Earlier today, the first CTP of WPF/E was announced which enables some compelling AJAX scenarios by providing cross-platform and cross-browser scriptable media and vector graphics based on XAML markup. It is personally exciting to be working in this space, and see the technology address some items from my browser and scripting wish list that I blogged about back in May. There have been a few other blog posts on this announcement earlier today such as the ones posted by Scott Guthrie, Mike Harsh, and Shawn Burke.
I built a WPF/E-based Flickr application to demonstrate the confluence of HTML, XAML and scripting. The application uses the JSON services offered by Flickr to search for some images by tags, and then display the resulting set of pictures in a WPF/E powered presentation. Here is a screenshot which links to a live running sample. Be sure to visit the WPF/E page, and install the browser plugin before viewing the sample for now.
The sample shows off some basic usage of WPF/E elements, scripting against them, and animating them. The other interesting aspect of this sample is that I didn't write a single line of script. Instead all the code was written in c# and compiled using script#.
The latest release of Script# (0.2.0.0), also published today, contains a code-behind model that allows me to add c# code-behind to a page, and have that converted into script. I'll blog more about that in the coming days, but this post is mostly about WPF/E.
Here is a portion of my Scriptlet class, which contains c# code-behind for the page. The code in bold shows the scriptlet instantiating a WPF/E plugin instance with some XAML markup.
using System;
using System.DHTML;
using System.WPFE;
using ScriptFX.UI;
namespace PhotoViewer {
public sealed class PhotoViewerScriptlet : IDisposable {
private PhotoViewerControl _photoViewerControl;
...
public static void Main(Dictionary arguments) {
PhotoViewerScriptlet scriptlet = new PhotoViewerScriptlet(arguments);
}
private PhotoViewerScriptlet(Dictionary arguments) {
...
Button searchButton = new Button((DOMElement)arguments["SearchButtonElement"]);
searchButton.Click += OnSearchLinkClick;
WPFEPlayer player =
WPFEFactory.CreateWPFEPlayer((string)arguments["ID"],
(DOMElement)arguments["ParentElement"],
(string)arguments["MarkupURL"],
"Black", /* windowLess */ true);
_photoViewerControl =
new PhotoViewerControl(player, (string)arguments["FlickrKey"]);
...
}
private void OnSearchLinkClick(object sender, EventArgs e) {
string[] tags = _tagsTextBox.Text.Trim().Split(' ');
_photoViewerControl.Start(tags);
}
...
}
}
And here is some code from the PhotoViewerControl that wraps the WPF/E plugin and implements the functionality to make requests to Flickr and progress the presentation from one photo to the next.
internal sealed class PhotoViewerControl : IDisposable {
private WPFEPlayer _player;
private string _flickrKey;
private Image _photo1;
private TextBlock _title1;
private Storyboard _storyboard1;
private HTTPRequest _request;
private Photo[] _photos;
private int _nextPhoto1;
public PhotoViewerControl(WPFEPlayer player, string flickrKey) {
_player = player;
_flickrKey = flickrKey;
}
public void Initialize() {
_photo1 = (Image)_player.FindName("image1");
_title1 = (TextBlock)_player.FindName("title1");
_storyboard1 = (Storyboard)_player.FindName("storyboard1");
...
}
public void Start(string[] tags) {
Dictionary scriptTransportParams = new Dictionary();
scriptTransportParams["callbackParameterName"] = "jsoncallback";
string uri = String.Format(FlickrSearchURLFormat, _flickrKey, tags.Join("+"));
uri = HTTPTransport.CreateURI(uri, typeof(ScriptTransport), scriptTransportParams);
_request = HTTPRequest.CreateRequest(uri, HTTPVerb.GET);
_request.Invoke(OnRequestComplete, null);
}
private void OnRequestComplete(HTTPRequest request, object userContext) {
IHTTPResponse response = request.Response;
if (response.StatusCode == HTTPStatusCode.OK) {
PhotoSearchResponse searchResponse = (PhotoSearchResponse)response.GetObject();
_photos = searchResponse.photos.photo;
StartInternal();
}
}
private static string CreateFlickrPhotoURL(Photo photo) {
return String.Format(FlickrPhotoURLFormat, photo.server, photo.id, photo.secret);
}
private void ShowFirstPhoto() {
_photo1.Source = CreateFlickrPhotoURL(_photos[_nextPhoto1]);
_title1.Text = _photos[_nextPhoto1].title;
Window.SetTimeout(StartFirstTimeline, 1000);
_nextPhoto1 += 2;
if (_nextPhoto1 >= _photos.Length) {
_nextPhoto1 = 0;
}
}
private void StartInternal() {
_nextPhoto1 = 0;
ShowFirstPhoto();
Window.SetTimeout(ShowSecondPhoto, 8000);
}
private void StartFirstTimeline() {
_storyboard1.Begin();
Window.SetTimeout(ShowFirstPhoto, 14000);
}
...
}
One other thing to observe in the sample... the sample makes requests to the Flickr service directly without going through a server-side proxy. To make these cross-domain calls successfully, It uses <script> tags rather than XMLHttp to work against Flickr, and uses the JSONP support provided by the service. Script# provides a mechanism to work against this protocol transparently by hiding away the plumbing in the ScriptTransport class that is referenced in the code above. If this is of interest, drop me a comment, and I'll blog about it further.
The sample is packaged into the Script# msi that you can download to delve into the code. In addition to this sample, the msi contains the Script# compiler, and various other samples. There is also a document with a how-to guide on using Script# with a lot of new content, that should help you get up and running using the installed project templates.
Posted on Monday, 12/4/2006 @ 11:26 PM
| #
Script#