Archive for Silverlight

Silveright FlyoutPanel: By Ola Karlsson

Note: This article is submitted by Ola for Silverlight 2 Beta 2 - Articles Competition.Thanks a lot, Ola! Hello All, Please drop a comment if you like it.

Contents

  • Overview
  • First there was creation
  • Blending it up
  • Back to where we started (in VS2008)
  • But I want to see the rest of my XAML UI
  • Take control
  • Two worlds come together
  • Conclusion
  • About the Author (Ola Karlsson)

Download : SL2Beta2FlyoutDemo_SubmissionVer.zip (419KB)

Overview

In this article we’ll be looking at the concept of adding interactive Silverlight elements to existing Web pages to provide added rich features. We’ll be looking at creating an animated Silverlight panel which will slide in from the side of the browser window when a button is clicked.

The somewhat tricky bit is that we want the panel to lie on top on the normal HTML content of the page and when when the Silverlight UI is slid out we want to be able to interact with the HTML instead.
Why did I decide on this topic:

Working as an asp.net developer and having a keen interest for web development in general, I find this way of using Silverlight quite interesting but I have also found that it is not covered very much on the web at the moment.

Concepts and tools we’re using

Tools

  • Visual Studio 2008 with the Silverlight 2 Beta 2 tools installed
  • Blend 2.5 June Preview
  • C#

Concepts

  • Visual State Manager
  • Silverlight/Managed Code - DOM/JavaScript interaction
  • XHTML/CSS
  • Controlling the SL Plugin

First there was creation

First things first, open Visual Studio and create a new Silverlight 2 project/solution to get the full structure including a asp.net test project.

I created a new .Net 3.5, C#, Silverlight project and chose to add a Web Application project to test my Silverlight UI in.

(I only chose “Web Application” because that’s what I’m used to working with, you could as easily use a “Web Site” project.)

Once the IDE has finished loading our new solution, we want to open the page.xaml file in Blend, so we can create the Silverlight UI interface.

Blending it up

In Blend, we now create our Silverlight interface.

For this demo I will simply use the default LayoutRoot grid and change the size to 200×300. However to create the effect we want, we then set the size of the usercontrol to 220×300.

Next drag on a button which we give a size of 20×40, then drag the button into position just outside the upper right corner of the grid. This is probably not the most elegant layout solution but as this article is about browser interaction and animating with Visual State Manager and not about advanced XAML layout, it’s good enough.

As a bit of a nice touch, we’ll make the background a dark semi transparent colour so that one implemented we’ll still be able to see a hint of the HTML content underneath it. To finish our UI, we also drag on a TextBlock, I then gave the controls the amazingly colourful names of “myText” and “myButton”. I also changed the foreground colour and font properties for the TextBlock so it will stand out a bit. This gave me the following:


<UserControl x:Class="SL2Beta2FlyoutDemo.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Width="220" Height="300"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">
<Grid x:Name="LayoutRoot" Background="#35000000" RenderTransformOrigin="0.5,0.5" Width="200" HorizontalAlignment="Left">
<TextBlock Height="35" HorizontalAlignment="Stretch"
Margin="41,42,45,0" VerticalAlignment="Top" FontSize="22"
Foreground="#FFE25B1C" Text="TextBlock" TextWrapping="Wrap"
x:Name="myText" FontWeight="Bold" />
<Button HorizontalAlignment="Right" Margin="0,-1,-20,0"
VerticalAlignment="Top" Content="&amp;amp;gt;" Click="myButton_Click"  x:Name="myButton"
Width="20" Height="40"/>
</Grid>
</UserControl>

Next we move on to use the new Visual State Manager to create our slide out, slide in effect. The Visual State Manger (VSM) is located in the upper left corner and lets us define visual “states” for our UI elements and group these states into “State Groups”. For our UI I defined a State Group called SlideStates and then added two States name Out and In, to it. Note that in VSM there is always a Base State which defines the default state/view in addition to any states you create.

Our next step is to define the base state of our UI, in our case we want create an effect where the UI comes sliding in from the side.

To accomplish this we first select the Base state, then we make sure the correct element is selected (the LayoutRoot grid) and finally we make any necessary adjustments. Since our UI is to slide in from out of view, it needs to start outside the screen, hence we set the x value for the LayoutRoot grid to a minus of its own width (in this case -200). This will set the main section of our UI off the screen but leaves the button on screen.

Next we move on to defining our own states, as the Out state in fact will be the same as the Base state we don’t actually need to make any changes to it.

In the case of the “In” state however, we want the UI to be on screen. As per when we set up the base state, we select the state, then the element and finally make any adjustments. In this case, to bring the LayoutRoot grid back onto the screen we simply set the X value back to 0.

We can now preview the behaviour by clicking on out different states and seeing what happens. However if we were to run this as it currently is, the transition between the states would be pretty much nonexistent, so to get a nice animated transition between the states we use the “Transition Duration” in VSM, here I’ve set it to 0.4 of a second, meaning that, 0.4sec is the time it will take to transition from one state to another.

We could also add more specific transitions of we wanted by using the Add Transition buttons, however for our simply demo, the Default Transition setting works perfectly fine.

Back to where we started (in VS2008)

With those final adjustments in Blend, it’s now time to go back to Visual Studio (VS), so make sure you have saved the changes and go back into VS, when asked if you want to reload the XAML, answer yes. NOTE: When the design surface in VS loads it will display and error message and won’t show the UI we created in Blend, this is a known bug and the good news is that the project will actually still build and when it’s run in the browser it should work fine.

So we now move from the world of XAML into the world of HTML, CSS and JavaScript. For this demo, I’m using the automatically created test page SL2Beta2FlyoutDemoTestPage.aspx, first step is to get the Silverlight plugin on top of the HTML. And to accomplish this we need to edit the default setup somewhat.

We’ll do it in a few steps:

1. To start with I’ve changed the size of the plugin, building a full Silverlight app the size of the Silverlight plugin is usually set to 100% width and height, however if we want to use it together with the HTML, we need to change it to match the actual size of your XAML/Silverlight element. We will also revisit these setting later when creating our slide in, slide out effect. This gives us the following:


<asp:Silverlight ID="Xaml1" runat="server"
Source="~/ClientBin/SL2Beta2FlyoutDemo.xap"
MinimumVersion="2.0.30523" Width="220" Height="300" />

2. Next we add another <div> tag with some HTML content, just after the div that holds the Silverlight plugin. (I’ve also set the background of the page to a gray colour so we can see better what’s happening with our Silverlight plugin).


<body style="height:100%;margin:0;background-color:#CCCCCC">
<form id="form1" runat="server" style="height:100%;">
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<div id="SLDiv" >
<asp:Silverlight ID="Xaml1" runat="server"
Source="~/ClientBin/SL2Beta2FlyoutDemo.xap"
MinimumVersion="2.0.30523" Width="220" Height="300" />
</div>
<div id="contentDiv" >
<h2 style="text-align:center;">Lorem ipsum dolor sit amet</h2>
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nam vestibulum lacinia tortor eros.</p>
<p>Sed ornare ullamcorper lacus. Ut vel risus. Vestibulum laoreet ligula et, per inceptos himenaeos. Nulla ipsum. Nullam metus. </p>
<p>Sed nisl nisi, sagittis non, ullamcorper ac, . Aliquam erat volutpat. In hac habitasse platea dictumst. Nunc vehicula enim et justo. </p>
</div>
</form>
</body>

All this will do however is to add some Content text on the page after our Silverlight plugin. You can note thought that we now can see the first part of “out” UI on the page!

3. To actually get the Silverlight area on top of the other content we turn to the power of CSS, and make the following changes:


