Table Of Contents
I should point out that this article starts out with me thinking it was going to be a blog post, nothing more, and in reality it is not much more than a blog post, but I have a rethink and although it is very small I think it may be useful to some folks, which is why I decided to release it as an article in the end. As I think that there may be some readers that take this the extra step and use it in their own apps.
So enough of the self flagellation, what does the thing attached actually do.
Well ok, here it is, for ages I have marvelled at the Web addin by CoolIris called "PicLens", which I have to say is the best add in I have ever seen. I have even tried to write my own version of this which myself and fellow coder Marlon Grech did for WPF, we did not do as good a job as CoolIris.
We called ours MarsaX, which you can look at right here.
Ours looks like this:
Whilst CoolIris PicLens looks like this:
Now as CoolIris PicLens is a browser addin, what makes it even more impressive is the fact that it runs in the browser. How the hell is this possible. I decided to have a look at whether it would be possible to host the CoolIris PicLens addin in my own WPF app, and to see if I could manipulate what was shown on the 3d wall, and that is what this small article is all about.
The only things you will need are:
- VS2008/VS2010
- .NET 3.5 SP1 or later
This section will explain how the demo WPF app hosts the CoolIris PicLens browser addin.
Flash (COM)
The very cool thing (at least I think so, there are folks who detest Flash but I love it), is that the CoolIris PicLens browser addin is just a standard Flash SWF file. That is kind of cool, as it means I can embed it in my own page. I know people hate Flash but as I stated I like it, and CoolIris has fully exposed its functionality via JavaScript. You can read more about how to embed the CoolIris PicLens addin using their documentation link, which you can find at http://www.cooliris.com/developer/reference/.
It is quite detailed. But for me, all I wanted to achieve was to be able to host the addin in my own web page which itself was hosted inside my own WPF app. So let's have a look into that, shall we.
Step 1: Creating some XAML to host the HTML page that will in turn host PicLens
This is by far the easiest part, all we have to do is create a WebBrowser
(.NET SP1) control, that we can use to host an arbitrary web page. This is the relevant XAML:
<Window x:Class="EmbeddedPicLensWpfApp.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="650" Width="650" Background="#ff121212">
<WebBrowser x:Name="browser"
Grid.Row="1" Width="650" Height="650" />
</Grid>
</Window>
And then in the code behind, all we have to do is set the WebBrowser
document to be our HTML page that is in turn hosting the CoolIris PicLens browser addin. This too is easily achieved as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace EmbeddedPicLensWpfApp
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
this.Loaded += Window1_Loaded;
}
void Window1_Loaded(object sender, RoutedEventArgs e)
{
String fullPath = System.IO.Path.Combine(
System.IO.Directory.GetCurrentDirectory(),
@"PicLensHostPage.htm");
browser.Navigate(
new Uri(fullPath));
}
}
}
Step 2: Creating the HTML
The moderately hard part of all of this is setting up the CoolIris PicLens addin. CoolIris actually exposes a express wall creator, but that uses an EMBED object tag, which is bit more rigid and less flexible that creating the CoolIris PicLens addin using JavaScript. I chose to use JavaScript as it offered me greater flexibility.
The entire page to host the CoolIris PicLens addin looks like this (I will explain some of this later):
<!---->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<head>
<style type="text/css">
Html, body
{
Overflow:auto;
}
#wall
{
background-color: #121212;
Overflow:auto;
}
</style>
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js">
</script>
<script type="text/javascript">
function LoadPicLensWithSearchWord(keyword)
{
try
{
var flashvars = {
feed: "api://www.flickr.com/?search="
+ keyword,
showEmbed: "false",
showSearch: "false"
};
var params = {
allowFullScreen: "true",
allowscriptaccess: "always"
};
swfobject.embedSWF(
"http://apps.cooliris.com/embed/cooliris.swf",
"wall", "600", "600", "9.0.0", "",
flashvars, params);
}
catch(err)
{
alert(err);
}
}
</script>
</head>
<body onload="javascript:LoadPicLensWithSearchWord('robots')"
bgcolor="#121212">
<div id="wall" >
-->
</div>
</body>
</html>
But for now, all you need to understand is that the CoolIris PicLens addin is a Flash SWF file and we can create an instance of it using JavaScript.
FlashVars
As you can see from the above example HTML file, there are really 2 main parts. One of them is the FlashVars, which is really a dictionary of configuration values, that can be used to configure the CoolIris PicLens addin.
Let me explain what I am setting there for the CoolIris PicLens addin.
feed
: Dictates what media stream to use (more on this later)
showEmbed
: Toggles the visibility state of the show embed button (I have it turned off)
showSearch
: Toggles the visibility state of the show search button (I have it turned off)
For a complete list of available options, you can check out the CoolIris PicLens addin documentation at the following URL:
http://www.cooliris.com/developer/reference/flashvars/
SwiftObject
The next thing that the JavaScript makes use of is the swfobject.embedSWF
library. This small but of JavaScript allows the embedding of Flash SWF files. This is a standard library and can be obtained from the following URL:
http://code.google.com/p/swfobject/wiki/documentation
You can see that it sets various options such as the URL to the SWF and various other bits and pieces such as height/width, etc.
Media Feeds
The more eagle eyed amongst you will notice that I am using the Flickr API, which has a URL similar to this "api://www.flickr.com/?search=Robots". Now according to the CoolIris PicLens addin documentation, you should be able to create your own custom RSS media stream.
This is described at http://www.cooliris.com/developer/reference/media-rss/. To do this, you would need to create an actual web site hosted in IIS, with an HTML file which hosted the CoolIris PicLens addin in it (similar to the example file included), and also create a RSS XML document which is also part of the IIS web site. The documentation suggests that you just also create a crossdomain.xml file which MUST also be located at the root of you IIS installation. I tried this and almost got it working, to the point I knew it was using my own RSS stream, but it still complained about a missing crossdomain.xml. I am not the most patient of people when it comes to the web, it just ain't my bag man, so I leave this as an exercise for the reader.
The demo app simply allows the user to type a keyword, which is then used against the Flickr API. This still obviously requires some interaction between the managed C# code and the browsers JavaScript, more on this in just a minute.
Browser Manipulation
As I just eluded, the attached code makes use of the Flickr media stream API. So all the attached demo code really does, is allow the user to input a new keyword and creates the appropriate Flickr media stream API URL, based on the new keyword, and then passes that to the CoolIris PicLens addin via a JavaScript call. This does mean we need some way of getting stuff from managed code (WPF in this case, though this would be the same in WinForms I would think), into the browser to call some JavaScript.
So let's have a look at that, it is fairly easy to do, we just need to use the System.Windows.Controls.WebBrowser.InvokeScript()
method. Here is how the demo app does this:
private void Search_Click(object sender, RoutedEventArgs e)
{
if (String.IsNullOrEmpty(txtKeyWord.Text))
{
MessageBox.Show("You need to enter a search word", "Error",
MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
browser.InvokeScript("LoadPicLensWithSearchWord", new Object[] { txtKeyWord.Text });
}
An Interesting Discovery
Along the way while I was looking for the best way to invoke JavaScript inside the WebBrowser, I came across a rather cool DLL/Namespace, which is as follows:
The Microsoft.mshtml
DLL can be referenced to give you access to some rather rich HTML interaction types.
Then you just need to include this namespace using mshtml;
and then look what you can do with the WebBrowsers document. You can basically do any of the typical DOM things you would expect like adding child nodes, adding attributes, injecting JavaScript, etc.
This however is not used in the demo app, I just thought it was cool, and could be very useful. You can read more about the Microsoft.mshtml
DLL and its exposed types over at:
If you want to know more about this approach, I found several very good links:
- Injecting JavaScript (by Ayende, so you know it rocks) from managed code
- Interacting with the Web Browser Control
- Alkampfer's Place (blog)
Mark Of The Web
One interesting thing that nags the crap out of me is when I open a web page up and I get the security bar. You may be interested to know that you can get rid of this, by using some special markup in your web content. I have done just that. This is called "Mark Of The Web", which you can read more about right here: http://msdn.microsoft.com/en-us/library/ms537628(VS.85).aspx.
This is what MSDN has to say about it:
"The Mark of the Web (MOTW) is a feature of Windows Internet Explorer that enhances security by enabling Internet Explorer to force Web pages to run in the security zone of the location the page was saved from—as long as that security zone is more restrictive than the Local Machine zone—instead of the Local Machine zone. The role of the MOTW is more prominent with Microsoft Internet Explorer 6 for Windows XP Service Pack 2 (SP2) because of increased security restrictions in the Local Machine zone. When you are developing Web content, the MOTW enables you to test your active HTML documents in the security zone where you intend the pages to run. Adding the MOTW to your Web pages also enables you to fully test their compatibility with users' security settings."
Again, this is from http://msdn.microsoft.com/en-us/library/ms537628(VS.85).aspx.
The effect of this is quite apparent, for example here is the attached demo apps HTML file without the special "Mark Of The Web" markup.
Notice the annoying nag notice.
And here it is again, this time with the special "Mark Of The Web" markup supplied in the demo apps HTML file.
All you have to do is add this to your HTML file.
<!---->
The Finished Thing
Here is a screen shot of the finished thing working inside a WPF Window:
Anyways folks, that is all I have to say for now. I know this is a very small article (and would have made a nice blog posting), but I just felt that some of you may find it useful, and I would rather a wider audience receive something useful.
So apologies that this article is so small and not my normal mammoth of an article (remember mammoths died out).
History
- 24th March, 2010: Initial post