July 2008 - Posts
Silverlight currently does not support mouse wheel events. However, you can attach an event to capture the mouse wheel movement through the HtmlPage object. This tutorial will show you how to do it for IE, Opera, Mozilla and Safari browsers.
To start, we declare three events to capture the mouse event in order to cover all the possibilities for all browsers. For example, the DOMMouseScroll event is used by Mozilla.
public Page()
{
InitializeComponent();
HtmlPage.Window.AttachEvent("DOMMouseScroll", OnMouseWheel);
HtmlPage.Window.AttachEvent("onmousewheel", OnMouseWheel);
HtmlPage.Document.AttachEvent("onmousewheel", OnMouseWheel);
}
In our event we can get the delta change in the wheel. The way we do this in Mozilla and Safari browsers is different than how we do it in IE or Opera.
- Mozilla/Safari: Check the property called “detail”
- IE/Opera: Check the property called “wheeldelta”
Here is the complete code:
private void OnMouseWheel(object sender, HtmlEventArgs args)
{
double mouseDelta = 0;
ScriptObject e = args.EventObject;
// Mozilla and Safari
if (e.GetProperty("detail") != null)
{
mouseDelta = ((double)e.GetProperty("detail"));
}
// IE and Opera
else if (e.GetProperty("wheelDelta") != null)
mouseDelta = ((double)e.GetProperty("wheelDelta"));
mouseDelta = Math.Sign(mouseDelta);
}
Thank you,
--Mike Snow
Subscribe in a reader
No game is complete without a great sound track and sound effects! This tutorial will show you how to do both with a very small amount of code.
Demo:
(Silverlight 2 RTW required).
Silverlight currently supports the following formats:
Video
- WMV1-3 (Windows Media Video 7, 8 & 9 respectively).
- WMVA (Windows Media Video Advanced Profile, non VC-1).
- WMVC1 (Windows Media Video Advanced Profile, VC-1).
Audio
- MP3 – ISO/MPEG Layer-3
- Sampling frequencies: 8, 11.025, 12, 16, 22.05, 24, 32, 44.1, and 48 kHz
- Bit rates: 8-320 kbps, variable bit rate.
- WMA 7 (Windows Media Audio 7)
- WMA 8 (Windows Media Audio 8)
- WMA 9 (Windows Media Audio 9)
To add sound, music or video you will need to declare a MediaElement. Each media file you reference must be added to your project. Select each file and change the Build Action = “Resource” in the Properties window as seen in Figure 22.1. This will ensure the media file gets copied to your ClientBin folder during execution.
Figure 22.1. Properties for the Media File
Let’s start by adding three buttons and three MediaElements to our Page.xaml. One button for playing music, one for sound, and the other for video.
<Canvas Background="Black">
<Button Click="Button_Click_Music" Canvas.Left="10" Canvas.Top="10" Width="80" Height="30" Content="Play Music"></Button>
<Button Click="Button_Click_Sound" Canvas.Left="100" Canvas.Top="10" Width="80" Height="30" Content="Play Sound"></Button>
<Button Click="Button_Click_Video" Canvas.Left="200" Canvas.Top="10" Width="80" Height="30" Content="Play Video"></Button>
<MediaElement x:Name="SoundFile" Source="Boom.mp3" AutoPlay="False"></MediaElement>
<MediaElement x:Name="MusicFile" Source="Sharon.mp3" AutoPlay="False"></MediaElement>
<MediaElement Width="300" Height="300" Canvas.Top="100" x:Name="VideoFile" AutoPlay="False" Source="MyVideo.wmv"></MediaElement>
</Canvas>
For our code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace Tip22
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
}
private void StopAll()
{
MusicFile.Stop();
SoundFile.Stop();
VideoFile.Stop();
}
private void Button_Click_Music(object sender, RoutedEventArgs e)
{
StopAll();
MusicFile.Play();
}
private void Button_Click_Sound(object sender, RoutedEventArgs e)
{
StopAll();
SoundFile.Play();
}
private void Button_Click_Video(object sender, RoutedEventArgs e)
{
StopAll();
VideoFile.Play();
}
}
}
Few things to notice:
- We set AutoPlay=”False” to prevent the media element from playing as soon as the app starts.
- We use the Source property to point to the name of the file we want to play.
- We use the x:Name attribute to identify the sound element. We can then use this ID to play the sound file. Example: VideoFile.Play().
Thank you,
--Mike Snow
Subscribe in a reader
Shapes, gradients and brushes provide the user with a lot of power to make custom controls that look beautiful. For example, gradients can be made to look like shadows or lighting for stunning visual effects. For this demo, we are going to make the beginnings of what could end up being a clock, or taken further, a gauge. The screen shot below is what we will be achieving in this tutorial.
Before we make this clock, let’s review the properties that will make these controls.
Shapes Elements
Silverlight supports what is called vector graphics by providing the following basic shapes:
- Ellipse – Describes an oval or circle.
- Rectangle – Describes a rectangle or a square (can have rounded corners).
- Line – Describes a line that connects two points.
- Polygon – Describes a closed shape with an arbitrary number of sides.
- Polyline – Descries a series of connected lines that may or may not form a closed shape.
- Path – Describes complex shapes that include curves and arcs.
Color
Color can be specified in one of the following formats:
- Its name such as “Red”, “Blue”, “Black”, etc.
- A 6 digit RGB (red, green, blue) notation #rrggbb where rr, gg and bb are the two digit hexadecimal value that describes the amount of color for red, green and blue respectively. Example: #FF0000 would be a bright red color.
- A 8 digit ARGB (alpha, red, green, blue) notation with two extra values that describe the alpha value (opacity) of the color. For example, #FFFF0000 would be bright red with no opacity.
Fill & Fill
Shapes consist of two parts, the outer outline (border) and the inner part. The color of these parts are controlled through the Stroke and Fill properties. Some shapes such as a Line only have a stroke. Specifying a Fill for a line would have no affect.
Brushes
Using <Ellipse.Fill> we can fill this ellipse using one of the following brush options:
- A linear gradient brush – Paints a gradient along a line. The line is diagonal and stretches from the upper left to the lower right corner by default. The properties StartPoint and EndPoint can change these positions.
- A Image brush – Paints with an image.
- A radial gradient brush – Paints a gradient along a circle. By default, the circle is centered on the area being painted.
- A solid color brush – Paints an area with a solid color.
- A video brush – Paints an area with live streaming video.
Here is an example of each:
As appears in source code:
<MediaElement Canvas.Top="300" x:Name="MyMedia" Source="MyVideo.wmv" Width="300" Height="300" />
<Ellipse Canvas.Top="20" Canvas.Left="5" Width="100" Height="100" StrokeThickness="2">
<Ellipse.Fill>
<LinearGradientBrush
StartPoint='0.1,0.06'
EndPoint='0.5,0.6'>
<GradientStop Color='#FFFFFFFF' Offset='0'/>
<GradientStop Color='#FF000000' Offset='1'/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse Canvas.Top="20" Canvas.Left="105" Width="100" Height="100" StrokeThickness="2">
<Ellipse.Fill>
<ImageBrush ImageSource="clock.png" Stretch="Uniform"></ImageBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse Canvas.Top="20" Canvas.Left="205" Width="100" Height="100" StrokeThickness="2">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="Yellow" Offset="0.0" />
<GradientStop Color="Red" Offset="1.0" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse Canvas.Top="20" Canvas.Left="305" Width="100" Height="100" StrokeThickness="2">
<Ellipse.Fill>
<SolidColorBrush Color="Blue"/>
</Ellipse.Fill>
</Ellipse>
<Ellipse Canvas.Top="20" Canvas.Left="405" Width="100" Height="100" StrokeThickness="2">
<Ellipse.Fill>
<VideoBrush SourceName="MyMedia" />
</Ellipse.Fill>
</Ellipse>
For the Video brush, make certain the video file is placed in your web sites ClientBin folder.
On to making the clock! We start with a simple ellipse in a <Canvas> object setting it’s Width and Height to 200. For the Fill property, we will apply the LinearGradientBrush
<Grid x:Name="LayoutRoot" Background="Gray">
<Canvas x:Name="Clock">
<Ellipse Canvas.Top="150" Canvas.Left="5" Width="200" Height="200" StrokeThickness="2">
<Ellipse.Fill>
<LinearGradientBrush StartPoint='0.1,0.06' EndPoint='0.5,0.6'>
<GradientStop Color='#FFFFFFFF' Offset='0'/>
<GradientStop Color='#FF000000' Offset='1'/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Canvas>
</Grid>
This code will generate the following circle:
Let’s add another small circle in the middle plus 2 sets of 3 red lines to make the the hour and minute hands for the clock.
<Line X1="100" X2="25" Y1="246" Y2="247" Stroke="Red" StrokeThickness="1" />
<Line X1="100" X2="25" Y1="247" Y2="247" Stroke="Red" StrokeThickness="1" />
<Line X1="100" X2="25" Y1="248" Y2="247" Stroke="Red" StrokeThickness="1" />
<Line X1="106" X2="60" Y1="246" Y2="290" Stroke="Red" StrokeThickness="1" />
<Line X1="106" X2="60" Y1="247" Y2="290" Stroke="Red" StrokeThickness="1" />
<Line X1="106" X2="60" Y1="248" Y2="290" Stroke="Red" StrokeThickness="1" />
<Ellipse Width="15" Height="15" Canvas.Left="100" Canvas.Top="240">
<Ellipse.Fill>
<LinearGradientBrush
StartPoint='0.1,0.06'
EndPoint='0.5,0.6'>
<GradientStop Color='#FFFFFFFF' Offset='0'/>
<GradientStop Color='#FF000000' Offset='1'/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
Finally, for the numbers we will programmatically generate and add them as Textblocks. We do this by calculating the X & Y location along a circle with a radius of 85 pixels. Each number is separated by 30 degrees. We start at 1:00 which is located at an angle of 300 degrees.
1: private void AddNumbers()
2: {
3: double angle = 300;
4: for (int i = 1; i < 13; i++)
5: {
6: TextBlock tb = new TextBlock();
7: tb.Foreground = new SolidColorBrush(Colors.White);
8: tb.Text = i.ToString();
9:
10: double radians = angle * (Math.PI / 180);
11: double radius = 85;
12: int x = (int)(radius * Math.Cos(radians));
13: int y = (int)(radius * Math.Sin(radians));
14: tb.SetValue(Canvas.LeftProperty, (double) x+97);
15: tb.SetValue(Canvas.TopProperty, (double) y+239);
16:
17: angle += 30;
18: Clock.Children.Add(tb);
19: }
20: }
Here is our final result:

Thank you,
--Mike Snow
Subscribe in a reader

Yesterday I had the opportunity to speak on Silverlight game programming at GameFest 2008. Working with Tim Heuer and two Silverlight MVP’s Bill Reiss and Joel Neubeck, we were able to hold two sessions that covered Silverlight.
During my talk we covered topics including:
- Creating Silverlight projects using Visual Studio 2008.
- Creating a tile based map
- Scrolling the map via keyboard events.
- Demo of a multi-player game called “Fireball” as well as “Tank Wars”.
I am going to be polishing up and optimizing the source code for both of these games and then I will post the full source with a tutorial and a live version of each game on my blog here. You can expect these by next week.
If you met me at GameFest and did not get my contact you can reach me at msnow@microsoft.com for any questions or suggestions. It was a great privilege to be at the conference and I am looking forward to the next opportunity to speak on Silverlight.
Thank you,
--Mike
Each application by default is given 1 MB of storage space through Isolated Storage where the server is able to store client specific data on the clients machine. So what if you need more than 1 MB? Fortunately, the IsolatedStorageFile object provides a method called IncreaseQuotaTo() that allows a server to prompt the user for permission to increase the amount of storage.
You can see the list of applications that are using IsolatedStorage on your box. To do this:
- Right click on any Silverlight Application and from the context menu choose the only option: “Silverlight Configuration”.
- This will bring up the Silverlight Configuration Dialog:
- Click on the last tab “Application Storage” and you will see a list of all web sites that are using Isolated Storage on this machine. The current amount of space each app is using as well as the quota allowed per app are also listed.
For our demo we will be displaying 3 pieces of information:
- Current Spaced Used
- Current Space Available
- Current Quota
I have added a text box where you can specify the number of bytes you wish to increase your quota by. By clicking on the button, this will then make the necessary call to IncreaseQuotaTo() in order to increase the quota.
***Note: You must call this function from a user event such as a button click. For security reasons, calling IncreaseQuotaTo() directly will automatically return false since the user did not instantiate the call.
Below is a screen shot of the test application we are creating:
When you enter a value and click on the button you will be prompted with this dialog:
If you click yes, you will see the new results:
Now, let’s look at our XAML code in Page.xaml that we use to set up this interface:
<UserControl x:Class="IncreaseIsolatedStorage.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="800" Height="600">
<Grid x:Name="LayoutRoot" Background="White">
<Canvas Canvas.Left="10" Canvas.Top="10">
<TextBlock Canvas.Left="10" x:Name="SpacedUsed" >Current Spaced Used=</TextBlock>
<TextBlock Canvas.Left="10" x:Name="SpaceAvaiable" Canvas.Top="20">Current Space Available=</TextBlock>
<TextBlock Canvas.Left="10" x:Name="CurrentQuota" Canvas.Top="40">Current Quota=</TextBlock>
<TextBlock Canvas.Left="10" x:Name="NewSpace" Canvas.Top="70">New space (in bytes) to request=</TextBlock>
<TextBox Canvas.Left="255" Canvas.Top="70" Width="100" x:Name="SpaceRequest"></TextBox>
<TextBlock Canvas.Left="365" Canvas.Top="70" Width="60">(1048576 = 1 MB)</TextBlock>
<Button Canvas.Left="10" Content="Increase Storage" Canvas.Top="100"
Width="100" Height="50" Click="Button_Click">
</Button>
<TextBlock Canvas.Left="10" Canvas.Top="160" x:Name="Results"></TextBlock>
</Canvas>
</Grid>
</UserControl>
And our code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO.IsolatedStorage;
namespace SilverlightApplication11
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
SetStorageData();
}
private void SetStorageData()
{
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
SpacedUsed.Text = "Current Spaced Used = "+(isf.Quota - isf.AvailableFreeSpace).ToString() +" bytes";
SpaceAvaiable.Text = "Current Space Available=" + isf.AvailableFreeSpace.ToString() + " bytes";
CurrentQuota.Text = "Current Quota=" + isf.Quota.ToString() + " bytes";
}
}
private void IncreaseStorage(long spaceRequest)
{
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
long newSpace = isf.Quota + spaceRequest;
try
{
if (true == isf.IncreaseQuotaTo(newSpace))
{
Results.Text = "Quota successfully increased.";
}
else
{
Results.Text = "Quota increase was unsuccessfull.";
}
}
catch (Exception e)
{
Results.Text = "An error occured: "+e.Message;
}
SetStorageData();
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
try
{
long spaceRequest = Convert.ToInt64(SpaceRequest.Text);
IncreaseStorage(spaceRequest);
}
catch { // User put bad data in text box }
}
}
}
Thank you,
--Mike Snow
Subscribe in a reader
Silverlight uses Isolated Storage as a virtual file system to store data in a hidden folder on your machine. It breaks up the data into two separate sections: Section #1 contains administrative information such as disk quota and section #2 contains the actual data. Each Silverlight application is allocated its own portion of the storage with the current quota set to be 1 MB per application.
Advantages:
- Isolated Storage is a great alterative to using cookies (as discussed in Tip of the Day #18) especially if you are working with large sets of data. Examples of use include undo functionality for your app, shopping cart items, window settings and any other setting your application can call up the next time it loads.
- Isolated storage stores by user allowing server applications to dedicate unique settings per individual user.
Possible Pitfalls:
- Administrators can set disk quota per user and assembly which means there is no guarantee on space available. For this reason, it is important to add exception handling to your code.
- Even though Isolated Storage is placed in a hidden folder it is possible, with a bit of effort, to find the folder. Therefore the data stored is not completely secure as users can change or remove files. It should be noted though that you can use the cryptography classes to the encrypt data stored in isolated storage preventing users from changing it.
- Machines can be locked down by administrative security policies preventing applications from writing to the IsolatedStorage. More specifically, code must have the IsolatedStorageFilePermission to work with isolated storage.
All that said, let’s take a look at how we save and load data. Note that you will need to add a using statement to reference the namespace System.IO.IsolatedStorage as well as System.IO.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO.IsolatedStorage;
using System.IO;
namespace SilverlightApplication10
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
SaveData("Hello There", "MyData.txt");
string test = LoadData("MyData.txt");
}
private void SaveData(string data, string fileName)
{
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(fileName, FileMode.Create, isf))
{
using (StreamWriter sw = new StreamWriter(isfs))
{
sw.Write(data);
sw.Close();
}
}
}
}
private string LoadData(string fileName)
{
string data = String.Empty;
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(fileName, FileMode.Open, isf))
{
using (StreamReader sr = new StreamReader(isfs))
{
string lineOfData = String.Empty;
while ((lineOfData = sr.ReadLine()) != null)
data += lineOfData;
}
}
}
return data;
}
}
}
Thank you,
--Mike Snow
Subscribe in a reader
Cookies are strings of text that the server can store on the client side. These cookies can then be sent back by the client to the server each time the client accesses that server again. Cookies are commonly used for session tracking, authentication, site preferences and maintaining specific information about users. For example, items a client stores in a shopping cart can be stored on the client side as cookies so that they can leave the online store and return later to check out.
So, how do we set cookies from within a Silverlight application? To accomplish this we turn again to the HtmlPage.Document object. To use this object you must add a using statement to reference the System.Windows.Browser namespace.
To set a cookie we need to call SetProperty() with a string in the following format: “Key=Value;expires=ExpireDate.”
For example:
private void SetCookie(string key, string value)
{
// Expire in 7 days
DateTime expireDate = DateTime.Now + TimeSpan.FromDays(7);
string newCookie = key + "=" + value + ";expires=" + expireDate.ToString("R");
HtmlPage.Document.SetProperty("cookie", newCookie);
}
Now, to get the cookie we split up and iterate through all the cookies returned through the property HtmlPage.Document.Cookies.
For example:
private string GetCookie(string key)
{
string[] cookies = HtmlPage.Document.Cookies.Split(';');
foreach (string cookie in cookies)
{
string [] keyValue = cookie.Split('=');
if (keyValue.Length == 2)
{
if(keyValue[0].ToString() == key)
return keyValue[1];
}
}
return null;
}
For more details on additional properties you can set when creating cookies please see MSDN.
Thank you,
--Mike Snow
Subscribe in a reader
Each Silverlight element exposes a property called RenderTransform that is used to set the transform information that affects the rendering position of the element. I will be demo’ing a non-stop circular transform rotation of an image as seen here below:
(Silverlight 2 RTM required).
First, let’s declare the the image in our Page.xaml. Make certain to add the image your are setting the source to here to your project in VS. Since we are rotating around the center of the image, we set the CenterX and CetnerY to be the center coordinates of the image which. In my case the image I am using is 64x48 pixels so the center is set at CenterX=32, CenterY=24.
In Page.XAML replace <Grid></Grid> with the following:
<Canvas Background="Black">
<Image x:Name="FireballLogo" Source="images/Fireballlogo.png">
<Image.RenderTransform>
<RotateTransform x:Name="FireballTransform" CenterX="32" CenterY="24"></RotateTransform>
</Image.RenderTransform>
</Image>
</Canvas>
Next, let’s setup our game loop timer using CompositionTarget.Rendering as our looping event. We perform the transform around the center of the image which is 32, 24 since the image is 64x48 in size. For each frame, we increment the angle by one.
namespace Tip17
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
}
void CompositionTarget_Rendering(object sender, EventArgs e)
{
FireballTransform.Angle += 1;
FireballTransform.Transform(new Point(32, 24));
}
}
}
Thank you,
--Mike Snow
Subscribe in a reader
In Tip of the Day #5 I discussed how to use the DispatcherTimer for your main game loop. However, a better approach may be to use the StoryBoard timer as discussed here or the CompositionTarget.Rendering event which was recently added to Silverlight 2. Check out Tip of the Day #50 for more info on using the CompositionTarget.Rendering event for your main game loop.
From my research, the reasons the StoryboardTimer is better than the DispatcherTimer is as follows:
- The StoryBoard is handled on a separate thread that is not affected by the UI thread which the DispatcherTimer is on.
- The DispatcherTimer is a lower resolution timer than the timer behind the Storyboard class, which causes loss in fidelity.
- The Storyboard execution is more stable across the different supported OS’s and web browsers.
Given that, let’s take a look at how it can be done. In the example below, we create a StoryBoard timer and increment and display a count variable that represents the number of times the MainGameLoop was called. I have set my duration between calls to be zero milliseconds but you will want to change this to whatever best fits your animation story.
Page.xaml.cs:
namespace SilverlightApplication8
{
public partial class Page : UserControl
{
Storyboard _gameLoop = new Storyboard();
int count = 0;
public Page()
{
InitializeComponent();
_gameLoop.Duration = TimeSpan.FromMilliseconds(0);
_gameLoop.Completed += new EventHandler(MainGameLoop);
_gameLoop.Begin();
}
void MainGameLoop(object sender, EventArgs e)
{
// Add any game logic/animation here.
// Example:
myTextbox.Text = count.ToString();
count++;
// Continue storyboard timer
_gameLoop.Begin();
}
}
}
Page.xaml:
<UserControl x:Class="SilverlightApplication8.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock x:Name="myTextbox">Display Counter</TextBlock>
</Grid>
</UserControl>
Thank you,
--Mike Snow
Subscribe in a reader
Communicating between Javascript and Silverlight is, fortunately, relatively straight forward. The following sample demonstrates how to make the call both ways.
Calling Silverlight From Java script:
- In the constructor of your Silverlight app, make a call to RegisterScriptableObject().This call essentially registers a managed object for scriptable access by JavaScript code. The first parameter is any key name you want to give. This key is referenced in your Javascript code when making the call to Silverlight.
- Add the function you want called in your Silverlight code. You must prefix it with the [ScriptableMember] attribute.
- In Javascript, you can now call directly into your Silverlight function. This can be done through the document object. From my example below: document.getElementById("silverlightControl").Content.Page.UpdateText("Hello from Javascript!"); where “silverlightControl” is the ID of my Silverlight control.
Calling Javascript From Silverlight:
- Javascript can be directly called via the HtmlPage.Window.Invoke() function.
Example for both:
Page.xaml:
namespace SilverlightApplication
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
HtmlPage.RegisterScriptableObject("Page", this);
HtmlPage.Window.Invoke("TalkToJavaScript", "Hello from Silverlight");
}
[ScriptableMember]
public void UpdateText(string result)
{
myTextbox.Text = result;
}
}
}
Default.aspx:
<script type="text/javascript">
function TalkToJavaScript( data)
{
alert("Message received from Silverlight: "+data);
var control = document.getElementById("silverlightControl");
control.Content.Page.UpdateText("Hello from Javascript!");
}
</script>
Page.xaml:
<UserControl x:Class="SilverlightApplication7.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock x:Name="myTextbox">Waiting for call...</TextBlock>
</Grid>
</UserControl>
Thank you,
--Mike Snow
Subscribe in a reader
You may have noticed that right clicking on a Silverlight application brings up the following context menu and configuration dialog:
Context Menu:
Configuration Dialog:
So what if you want to use right click in your application? While right click functionality is not currently supported in Silverlight, there is a work-around. I will step you through how to intercept the right click event, process it and display your own content instead of the Silverlight dialog.
Step 1. To start, let’s add a <TextBlock> control to our Page.xaml to track the status of the right click:
<UserControl x:Class="SilverlightApplication15.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="red">
<TextBlock x:Name="MyField">Right click please.</TextBlock>
</Grid>
</UserControl>
Step 2. Next, we need to set the Silverlight control to be windowless. Open the web page (i.e. default.aspx) that contains the Silverlight control and add the tag Windowless=”true” to it. Example:
<asp:Silverlight ID="Xaml1" runat="server" Windowless="true" Source="~/ClientBin/SilverlightApplication15.xap" MinimumVersion="2.0.30523" Width="100%" Height="100%" />
Step 3: Finally, let’s take a look at the code we add to Page.xaml.cs.
-
I start by creating a new class called ContextMenuInterceptor. In this class constructor we attach an event “OnContextMenu” to the document for the HtmlPage. In order to use the HTMLPage object you will need to add a using statement referencing System.Window.Browser.
-
I then call e.PeventDefault(). This cancels further propagation of the right click event so that Silverlight does not receive it.
-
At this point, we have intercepted the right click and can do whatever else we want. In my case, I simply display the coordinates of where you right clicked.
Page.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Browser;
namespace SilverlightApplication15
{
public partial class Page : UserControl
{
ContextMenuInterceptor _cmi = null;
public Page()
{
InitializeComponent();
_cmi = new ContextMenuInterceptor(MyField);
}
}
public class ContextMenuInterceptor
{
TextBlock TextField;
public ContextMenuInterceptor(TextBlock textField)
{
TextField = textField;
HtmlPage.Document.AttachEvent("oncontextmenu", this.OnContextMenu);
}
private void OnContextMenu(object sender, HtmlEventArgs e)
{
TextField.Text = "Right Clicked Blocked at "+e.OffsetX+","+e.OffsetY;
e.PreventDefault();
}
}
}
Thank you,
--Mike Snow
Subscribe in a reader