<div id="SLDiv" style="position:absolute; left:0; top:0; z-index:2;">
<div id="contentDiv" style="position:absolute; top:0; left:0; z-index:1;">

Basically, we’re using CSS to lock the two divs in absolute positions and then we use the z-index attribute to layer them on top of each other (higher number = higher up in the stack). These changes give us quite a different result, where the Silverlight plugin is layered on top of our HTML content.

Now some of you might be saying, “Hey, what happened the transparency we set back in Blend?”
Well, what we did in Blend was that we set the XAML to be semi transparent, but to get the actual plugin transparent there’s a couple of things we need to change in how we set up the plugin on our page. Namely we need to add the two new parameters, Windowless and PluginBackground to the Silverlight control:


<asp:Silverlight ID="Xaml1" runat="server"
Source="~/ClientBin/SL2Beta2FlyoutDemo.xap"
MinimumVersion="2.0.30523" Width="220" Height="300"
Windowless="true" PluginBackground="Transparent" />

If we now run our page again, you should find that the Silverlight plugin area is no longer visible, so where’s our UI? Well all is as it should, as you might recall, the base state for our XAML UI is actually to start off page, hence we can’t see it!

However, to make things a bit easier for ourselves while working with the plugin, we’ll change the PluginBackground to Black for now, so we can see the plugin area.

But I want to see the rest of my XAML UI

Ok fair enough, this is all very exciting but I’m sure by now most of you want to see if this actually works?!
Lets get coding! So to prove to you that this actually works, we’ll go over to VS and open up the code behind file for our XAML file “Page.xaml.cs”. Just as a quick test we make the following change to the Page method:


public Page()
{
InitializeComponent();
VisualStateManager.GoToState(this, "In", true);
}

All this does is that it tells the VSM, that our current control (this) should go to the state “In”, the final boolean value decides whether a transition should be used. If you now go ahead and compile the solution and run the  page, you should see our XAML UI slide in from the left hand side in all its glory! (Minus the transparency of course but we’ll get back to that later)

So how do we control this? We don’t want it coming in when the page loads! Well for that we need to go back into code land.

Take control

Step one of taking control of our animated UI is to create a new event handler for out button, open the XAML view of Page.xaml, in the tag for the button, add a Click event:

Next right click on the new event and select “Navigate to Event Handler”, this will take us into the code behind file straight to the newly created event handler. Once there, simply move the line for the VSM interaction from the Page method into the new event handler. In the code behind make the changes to make it look like the following code:


public partial class Page : UserControl
{
String state;
public Page()
{
InitializeComponent();
state = "out";
}
private void myButton_Click(object sender, RoutedEventArgs e)
{
if (state == "out")
{
VisualStateManager.GoToState(this, "In", true);
state = "in";
myButton.Content = "<";
}
else
{
VisualStateManager.GoToState(this, "Out", true);
state = "out";
myButton.Content = ">";
}
}
}

Basically we’ve set up a variable to hold the current state of the control, then we’re using the Click event handler to slide the UI in or out based on the value in out variable. Just as an extra touch I also change the content (text) of the button to reflect which direction it will go when clicked.

So if we now set the PluginBackground to Transparent, we’re done right?!

We’ll, almost, the problem we’ll run into with our project that way it is currently. Is pretty plain to see while we don’t have the PluginBackground set to Transparent, as the Silverlight plugin is layered on top of our HTML, it stops us from interacting with the HTML below. Setting the PluginBackground will let us see what’s underneath, but as I’ve been pointing out to people when discussing these kind of implementations, just because you can see it doesn’t mean you can click it!

So how do we solve this problem? In steps JavaScript to save the day!

Two worlds come together

To solve this issue, we’ll use JavaScript to change the size of the Silverlight plugin container.

First we need to make a couple of adjustments to the Silverlight plugin tag, we’re adding a OnPluginLoaded event which I’ve set up to call the JavaScript function “PluginLoaded”. That function will be used to get a JavaScript reference to the Silverlight plugin object.

We also set the width of the plugin to 20(the width of our button), this so that our button appears but the rest of the Silverlight area isn’t hindering us from interacting with the HTML. As we’re not changing the height there will be a narrow strip left but if we really wanted to we’d just change that as well.

Next we go into our Page.xaml.cs file and add a “using System.Windows.Browser; ” then modify our Click event handler to what’s shown below.

Then once we have the System.Windows.Browser using statement in place, we can use HTMLPage.Window to get a reference to the browser window and thereby interact with it from our managed code!  CreateInstance, simple calls and executes the JavaScript methods defined.


private void myButton_Click(object sender, RoutedEventArgs e)
{
if (state == "out")
{
VisualStateManager.GoToState(this, "In", true);
HtmlPage.Window.CreateInstance("GrowHorisontal");
state = "in";
myButton.Content = "<";
}
else
{
VisualStateManager.GoToState(this, "Out", true);
state = "out";
myButton.Content = ">";
HtmlPage.Window.CreateInstance("DelayedShrink");
}
}

Next step is to add the necessary JavaScript functions to the header section of out aspx page:


<head runat="server">
<script type="text/javascript">
var slCtl = null;

function pluginLoaded(sender)
{  // Get a refernce to the Silverlight plugin when the page loads
slCtl  = sender.get_element();
}
function GrowHorisontal()
{
//Grow the width
slCtl.style.width='220px';
}
function DelayedShrink()
{
//Delay the call to the actual shrink function to let the animation finish before shrinking the area
setTimeout(ShrinkHorisontal, 500);
}
function ShrinkHorisontal()
{  //Shrink the area
slCtl.style.width='20px';
}
</script>
<title>Test Page For SL2Beta2FlyoutDemo</title>
</head>

Pheew, well good news is we’re almost done!

The only real thing that remains is to set the PluginBackground to Transparent and we’re done, if you’ve made it this far, give yourself a pat on the shoulder from me, well done for sticking with it. :)

The final thing I did was to set the background colour of the HTML body back to white to better see the Silverlight UI.

Conclusion

Using the new Visual Sate Manger we were able to very easily create a animated Silverlight and using JavaScript and CSS we dynamically slid the Silverlight on top of the HTML without hindering interaction with the HTML.
References:

About the Author (Ola Karlsson)

After a misspent past as a scuba diving instructor in various places around the world. This blue eyed, blond, 6,2” Swede is now living and working in the land down under as a full time Asp.Net developer for a medium sized Microsoft Gold Certified Partner specialising in Moss and custom Asp.Net solutions.

Silverlight and Flash Interoperability using HTML Bridge and ExternalInterface API: By Jonas Follesø

Note: This article is submitted by Jonas for Silverlight 2 Beta 2 - Articles Competition.Thanks a lot, Jonas! Hello All, Please drop a comment if you like it.

Contents

  • Introduction
  • Flash ExternalInterface API
  • Silverlight HTML Bridge
  • Calling Flash from Silverlight
  • Calling Silverlight from Flash
  • Silverlight web cam support using Flash interoperability
  • Summary

Download :SilverlightFlashIntegrationWeb.zip (1.43  MB)

Introduction

Silverlight and Flash have many similarities. Both are browser plug-ins that enables web developers to build richer internet applications. You can choose to have Silverlight or Flash occupy the entire screen and build all off the application, or you can have Silverlight or Flash embedded as small islands on your HTML page.
When you decide to include Silverlight or Flash on your page there are many scenarios where you may want to enable the application to interact with the rest of your page. You may want to read data from the HTML page, or update certain HTML elements at runtime. Perhaps you are gradually enhancing an existing web application and need to leverage existing JavaScript code running on the page. In these scenarios you need to leverage the browser integration model supported by Silverlight or Flash to communicate between your application and the hosting HTML page.

In this tutorial I’ll walk you through the basics of browser integration, and how you can make Silverlight and Flash talk to each. The tutorial starts off with some basic examples of browser integration in Flash and Silverlight, and move on to show how to enable webcam support in Silverlight using Flash.

Flash ExternalInterface API

Flash has been around for a long time and there have been different techniques to communicate between Flash and the. The “Flash JavaScript Integration Kit” is one example of such a framework. In Flash 8 Adobe included an official API to enable integration between your Flash application and the browser called the “ExternalInterface API”. The API consists of two main functions: the “addCallback” function to expose an ActionScript function to the web page, and the “call” function to invoke JavaScript functions on the web page.

The following code shows how to create a simple ActionScript function and expose it using the ExternalInterface API:


ExternalInterface.addCallback("saySomething", saySomething);

function saySomething():String
{
return "Hi, I'm Flash!";
}

To call the function from JavaScript you need to get a reference to the Flash player on the HTML page, and simply call the “saySomething” function:


function callFlash()
{
// Get a reference to the player
var player = null;

if (document.all)
player = document.all("flashPlayer");
else
player = document.getElementById("flashPlayerEmbedded");

alert(player.saySomething());
}

If you want to call a JavaScript function from ActionScript you simply use the “call” function on the ExternalInterface API:


function clicked(eventArgs:Object):void
{
ExternalInterface.call("saySomething", "What's up? I'm calling from Flash!");
}
this.btnSave.addEventListener("click", clicked);

Off course you also need to have the “saySomething” JavaScript function defined on your page:


function saySomething(something)
{
alert(something);
}

As you can see the ExternalInterface API makes it really easy to communicate between Flash and the browser. This first example use simple strings as parameters, but you can pass any ActionScript type across the ExternalInterace API. That being said, in many cases passing strings is all you need as you can format your data using JSON or XML.

Silverlight HTML Bridge

The browser integration layer in Silverlight 2 is often called the “HTML Bridge”, and enables you to interact with both HTML elements as well as JavaScript code running in the browser. This opens up for several interesting integration scenarios, some not even using Silverlight for the user interface at all.
At the core of the HTML bridge is the “ScriptObject”, which objects such as HtmlObject, HtmlPage, and HtmlElement derive from. To invoke the “saySomething” function from our previous example you simply write:


HtmlPage.Window.Invoke("saySomething", "Can you see the light? The Silverlight!");

The HTML bridge contains a lot of functionallity in addition to basic function invocation. It contains functionalliy to grab a HTML DOM Element and change it’s properties and style attributes. You can expose .NET types to the JavaScript runtime and then create new instances of the type from JavaScript. You can also expose instances of .NET objects to the JavaScript and directly interact with the object.

For a good introduction to interaction between HTML and managed code I recommend checking out the Silverlight quick starts at http://www.silverlight.net/QuickStarts/Dom/. The focus on this article will be on Silverlight 2 to Flash integration, and will not go in-depth on the Silverlight HTML Bridge.
Calling Flash from Silverlight

The Flash ExternalInterface API and the Silverlight HTML gives us the building blocks we need to start passing data between a Silverlight 2 and Flash application running on the same page. For this example we’re going to create a simple user interface to edit a person in both Flash and Silverlight. The interface looks something like this:

Calling Flash from Silverlight

The Flash ExternalInterface API and the Silverlight HTML gives us the building blocks we need to start passing data between a Silverlight 2 and Flash application running on the same page. For this example we’re going to create a simple user interface to edit a person in both Flash and Silverlight. The interface looks something like this:

In the example I want to show how to call Flash from Silverlight, and Silverlight from Flash. We’ll start by covering how to read a Person object from Flash into Silverlight when the user clicks the “Load from Flash” button.

The first thing we need to is create an ActionScript function to get a Person object from the Flash application. Both JavaScript and ActionScript are following based on the ECMAScript specification, and support object literals. This is a straight forward syntax to create new objects on the fly. For more information check out object literals in ECMAScript check out http://en.wikipedia.org/wiki/Object_literal.


ExternalInterface.addCallback("getPerson", getPerson);
function getPerson():Object
{
return { name: txtName.text, email: txtEmail.text, url: txtUrl.text };
}

The object returned contains three properties, name, email and url. To get the object from Flash into Silverlight we use a little JavaScript helper function:


function loadFromFlash()
{
var flash = getFlash();
if(flash)
{
return flash.getPerson();
}
}

The final piece is the C# code to handle the button click and call the JavaScript helper function. The code calls the JavaScript function and gets a generic ScriptObject back. This object contains a ConvertTo-method that tries to convert a script object to a .NET type. To hold the person data we’ve created a simple .NET person object. The property names have to match in order for the conversion to successes:


public class Person
{
public string name { get; set; }
public string url { get; set; }
public string email { get; set; }
}
private void btnLoad_Click(object sender, RoutedEventArgs e)
{
ScriptObject scriptObject =
(ScriptObject)HtmlPage.Window.Invoke("loadFromFlash");

var person = scriptObject.ConvertTo<Person>();
txtName.Text = person.name;
txtEmail.Text = person.email;
txtUrl.Text = person.url;
}

When we run the application we can enter data in the Flash application, and hit the “Load from Flash” button to update the Silverlight application. The next step is the “Save to Flash” functionality. When the user clicks the “Save to Flash” button we want to push the .NET object from Silverlight to Flash. The HTML bridge supports passing .NET objects from Silverlight to JavaScript by marking an object as scriptable:


[ScriptableType]
public class Person
{
[ScriptableMember]
public string name { get; set; }

[ScriptableMember]
public string url { get; set; }

[ScriptableMember]
public string email { get; set; }
}

Once the type is marked as scriptable we can pass it to any JavaScript function invoked using the HTML bridge. In theory we should be able to pass this type along to Flash, but for some reason the Flash plug-in is throwing an exception when you try passing it a scriptable type. There are several possible workarounds. One option is to pass the object to JavaScript, and then write JavaScript code to create a new object before passing on to Flash. Another option is to use the Silverlight JSON serializer and pass the object as a JSON-string to the JavaScript function, and then evaluate the string before passing it as a JavaScript object to Flash. I’ve included sample code for both solutions.

The first option is to pass the object directly to the JavaScript function, and then create a JavaScript object on the fly before passing it to Flash:


private void btnSave_Click(object sender, RoutedEventArgs e)
{
var person = new Person();
person.name = txtName.Text;
person.email = txtEmail.Text;
person.url = txtUrl.Text;
HtmlPage.Window.Invoke("saveToFlash", person);
}

The “saveToFlash” function creates a new JavaScript object before passing it along to Flash. The optimal solution would be to pass the object straight to Flash:


function saveToFlash(person)
{
var flash = getFlash();
if(flash)
{
var jsPerson = {
name: person.name,
email: person.email,
url: person.url
};
flash.setPerson(jsPerson);
}
}

The ActionScript function to update the Flash UI is really straight forward:


ExternalInterface.addCallback("setPerson", setPerson);

function setPerson(person:Object):void
{
txtName.text = person.name;
txtEmail.text = person.email;
txtUrl.text = person.url;
}

The second approach for passing our Person object from Silverlight to Flash is to serialize it to JSON before passing it to the browser. JSON stands for “JavaScript Object Notation”, and is a way to represent a full object graph as a string. Basically it’s just a string containing object literals, which can be evaluated by the JavaScript engine to build an object three. Silverlight has built-in support for JSON serialization, so implementing this is fairly straight forward. We’re going to use the DataContractJsonSerializer class, which is located in the System.ServiceModel.Web assembly under the System.Runtime.Serialization.Json namespace. We also need to reference the System.Runtime.Serialization assembly to get the DataContract and DataMember attributes we need to tag our Person object with.

After adding the DataContract and DataMember attributes our Person object now looks like this:


[DataContract]
public class Person
{
[DataMember]
public string name { get; set; }

[DataMember]
public string url { get; set; }

[DataMember]
public string email { get; set; }
}

Using C# 3.5 extension methods you attach a “ToJson”-method to all objects for an easy way to serialize any object. An extension method is basically a public static method in a static class, where the first parameter has the “this” keyword:


public static class Extensions
{
public static string ToJson(this object obj)
{
using(MemoryStream ms = new MemoryStream())
{
DataContractJsonSerializer serializer = new
DataContractJsonSerializer(obj.GetType());

serializer.WriteObject(ms, obj);
ms.Position = 0;
using(StreamReader reader = new StreamReader(ms))
{
return reader.ReadToEnd();
}
}
}
}

So in order to pass the JSON –serialized Person object to our JavaScript function we simply need to call the “ToJson” method on the person object:


private void btnSave_Click(object sender, RoutedEventArgs e)
{
var person = new Person();
person.name = txtName.Text;
person.email = txtEmail.Text;
person.url = txtUrl.Text;
HtmlPage.Window.Invoke("saveToFlash", person.ToJson());
}

We only need a minor change in the JavaScript function, where we evaluate the JSON string to build a JavaScript object we can pass to Flash:


function saveToFlash(person)
{
var flash = getFlash();
if(flash)
{
var jsPerson = eval('(' + person + ')');
flash.setPerson(jsPerson);
}
}

Calling Silverlight from Flash

So far we’ve covered how to call ActionScript functions from Flash. Now we want to do the opposite, call Silverlight functions from ActionScript. The first thing we want to implement is the “Load from Silverlight”-button. When the user clicks the button in Flash we want to call Silverlight to get our Person object. To do that we add a new method to your Silverlight application, as well as mark the user control as a scriptable type, and the method as a scriptable member:


[ScriptableType]
public partial class Page : UserControl
{
[ScriptableMember]
public string GetPerson()
{
var person = new Person();
person.name = txtName.Text;
person.email = txtEmail.Text;
person.url = txtUrl.Text;
return person.ToJson();
}
// Additonal code removed from example...
}

We also need to expose an instance of the user control to the browser so that we can call the GetPerson-method from JavaScript:


public Page()
{
InitializeComponent();
HtmlPage.RegisterScriptableObject("Page", this);
}

The next thing we need to do is to create a JavaScript function to be called from Flash to get the Person object from Silverlight:


function loadFromSilverlight()
{
var silverlight = getSilverlight();
if(silverlight)
{
var jsPerson = eval('(' + silverlight.content.Page.GetPerson() + ')');
return jsPerson;
}
}

To call the Silverlight method we get an instance of the Silverlight control and then access its content property. For each object we’ve registered as scriptable we can access it based on its key. The Page was registered with the key “Page”, so to syntax to call the GetPerson-method looks like this:


silverlight.content.Page.GetPerson()

The final step is to add the ActionScript code to handle the Flash button click event and call the JavaScript function:


function loadClicked(eventArgs:Object):void
{
var person = ExternalInterface.call("loadFromSilverlight");
txtName.text = person.name;
txtEmail.text = person.email;
txtUrl.text = person.url;
}
this.btnLoad.addEventListener("click", loadClicked);

Now that we can load from Silverlight to Flash we want to be able to save. The steps involved should be familiar by now. We start by adding a scriptable method on the Silverlight page:


[ScriptableMember]
public void SetPerson(ScriptObject scriptPerson)
{
var person = scriptPerson.ConvertTo<Person>();
txtName.Text = person.name;
txtEmail.Text = person.email;
txtUrl.Text = person.url;
}

The only important thing is the parameter type. We accept a ScriptObject which we convert to a Person object, before updating the UI. We also need to add some JavaScript to call from Flash:


function saveToSilverlight(person)
{
var silverlight = getSilverlight();
if(silverlight)
{
silverlight.content.Page.SetPerson(person);
}
}

In this example there is no type conversion, we’re passing the ActionScript person object straight to the SetPerson-method. We also need to add a click event handler for the Flash button:


function saveClicked(eventArgs:Object):void
{
ExternalInterface.call("saveToSilverlight",
{
name: txtName.text,
email: txtEmail.text,
url: txtUrl.text
});
}

Again we use object literals to create an on-the-fly person object that we pass to the JavaScript function.

Silverlight web cam support using Flash interoperability

Now that we’ve seen how we can integrate Silverlight and Flash using the HTML Bridge and ExternalInterface API it’s time to do something (somewhat) useful. We’re going to enable some basic webcam support in Silverlight using Flash interoperability.

Even though Flash and Silverlight have much of the same functionality, there are certain things you cannot do on both platforms. Local access of webcam and microphone is one example of something that is only possible in Flash. The way webcams work in Flash is through a camera–object. You attach the camera object to a video-object do display the video stream. The ActionScript code to create a camera and video looks like this:


var cam:Camera = Camera.getCamera();
cam.setMode(320, 240, 12);
var video:Video = new Video(320, 240);
video.attachCamera(cam);
this.addChild(video);

When you run your Flash application you’ll see a dialog asking if you want to give permission to access webcam and microphone. If you give the permission the video from your webcam should show on your screen.

With the video showing on screen, the next step is to add expose some ActionScript functions to take a snapshot of the video, and send the snapshot back to Silverlight. Flash supports drawing a frame from the video-object into a BitmapData-object. Using the open source as3corelib ActionScript extensions we can use a PNG-encoder to encode the BitmapData into a byte array. This byte array can be encoded as a Base64 string and passed to the browser using the ExternalInterface API:


import com.adobe.images.PNGEncoder;
import com.dynamicflash.util.Base64;

flash.system.Security.allowDomain("*");
ExternalInterface.addCallback("takeSnapshot", takeSnapshot);

function takeSnapshot():String
{
var bmd:BitmapData = new BitmapData(video.width, video.height,
false, 0x00CCCCCC);
bmd.draw(video, new Matrix());

var pngBytes:ByteArray = PNGEncoder.encode(bmd);
return Base64.encodeByteArray(pngBytes);
}

Using the code above we have a way to call our Flash application to get a Base64 encoded PNG image as a string. This string can be passed to Silverlight, which can decode the Base64 data into a byte array, and load the PNG image into a BitmapImage that can be used as the source of a Silverlight Image control. The JavaScript function to call Flash and pass the string back is really simple:


function takeSnapshot()
{
var flash = getFlash();
if(flash)
{
return flash.takeSnapshot();
}
}

The click event handler for the “take snapshot”-button calls the JavaScript function to get the Base64 encoded PNG image, decode it and set it as the source of a Silverlight image control:


void btnTakeSnapshot_Click(object sender, RoutedEventArgs e)
{
string base64Image = (string)HtmlPage.Window.Invoke("takeSnapshot");
if (base64Image != string.Empty)
{
byte[] imageData = Convert.FromBase64String(base64Image);
MemoryStream ms = new MemoryStream(imageData);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(ms);
ms.Dispose();

snapshot.Source = bitmapImage;
}
}

Silverlight supports both JPG and PNG images, and the image returned from Flash is PNG encoded. All we have to do on our side is turn the Base64 string into a byte array, load it into a MemoryStream, and then load the MemoryStream into a BitmapImage. A BitmapImage can be used as the source for any Silverlight Image control.

Now that we got the PNG image in Silverlight we can do what ever we want with it. We can upload it to the server, we can use it in Silverlight animations, or perhaps for cassual games like a sliding photo puzzle.

Summary

Both Silverlight and Flash have APIs to bridge between the plug-in and the browser. Using this you can make Flash and Silverlight talk, using JavaScript as the bridge between the technologies. I hope this tutorial has given you a good introduction to both Silverlight and Flash browser integration, and how it can be used to bridge the technologies. The webcam example shows how we can use this technique to achieve something quite interesting. This is by no mean a substitute for a proper webcam API in some future Silverlight version, but it gives you some basic webcam support you can play with. I will definitely be posting more about webcam support in Silverlight, and some examples of what you can use it for.

All the code is included with this article, and is licensed under the MS-Pl license. The article is copyrighted under the creative commons license.

Connecting Drupal and Silverlight: By Matt Serbinski

Note: This article is submitted by Matt Serbinski for Silverlight 2 Beta 2 - Articles Competition.Thanks a lot, Matt! Hello All, Please drop a comment if you like it.

Connecting Drupal and Silverlight
Connecting Drupal and Silverlight

Contents

  • Introduction
  • Custom Module for Drupal
  • Silverlight application
  • About the Author (Matt Serbinski)

Download ~

Introduction

Drupal is an open source content management system (CMS) that allows users to add modules to their sites for great flexibility. These modules are typically written in PHP and use drupal’s API to connect themselves with the framework. Silverlight (version 2.0 and greater) is a cross-platform browser plugin that allows users to write managed .NET code for the back end and use XAML as the front end. Since silverlight is a browser application and drupal is a framework for hosting data in browsers there can be data that is shared between the two using HTTP as the transport. In this example, I have chosen to use XML-RPC as drupal uses xml-rpc natively and silverlight can easily take advantage of this.

XmlRPC
XmlRPC

One thing to note, drupal has a module called services that is currently under development that will allow external applications (such as silverlight) to easily communicate through various interfaces, such as: XML-RPC, SOAP, REST and AMF. I am NOT using this module in my example.

Custom Module for Drupal:

As far as the drupal side of things go, I have a basic install of drupal 6.2 with a custom module that I have written to communicate with my silverlight application. I wrote the module based off of tips from Pro Drupal Development using chapters 2 and 19. There are two files that are needed to construct a drupal module: a .info file that contains information about the module and a .module file that is the actual custom module. My custom module is extremely simple in which it implements hook_xmlrpc to map xml-rpc methods to php callback functions. The xml-rpc methods are received from silverlight and then call the appropriate php function with the appropriate arguments. My php functions do various tasks, such as: checking the drupal database to see if a user exists so that user can login if the correct credentials are supplied,logging in an anonymous user, logging a user out and creating a fully authenticated user. Once the module is uploaded to the server, enable the module and your done! Well, done with the drupal side.

Silverlight application

There are currently two methods in silverlight to communicate via a web service (in this case xml-rpc): WebClient and HttpWebRequest. HttpWebRequest contains all of the properties and methods that WebClient has and then some. But of course the extra goodies that HttpWebRequest provides come at a price; added complexity. WebClient is extremely easy to use and only requires one event and one property to be used, all though there are additional events and properties that can make life easier. Here are the events and properties that I have used in my application:


drupalDB = new WebClient();
drupalDB.UploadStringCompleted += new UploadStringCompletedEventHandler(drupalDB_UploadStringCompleted);
drupalDB.UploadProgressChanged += new UploadProgressChangedEventHandler(drupalDB_UploadProgressChanged);
drupalDB.UploadStringAsync(location, xmlData);

drupalDB is my WebClient and there are two events defined that will notify me when upload progress has changed (perfect for a progress bar) and completed. The property, UploadStringAsync takes in a Uri and a string as the data to upload to the web service. One thing to note here is that I am using UploadStringCompleted and not DownloadStringCompleted as WebClient does a GET for downloads and a POST for uploads and xml-rpc only accepts POSTs.

The data that I am uploading to the xml-rpc service is a string that is xml formatted that uses the standard xml-rpc definition:

</p></p>

string xmlData = "<?xml version=\"1.0\"?>"
+ "<methodCall>"
+ "<methodName>userlogin.logAnonIn</methodName>"
+ "<params>"
+ "<param>"
+ "<value>"
+ "</value>"
+ "</param>"
+ "</params>"
+ "</methodCall>";

There are two important things to note here. The tag <methodName> contains the xml-rpc method that the hook_xmlrpc function will map to a php function. The first part of that argument, userlogin is the name of my module and a standard format that drupal follows. The <param> tag contains a <value> tag where an argument can be passed to the hook_xmlrpc function. If multiple arguments are required then one needs to add additional <param><value>my data here</value></param> tags in between the <params> tag. Once the data is sent from silverlight to drupal and the appropiate mapping php function is processed, a return value is sent back to silverlight:


<methodResponse>
<params>
<param>
<value>
<string>return value</string>
</value>
</param>
</params>
</methodResponse>

The UploadStringCompleted event will fire once all of the above data is received. A popular way to parse the returned data is to use LINQ. LINQ is very powerful and very easy to use once you learn a little about it. I can parse the above data with the following LINQ statement:

</p></p>

XDocument document = XDocument.Parse(e.Result);
List<MethodToCall> methodsToCall = (from user in document.Descendants("value").Elements("string")
select new MethodToCall
{
MethodName = user.Value
}).ToList();

I place the result in an XDocument and use LINQ to parse the document for any node ‘value’ that has a child node ’string’. This is the standard format that xml-rpc sends. If there are mutliple arguments then there will be multiple ‘value’ nodes. I call ToList() on the LINQ statement to create a generic list for multiple arguments that may be passed back.

With my particular application, since I can control what I send to and from the xml-rpc service, I decided to use reflection and pass back a string from drupal that I have defined as a method in my silverlight application. One thing to note here is that arguments are passed back as objects. If multiple arguments are passed back they are each an object, not an object[] so your method must take multiple arguments that are each objects instead of one argument of type object[].

After my method is called via reflection I simply update my user object as appropriate. And that’s it! That is the basics of how to get drupal to talk to silverlight and vice versa. Of course, one needs to add some more logic and flare to a silverlight application to take advantage of the data it receives from drupal. I have added a few items that hopefully make the user experience that much better (and easier) in my user login application.

As a side note, once silverlight is configured on your web server all you need to display the silverlight application is the following html code placed wherever you would like:

</p></p>

<div style="width: 325px; height: 350px;">
<object data="data:application/x-silverlight," type="application/x-silverlight-2-b2" width="100%" height="100%">
<param name="source" value="/silverlight/SLApps/DrupalGameDemo.xap" />

<!-- Only need the below to show a link to get silverlight if it is not installed -->
<a href="http://go.microsoft.com/fwlink/?LinkID=115261" style="text-decoration: none;">
<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none" />
</a>
</object>
</div>

The <object> tag contains a <param> tag that has the ‘value’ attribute of the location of your .xap file. The link in the above html code (<a> tag) is the link that will be displayed if a user does not have silverlight installed.

Feel free to try out the attached silverlight application (it is also displayed at the top of this page) and change things to your liking. Enjoy!

About the Author (Matt Serbinski)

My name is Matt Serbinski and I graduated from Rochester Institute of Technology (RIT) in March 2007 with a BS in computer engineering. I have been working at my current job, Naval Surface Warfare Center in Dahlgren, Virginia since 2004 while I was cooping. I have worked on a handful of projects that integrate existing technologies (lethal and non lethal) onto vehicle platforms as a single point to control everything. I was introduced to WPF when it was in beta and started to love everything it offered. Once Silverlight 2.0 was in beta I started to develop applications on the side for fun. I enjoy taking technologies and figuring out ways to use them together and developing WPF and Silverlight applications for fun.

VIEWS - Opensource Virtual Earth Wrapper for Silverlight

Jared Bienz from Microsoft announced that VIEWS, the Virtual Earth Wrapper for Silverlight, is live on Codeplex.

VIEWs is a wrapper around the JavaScript Virtual Earth control that allows a developer to write a complex Virtual Earth Mashup using a managed language for all client-side scripting. It was originally created by Adam Eversole in Microsoft Research and it was an internal-only project when Jared found it. Then,  Jared refine and polish it with the help of Adam within 8 days. Now, we all can download the VIEWS and its sample project. If you are new to VIEWS, you can take a look at Jared’s screencast and Getting Start tutorial.

(Yes. He announced about that July 1 but there was a few issues in accessing the homepage of VIEWS in Codeplex. Now, I just checked the URL agan and found that we are able to access the home page of VIEWS so I’m posting about that information here. )

Tall skinny data columns using improved WrapPanel for Silverlight: By Matt Perdeck

Note: This article is submitted by Matt for Silverlight 2 Beta 2 - Articles Competition.Thanks a lot, Matt! Hello All, Please drop a comment if you like it.

Download: TallColumns.zip (758 KB)

Contents

Introduction

When it comes to presenting data, there is a lot of emphasis on grids that present data in a single wide column, showing lots of fields horizontally.

However, some data tend to be presented in narrow columns, such as address lists and book indices. Instead of complex navigation, they have letter buttons, so you can quickly get to everybody whose name starts with “K”.

And, now that lots of people use monitors that are much wider then they are high, it makes sense to put the column into a vertical WrapPanel, so it takes all the available monitor width. Less scrolling and less wasted screen real estate.

The solution I came up with has a nice (I think) page transition effect. It supports paging at the data level, to minimize network traffic. Although people have already come up with WrapPanel implementations for Silverlight, I decided to do an improved version for this project (details). I also did a very simple reusable GroupHeader element that makes it easy to insert group headers in the list of records (the red “A”, “B”, etc., you see in the image above).

Requirements

Installation

  1. Download the zip file with the source code, and unzip in a directory.
  2. Open the TallColumns.sln file in Visual Studio.
  3. The main project within the solution is TallColumns. Project TallColumns2 is a version of TallColumns that uses scroll bars. The other projects are described further below.

How to add the GroupHeader element and the improved WrapPanel to the toolbox

  1. Build the solution.
  2. Right click in the toolbox and choose “Choose Items”. This opens the “Choose Toolbox Items” window.
  3. Click the Silverlight tab.
  4. Click the Browse button, and double click the WrapPanel.dll in the WrapPanel/Bin directory.
  5. Now, do the same with the GroupHeader.dll in the GroupHeader/Bin directory.
  6. Click OK to add the controls to the toolbox.

TallColumns Project

If you download the source file and open the solution, you’ll find it contains a number of projects.

The TallColumns and TallColumns2 projects contain the actual XAML. TallColumns2 is the same as TallColumns, except that it uses a ScrollViewer to show that the WrapPanel used here can be used with a ScrollViewer.

By way of test data, a list of companies and their addresses is used.

Page.xaml

The Page.xaml file starts with the two Storyboards that create the page transition effect when you move from page to page with the “Next” button, “Previous” button, etc. One blurs the page, and the other makes it visible again. The code actually updates the page contents after the first storyboard has completed but before starting the second storyboard.

After the storyboard definitions comes a horizontal StackPanel with the navigation buttons. The letter buttons sit in their own ItemsControl. The letters are actually loaded from the same database object as the main records. That makes it easy to ensure that if there are no companies starting with “X”, there is no “X” button either.

Finally, there is the ItemsControl that shows the actual company records. If you look at its ItemTemplate, you’ll see that on top of the TextBlocks for the record fields is a GroupHeader element that at the appropriate time shows the first letter of the company name (how this works). The ItemsControl’s ItemsPanel meanwhile specifies that a WrapPanel is being used to show the records (details WrapPanel).

Page.xaml.cs

When you look at Page.xaml.cs, you’ll find that all the code that makes the tall columns work is in a separate class TallColumnsManager, defined in the project TallColumnsManager. This to facilitate code reuse.

TallColumnsManager project

The TallColumnsManager class contains all the code needed to manage tall columns. When you look in the TallColumnsManager.cs file, you’ll see it is extensively documented.

Db Project

This project acts as the Data Access Layer. It has the Customer class that defines a customer, with company name, address, etc. The CustomerGroupCaption class defines the letters for the letter buttons. And, the CustomerAccess class exposes the methods used by the TallColumnsManager object (passed in via delegate parameters to the TallColumnsManager constructor) to retrieve the data. The methods are well documented in the source file.

Obviously, this project is just a dummy, with the data hard coded (old timers will recognize the Northwind list of customers). However, it would be easy to change the implementation so it retrieves data over the network, minimizing traffic by only retrieving a page load of records instead of all available records. This wouldn’t affect the class’ interface or the rest of the tall columns code.

WrapPanel Project

Improvements

This WrapPanel has a few improvements compared with the WrapPanel published by lneir:

  • Can be used inside a ScrollViewer.
  • Allows you to only show complete child elements (leaving out elements at the edges that are partly cutoff).
  • Lets you get the number of elements shown within the WrapPanel. This is useful when you try to create a paged interface with elements of different sizes.

WrapPanel Members

WrapPanel derives from Panel. It adds the following methods and properties to those provided by Panel:

Methods


static WrapPanel GetWrapPanel(string name)

Given the name of a WrapPanel, returns a reference to that WrapPanel. This may be the only way to get a reference to a WrapPanel that is used as the ItemsPanel of an ItemsControl.

Example:


WrapPanel wp = WrapPanel.GetWrapPanel("MainWrapPanel");

Properties

Name Type Description
Orientation Orientation Either Orientation.Horizontal or Orientation.Vertical.
ShowIncompleteChildren bool If true, you may see incomplete child elements at the right or bottom edge of the WrapPanel.

If false, child elements that cannot be completely shown within the WrapPanel are not shown at all.

ShownChildren int Number of child elements currently shown in the WrapPanel. Only valid after the WrapPanel has loaded (Loaded event has fired).

GroupHeader Project

This is a very simple element that makes it easy to insert single letter group headers in the list of records shown with an ItemsControl.

Say, you are showing the company name and address of a list of companies, sorted by company name, in an ItemsControl:


<ItemsControl  ...>
<ItemsControl.ItemTemplate>

<DataTemplate>
<StackPanel Orientation="Vertical" >
<TextBlock FontWeight="Bold" Text="{Binding CompanyName}" />

<TextBlock Text="{Binding Address}" />
</StackPanel>
</DataTemplate>

</ItemsControl.ItemTemplate>
</ItemsControl>

With the GroupHeader element, you can then insert a TextBlock each time a company name starting with a new letter is reached. The TextBlock would show that letter, like this:

A
Alfreds Futterkiste
Ana Trujillo Emparedados y helados
Antonio Moreno Taquería

B
Berglunds snabbköp
Blauer See Delikatessen
...

The GroupHeader element goes above the TextBlocks with the actual record fields:


<ItemsControl  .....>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" >

<my:GroupHeader GroupName="Main" GroupingValue="{Binding CompanyName}">
<my:GroupHeader.Template>
<ControlTemplate TargetType="my:GroupHeader">

<TextBlock x:Name="GroupHeaderText"></TextBlock>
</ControlTemplate>
</my:GroupHeader.Template>

</my:GroupHeader>
<TextBlock FontWeight="Bold" Text="{Binding CompanyName}" />

<TextBlock Text="{Binding Address}" />
</StackPanel>
</DataTemplate>

</ItemsControl.ItemTemplate>
</ItemsControl>

The GroupHeader has a template containing the TextBlock that will show the letter. Add properties to make it bold, change its color, etc.

Make sure that the TextBlock in the template has the name “GroupHeaderText“.

Instead of a TextBlock, you can also use a Button or HyperlinkButton, like this:


<my:GroupHeader GroupName="Main" GroupingValue="{Binding CompanyName}">
<my:GroupHeader.Template>
<ControlTemplate TargetType="my:GroupHeader">

<Button x:Name="GroupHeaderText" Click="LetterButton_Click"></Button>
</ControlTemplate>

</my:GroupHeader.Template>
</my:GroupHeader>

Bind the GroupingValue property of the GroupHeader to the record field whose first letter the GroupHeader needs to look at (CompanyName, in this case).

If you have more then one ItemsControl with a GroupHeader in your application, assign a unique name to the GroupName property of each GroupHeader.

When you reload an ItemsControl with a GroupHeader (for example, by assigning a new collection of records to the DataContext of the ItemsControl), use the static GroupHeader.ResetGroup(string groupName) method to reset the group. That way, the last record processed before the reload doesn’t upset the group headers after the reload.

For example:


// Reset group before assigning new collection to the ItemsControl
GroupHeader.ResetGroup("Main");
MainItemsControl.DataContext = newCollection;

About the Author (Matt Perdeck)

  • Technical skills used in last 2 years: AJAX, JavaScript (5 years), ASP.NET 3.5 and 2.0 (C#, VB.NET), SQL Server 2000 and 2005, XML, XHTML, CSS, InsiteCreation CMS.
  • Other technical skills include: Visual C++ (5 years), PHP, C, Linux, HTTP, SMTP, POP3, TCP/IP, PERL, C, embedded low level multi tasking software, CGI, MySQL, X.25, SNA.
  • Loves developing software. Passionate about producing reliable and maintainable software that makes the client happy.
  • Ranked #4,369 out of 154,356 (top 3%) at rentacoder.com.
  • GMAT score in top 2%.
  • Graduated with BSc Computer Science in 1987 from University of Technology Twente, The Netherlands.
  • Dutch national, with Australian permanent residence. Has worked in The Netherlands, Australia, Slovakia and Thailand.
  • Currently on contract with a major international publishing house until 1 August 2008, developing web sites.

    Email: mperdeck (at) gmail (dot) com
    Web site: http://advancedwebsitetechnology.com

Silverlight Drag, Drop, Import , and Export: By Michael Washington

Note: This article is submitted by Michael for Silverlight 2 Beta 2 - Articles Competition.Thanks a lot, Michael!
Hello All, I would like to remind you that our contest will be closed on 25th July, 2008 so please get writing and submit your great article as soon as possible. You will get a chance to win cool prizes. Your articles will be read by a lot of Silverlight fans and you will be recognized by our Silverlight community. Plus, we will drive some traffic to your site. This contest is sponsored by SilverlightShow.net and Silverlight 2 in Action.

Contents

  • Silverlight Drag, Drop, Import , and Export
  • The Code
  • Creating the Windows
  • The ImportContent Method
  • Summary
  • Author Bio

Download ~

SourceCode: SilverlightDragDropImportAndExport.zip (618 KB)
Live Demo : Silverlight Drag and Drop/Import and Export Demo

Silverlight Drag, Drop, Import , and Export

The example is based on an earlier drag and drop code sample that was based on an example created by Keith Mahoney. In his example he shows how to drag and drop various types of elements into various types of Panels. In this example we only cover dragging and dropping an element onto a Canvas. We also cover importing and exporting content from one user control into another.

When you view the sample you see three movable windows (you grab the colored bar on the top of the window to drag it) with three boxes in the first window.

The “Drag Me” box can only be dragged inside the window, but the blank box and the “Drag Me Outside” box can be dragged anywhere on the screen.

To determine the different behavior of the boxes, the word “[draggable]” is added to the Tag property of the element that will be draggable outside the window. When an element with that Tag is detected, the element is removed from the Canvas it is currently on and placed on the root Canvas of the application while it is being dragged.

When the element is dropped a HitTest is performed to see if the mouse is currently in the bounds of a Canvas element that is in one of the windows. If it is in the bounds, the element is removed from the root Canvas and placed onto the Canvas that is in the respective window.

If the boxes are dragged inside another window, they will remain in the window and move with that window when that window is moved.

The window labeled “Drop Items Here” implements a “ImportContent” interface that allows content to be possibly imported and parsed rather than simply added to the window. When the blank box is dragged onto the window labeled “Drop Items Here”, the box is simply added to the window.

However, when the box labeled “Drag Me Outside and Export Me” is dragged onto the window labeled “Drop Items Here”, the TextBlocks in the box are parsed and their content is added to the TextBlock in the window.

The Code

The solution file contains a Silverlight project and a web project to display the Silverlight project output. The following describes the files in the SilverlightDragAndDrop project:

  • Page.xaml - This is the main control that is displayed by the web project. This control creates windows using the SilverlightWindowControl control and inserts the contents of the SilverlightCanvas controls. It also create handlers to handle the drag and drop of the elements and calls the ImportContent method if it exists on the panel an element is being dropped on.
  • SilverlightCanvas1.xaml -  This control implements an ImportContent interface and a TextBox to display any imported content.
  • SilverlightCanvas2.xaml -  This control simply contains a blank Canvas that elements can be placed on.
  • SilverlightObjects.xaml -  This control contains three elements that can be dragged and dropped.
  • SilverlightWindowControl.xaml - This control is used by the Page.xaml control to create the windows the other controls are placed into.
  • Interfaces.cs - This contains an interface called ImportContent that the SilverlightCanvas1.xaml control implements to import content.

Creating the Windows

The Page.xaml control creates the three windows and a list of panels that elements can possibly be dragged onto. It will perform a HitTest to determine if an element is being dragged onto one of the panels.


public Page()
{
InitializeComponent();

// Window #1 - Contains objects
SilverlightWindowControl objSilverlightWindowControl = new SilverlightWindowControl(50);
// Pass an instance of this control to the child control. This will allow the child to
// wire-up mouse events to the parent.
SilverlightObjects SilverlightObjects = new SilverlightObjects(this);
objSilverlightWindowControl.Window.Content = SilverlightObjects;
Canvas.SetLeft(objSilverlightWindowControl, (double)50);
Canvas.SetTop(objSilverlightWindowControl, (double)50);

// SilverlightCanvas1 - Implements the "ImportContent" Interface
SilverlightWindowControl objBlankSilverlightWindowControl1 = new SilverlightWindowControl(60);
SilverlightCanvas1 objSilverlightCanvas1 = new SilverlightCanvas1();
objBlankSilverlightWindowControl1.Window.Content = objSilverlightCanvas1;
Canvas.SetLeft(objBlankSilverlightWindowControl1, (double)300);
Canvas.SetTop(objBlankSilverlightWindowControl1, (double)50);

// SilverlightCanvas2 - Does NOT implement the "ImportContent" Interface
SilverlightWindowControl objBlankSilverlightWindowControl2 = new SilverlightWindowControl(60);
SilverlightCanvas2 objSilverlightCanvas2 = new SilverlightCanvas2();
objBlankSilverlightWindowControl2.Window.Content = objSilverlightCanvas2;
Canvas.SetLeft(objBlankSilverlightWindowControl2, (double)300);
Canvas.SetTop(objBlankSilverlightWindowControl2, (double)310);

// Add Canvases to a collection that will be checked as possible drop points
colPanels.Add(SilverlightObjects.LayoutRoot);
colPanels.Add(objSilverlightCanvas1.LayoutRoot);
colPanels.Add(objSilverlightCanvas2.LayoutRoot);

// Add Windows to the Page
this.LayoutRoot.Children.Add(objSilverlightWindowControl);
this.LayoutRoot.Children.Add(objBlankSilverlightWindowControl1);
this.LayoutRoot.Children.Add(objBlankSilverlightWindowControl2);
}

The SilverlightObjects.xaml control uses an instance of the parent control to wire-up the mouse events to detect when an element is being dragged.


public SilverlightObjects(Page objPage)
{
InitializeComponent();

// Add delegates to the parent to allow it to handle the drag and drop
FrameworkElement objFrameworkElement1 = (FrameworkElement)this.DragElement1;
objFrameworkElement1.MouseLeftButtonDown +=
new MouseButtonEventHandler(objPage.objFrameworkElement_MouseLeftButtonDown);
FrameworkElement objFrameworkElement2 = (FrameworkElement)this.DragElement2;
objFrameworkElement2.MouseLeftButtonDown +=
new MouseButtonEventHandler(objPage.objFrameworkElement_MouseLeftButtonDown);
FrameworkElement objFrameworkElement3 = (FrameworkElement)this.DragElement3;
objFrameworkElement3.MouseLeftButtonDown +=
new MouseButtonEventHandler(objPage.objFrameworkElement_MouseLeftButtonDown);
}

Note, the only way to get an instance of the parent control when a control has been dynamically added is to pass an instance of that parent to the control when the control is added. The mouse events must be attached to, and handled by, the parent not the child control for the communication between windows to work.

When the mouse starts to drag an element, the element is checked to see if it’s tag contains “[draggable]“. If it does, the element is removed from the Canvas it is on and placed on the main Canvas.


void objFrameworkElement_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
//Start Drag
FrameworkElement objFrameworkElement = (FrameworkElement)sender;
objFrameworkElement.CaptureMouse();

// Set the starting point for the drag
StartingDragPoint = e.GetPosition(objFrameworkElement);

// Remove the element from it's control and move it to the parent
// if it's Tag contains the words [draggable]
if (objFrameworkElement.Tag.ToString().Contains("[draggable]"))
{
Panel objParent = objFrameworkElement.Parent as Panel;
objParent.Children.Remove(objFrameworkElement);

this.LayoutRoot.Children.Add(objFrameworkElement);
MoveToTop(objFrameworkElement);
UpdateElementPosition(objFrameworkElement, e.GetPosition(this.LayoutRoot));
}

objFrameworkElement.MouseMove += new MouseEventHandler(objFrameworkElement_MouseMove);
objFrameworkElement.MouseLeftButtonUp += new MouseButtonEventHandler(objFrameworkElement_MouseLeftButtonUp);
}

The code to move an element being dragged is the same whether or not the element has been moved to the main Canvas or not.


#region MouseMove
void objFrameworkElement_MouseMove(object sender, MouseEventArgs e)
{
FrameworkElement objFrameworkElement = (FrameworkElement)sender;
Canvas objCanvas = (Canvas)objFrameworkElement.Parent;
Point Point = e.GetPosition(objCanvas);

UpdateElementPosition(objFrameworkElement, Point);
}
#endregion

When an element is dropped, this code is used to determine if the element will simply be placed inside a control, or if the ImportContent method will be called:


#region MouseLeftButtonUp
void objFrameworkElement_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
//Stop Drag
FrameworkElement objFrameworkElement = (FrameworkElement)sender;
objFrameworkElement.ReleaseMouseCapture();

objFrameworkElement.MouseMove -=
new MouseEventHandler(objFrameworkElement_MouseMove);
objFrameworkElement.MouseLeftButtonUp -=
new MouseButtonEventHandler(objFrameworkElement_MouseLeftButtonUp);

// If it is an element marked [draggable]
// try to drop it on a panel stored in the colPanels collection
if (objFrameworkElement.Tag.ToString().Contains("[draggable]"))
{
Point tmpPoint = e.GetPosition(null);
// Build a list of elements at the current mouse position
List<UIElement> hits = (List<UIElement>)this.HitTest(tmpPoint);
// Loop through all the Panels in the colPanels collection
foreach (Panel objPanel in colPanels)
{
if (hits.Contains(objPanel))
{
// Grab the position of the element being dragged in relation to it's                position on the
// main canvas and its position in relation to the panel it may be dropped on
Point mousePos1 = e.GetPosition(objPanel);
Point mousePos2 = e.GetPosition(objFrameworkElement);

// Remove the element from the main canvas
this.LayoutRoot.Children.Remove(objFrameworkElement);

// Import content
// Get a reference to the parent of the current panel
UserControl objUserControl = (UserControl)objPanel.Parent;
// See if that parent implements an interface called "ImportContent"
object objObject = objUserControl.GetType().GetInterface("ImportContent", true);

// If the object is not null then the parent object has a method             called "ImportContent"
if (!(objObject == null))
{
// Create a parmeters array
object[] parameters = new object[1];
// Add the elemnt that is being dragged to the array
parameters.SetValue(objFrameworkElement, 0);
// invoke the "ImportContent" on the parent object passing the parameters array that
// contains the element being dragged
bool boolImport = (bool)objUserControl.GetType().InvokeMember("ImportContent",
BindingFlags.InvokeMethod, null, objUserControl, parameters);

// If the import was not successful simply add the element to the panel
if (!boolImport)
{
// Add the element to the panel
objPanel.Children.Add(objFrameworkElement);
Canvas.SetLeft(objFrameworkElement, mousePos1.X - mousePos2.X);
Canvas.SetTop(objFrameworkElement, mousePos1.Y - mousePos2.Y);
}
}
else
{
// The parent object does not implement the "ImportContent" Interface
// Add the element to the panel
objPanel.Children.Add(objFrameworkElement);
Canvas.SetLeft(objFrameworkElement, mousePos1.X - mousePos2.X);
Canvas.SetTop(objFrameworkElement, mousePos1.Y - mousePos2.Y);
}
break;
}
}
}
}
#endregion

The ImportContent Method

Each control determines what content it will import. The control must implement an interface called ImportContent that accepts a FrameworkElement as a single parameter and returns a bool:


interface ImportContent
{
bool ImportContent(FrameworkElement objFrameworkElement);
}

In this example the SilverlightCanvas1.xaml control is coded to only import text from any TextBlocks.


#region ImportContent
public bool ImportContent(FrameworkElement objFrameworkElement)
{
// This import method will only import text content contained
// in TextBlocks that are placed on a Canvas

StringBuilder StringBuilder = new StringBuilder();
Canvas objCanvas = objFrameworkElement as Canvas;

// If the element being imported is not a canvas return false
if (objCanvas == null)
{
return false;
}

try
{
// Loop through all the UIElements in the Canvas
foreach (UIElement objUIElement in objCanvas.Children)
{
// Try to cast the UIElement as a TextBlock
TextBlock objTextBlock = objUIElement as TextBlock;
if (objTextBlock != null)
{
// Add the contents of the TextBlock to the output
StringBuilder.Append(String.Format(" {0}", objTextBlock.Text));
}
}
}
catch
{
return false;
}

// Was content in the output ?
if (StringBuilder.Length > 0)
{
// Add output to the Textbox display
this.txtContent.Text = StringBuilder.ToString();
return true;
}
else
{
return false;
}
}
#endregion

Summary

This example demonstrates a method for dealing with the “unknown”. With SilverlightDesktop, modules can be created by different developers that have no knowledge of each other, yet using a method such as this, content can be exported and imported between them.

Author: Michael Washington

Michael Washington is a website developer and an ASP.NET, C#, and Visual Basic programmer. He is has been named Microsoft MVP in ASP.NET for two straight years. He is a DotNetNuke Core member and has served for over three years. He is the author of the module development chapter in Building Websites with VB.NET and DotNetNuke 4 (Packt Publishing).

He has a son, Zachary and resides in Los Angeles with his wife Valerie.