Infosys Microsoft Alliance and Solutions blog

« WCF Service Factory | Main | Writing my first WCF service on Vista »

Integrating WPF/E content in ASP.NET AJAX 1.0 based application,,,

In one of my previous blogs -  http://infosysblogs.com/microsoft/2006/10/aspnet_20_ajax_extensions_atla.html, I had provided details about invoking web services, without worrying about the hassles of creating proxy and all, from the client side of the ASP.NET applications, using ASP.NET 2.0 AJAX extensions. And I had also mentioned that client-side invocation of the remote (cross-domain) web services is forbidden because of security implications surrounding that. In order to invoke a remote web service, we have to use a local web service, that acts as a bridge service, which in turn invokes the remote web service. I had used such a bridge web service to invoke a remote web service http://www.xmlme.com/WSShakespeare.asmx  – Shakespeare quotes web service – that provides us back with the timeless quotes from not so forgettable protagonists of Shakespeare’s works.

And it has been long time since I blogged on that topic any further and when I get sufficient enthusiasm and time to come back to that, lot of changes have already happened – the major one is the version 1.0 release of ASP.NET AJAX extensions (and in fact ASP.NET AJAX  Feature Set  January CTP is also available in addition to that), apart from plethora of technology previews, evaluation versions of longhorn server, Orcas development tools, Expression Suite products, WPF/E, etc. Immediately after installing the relevant pieces (Well. To be honest, not so before fully overcoming the mental numbness that happens mainly due to the presence of sheer number of upcoming technologies at such a rapid pace Smile ) I wanted to port the sample piece of code that I wrote based on previous CTP of ASP.NET 2.0 AJAX Extension, to this new version. Not to anybody’s surprise (if anybody gets surprised, he/she might be visiting our planet from far away galaxy Smile ), it invariably breaks.

In the process of fixing up the code, I have picked up certain new exciting features. The previous sample code worked fine after a bit of tinkering, as exactly as it was, but I felt that the approach to invoke the web service got to be simpler and elegant in this new version. But that sample that worked as exactly as it had been, was still a matter of concern Smile, because the UI of the sample is nothing but a set of text boxes and a button and so the sample is as dull as that application was trivial. To jazz the UI up with less effort and without dealing with javascript and DHTML object model, I have pulled in the pieces of WPF/E CTP. Basic exposure to XAML and ever-expanding intellisense of VS 2005 really makes the wonders to let the developer to add the gizmos to the UI, though he is not professionally trained for that. But I really don’t know whether the designers who don’t deal with products such Expression suite, will feel the same way if they need to deal with XAML Smile. 

The installation of ASP.NET AJAX Extensions 1.0 is simple and straight forward but  the installation of WPF/E CTP SDK would require you to install something more like Visual Studio 2005 Updates to support Web Application projects. See the links http://www.microsoft.com/downloads/details.aspx?FamilyID=ca9d90fa-e8c9-42e3-aa19-08e2c027f5d6&displaylang=en. And http://msdn2.microsoft.com/en-us/asp.net/bb187452.aspx  and release notes for WPF/E for details.

Once all of them got installed, it is time to plunge into some action. As there are a lot of breaking changes between previous versions and the current version, it is better to keep this link http://ajax.asp.net/files/AspNet_AJAX_CTP_to_RC_Whitepaper.aspx handy. Though going through such documents at least cursorily, is a good time-saving process, before we plunge into new version of any technology, unfortunately I  am not the one who normally takes that approach, but I wish I had done that.

The first striking change is that even local web services can not be invoked in the client-side just like that and the web services need to specifically be attributed with “ScriptService” in order to be invoked from the client side scripts. It is a security measure to prevent accessing and exploiting  all web services through not-so-robust (or vulnerable, depends on what side you are) client technologies. Web methods would also require an extra attribute “ScriptMethod” for specific needs, though it is not mandatory one like “ScriptService” attribute. Secondly, there are attribute level changes with respect to the usage of  “ServiceReference” and to invoke web services. But overall, invocation of web services has become simpler and flexible. For example, now, generic client-side error handlers can be connected to more than one web services. But the real killer feature is the introduction of page level methods that can be exposed as web methods in this version of ASP.NET AJAX. It means that there is no need for a separate web service file such as an asmx file. If you wonder, what is the need indeed to embed the web service methods at page level as it would be contrary to the convention that they (Web Services) should be exposed from a centralized place to multiple clients, remember the fact that cross-domain invocation is still not allowable from the client-side technologies and we need to build our own bridge web service for such cross domain access. In such cases, in stead of building the wrapper local web services (.asmx files) unnecessarily, we can use the page level methods that would act as bridge methods to access remote web methods.

So, I took my old sample code and ported into new version of ASP.NET AJAX. I did a few changes in the code (highlighted below) when I took the customary approach of creating a local bridge web service to invoke the remote web service. In this approach, the solution will have a web reference to the remote web service (in this case, it is http://www.xmlme.com/WSShakespeare.asmx  and my local web service will invoke the remote web service by utilizing the proxy created by the VS 2005. The proxy for the local web service would be generated behind the screens, when the web service file is referenced through “asp:ServiceReference” at the server side. This technology would generate all relevant script code that enables the invocation seamlessly from the client side. The generated proxy would be used to invoke web methods. As these web methods are always invoked asynchronously, it is our responsibility to provide callback methods for eventual situations where the operation may succeed or fail.

Before we proceed further, let us take a look at the snapshot of the solution explorer. Ignore the folder “js” that appears in the solution explorer for a moment as we require this only when we integrate WPF/E pieces herewith.

Solution Explorer

You can see some folder inside App_WebReferences. These folders were created by VS 2005, as I used the Web Reference property of the solution in order to let the VS create a proxy for the remote web service. See the following snapshot of adding a web reference.

Adding Web Reference in VS

Now we have to create a local web service that would consume the remote web service.

The web service code (MyWebService.cs file) is given below. The point to be noted here is, there are additional attributes “ScriptService”  and “ScriptMethod” (of System.Web.Script.Services namespace) that are applied to the web service and its method. This attribute ensures that this web service can be accessed from the client side scripts. This web service in turn consumes the remote web service through the proxy, generated by the VS 2005.

using System;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.Script.Services;
using System.Net;
using System.Text;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService()]
public class MyWebService : System.Web.Services.WebService {
    com.xmlme.www.Shakespeare proxy;
   
    public MyWebService () {
       proxy = new com.xmlme.www.Shakespeare();      
    
    }
    [WebMethod][ScriptMethod()]
    public string GetInfo(string strName) {
        string strContent = proxy.GetSpeech(strName);       
       
        string strContent1 = strContent.Replace("<SPEECH>", "");
        string strContent2 = strContent1.Replace("</SPEECH>","");
        int playIndex = strContent2.IndexOf("<PLAY>");
        int playEndIndex = strContent2.IndexOf("</PLAY>");
        string playContent = strContent2.Substring(playIndex + 6, playEndIndex  - ( playIndex + 6 ) );
        string contentWithoutPlay = strContent2.Remove(playIndex, playEndIndex + 7);
       
        int speakerIndex = contentWithoutPlay.IndexOf("<SPEAKER>");
        int speakerEndIndex = contentWithoutPlay.IndexOf("</SPEAKER>");
        string speakerContent = contentWithoutPlay.Substring(speakerIndex + 9, speakerEndIndex - ( speakerIndex + 9 ) );
        string speechContent = contentWithoutPlay.Remove(speakerIndex, speakerEndIndex + 10);
        return playContent + "::" + speakerContent + "::" + speechContent;
       
    }
   
}

Now, we have to rely on the ASP.NET AJAX 1.0 to create a proxy for this local web service, to be eventually used in the client side. The following piece of code exactly does that. The element “asp:ServiceReference” creates the proxy for the local web service – MyWebService that is defined in the asmx file. The proxy creation is done at the server side and it is rendered as JavascriptObjectNotation (JSON) object to the client side scripts (Note the “runat” attribute of “asp:ScriptManager” element).

<asp:ScriptManager ID="ScriptManager1" runat="server" >
<Services>
<asp:ServiceReference Path="~/MyWebService.asmx" InlineScript="true"  />              
</Services>                     
</asp:ScriptManager>

As I had already mentioned, the web service’s method will always be executed in asynchronous manner. So, to get to know the result, we have to attach the callback methods to certain eventualities such as success or failure of the execution of web methods.

The following code provides details on how we can write client side functions that would invoke the web methods through the proxy generated through ASP.NET AJAX elements such as asp:ScriptManager and  asp:ServiceReference. “MyWebService” is the name of of class of type – web service – that is referenced in the “path” attribute of the element “asp:ServiceReference”. Inside our client side function – InvokeBiographyWebService – a web method named “GetInfo” of local web service – MyWebService” is invoked. This web method takes two parameters. First parameter is basically an input to the web method and the second parameter is the client-side callback method name that would be invoked when there is no error during the execution of the web method. As you might already have already realized by now, that there are more than these two parameters, one may need to pass depends on the complexity of the application. And we have also implemented the callback function – OnSuccess() for the web method “GetInfo” and it updates the resultant contents into html based UI elements.

<script language="javascript" type="text/javascript" >
    function InvokeBiographyWebService()
    {
        var strInput = document.getElementById('textBoxName').value;  
        MyWebService.GetInfo(strInput, OnSuccess);
    
    }
   
    function OnSuccess(result)
    {
      var strContent = result.split("::");
     
      document.getElementById('textBoxPlay').value = strContent[0];
      document.getElementById('textBoxSpeaker').value = strContent[1];
      document.getElementById('textAreaBio').value = strContent[2];
    }
    </script>

For reference, complete code is given below. If you wonder about those mysterious looking scripts that deal with “agHost” in the html, they are nothing but the script to create ActiveX controls for WPF/E content in web pages.More details on it, are provided at the end of this blog.

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>ThusSpakeShakespeare!!!</title>   
    <script type="text/javascript" src="js/aghost.js"></script>
    <script type="text/javascript" src="js/eventhandlers.js"></script>
    <script language="javascript" type="text/javascript" >
    function InvokeBiographyWebService()
    {
        var strInput = document.getElementById('textBoxName').value;  
        MyWebService.GetInfo(strInput, OnSuccess);
    
    }
   
    function OnSuccess(result)
    {
      var strContent = result.split("::");
     
      document.getElementById('textBoxPlay').value = strContent[0];
      document.getElementById('textBoxSpeaker').value = strContent[1];
      document.getElementById('textAreaBio').value = strContent[2];
    }
    </script>
</head>
<body>
    <form id="form2" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server" >
            <Services>
                <asp:ServiceReference Path="~/MyWebService.asmx" InlineScript="true"  />              
            </Services>                     
        </asp:ScriptManager>
       
        <table width="500" style="background-color: Teal" >
            <tr>
            <td>
            <div id="wpfeControl1Host" >
            <script type="text/javascript">
                  new agHost("wpfeControl1Host", // hostElementID (HTML element to put WPF/E control into)
                             "wpfeControl1",     // ID of the WPF/E ActiveX control we create
                             "500",              // Width
                             "100",              // Height
                             "black",            // Background color
                             null,               // SourceElement (name of script tag containing xaml)
                             "plugin.xaml",      // Source file
                             "true",            // IsWindowless
                             "50",               // MaxFrameRate
                             null                // OnError handler   
                            );
            </script>
            </div>
            </td>
            </tr>
            <tr>
             <td align="center" style="background-color:Lime" >
             <div id="contentPart" >
                <input id="textBoxName" type="text" runat="server" style="width: 207px" value="Type content to be searched!!" />
                <input id="btnForTextBoxName" type="button" value="Click!!!" onclick="InvokeBiographyWebService();" />
                <br/>
                <input id="textBoxPlay" type="text" style="width: 207px" value="Play" />
                <br />
                <input id="textBoxSpeaker" type="text" style="width: 207px" value="Speaker" />
                <br />
                <textarea id="textAreaBio"  visible="false" style="width: 208px; height: 80px" ></textarea>
            </div> 
            </td>
            </tr>
            <tr>
            <td>
            <div id="wpfeControl2Host" >
            <script type="text/javascript">
                  new agHost("wpfeControl2Host", // hostElementID (HTML element to put WPF/E control into)
                             "wpfeControl2",     // ID of the WPF/E ActiveX control we create
                             "500",              // Width
                             "100",              // Height
                             "black",            // Background color
                             null,               // SourceElement (name of script tag containing xaml)
                             "plugin.xaml",      // Source file
                             "true",            // IsWindowless
                             "50",               // MaxFrameRate
                             null                // OnError handler   
                            );
            </script>
        </div>
            </td>
            </tr>
        </table>
        <div>
        </div>         
    </form>
</body>

</html>

 

Having understood the ease with which we can create a proxy for the local web service and use that at the client side, we will now try the second approach, in which, we can create page level script method that can be exposed as a web method.

In the code snippet given below, I have simply cut and pasted the content from the separate web service – MyWebService - that we saw earlier. Only obvious change to be noted here is both the method and the proxy variable are defined to be “static” and it is a mandatory condition for page level web methods to stick with. Since the method is attributed with “WebMethod”, this page needs to reference “System.Web.Services” namespace. For that, we need to put the following preprocessor directive at the starting of the page.

<%@ Import Namespace="System.Web.Services" %> 

 
<script runat="server" language="C#" >
private static com.xmlme.www.Shakespeare proxy = new  com.xmlme.www.Shakespeare();  
           
[WebMethod]
public static string GetInfo(string strName)
        {
            string strContent = proxy.GetSpeech(strName);
            string strContent1 = strContent.Replace("<SPEECH>", "");
            string strContent2 = strContent1.Replace("</SPEECH>", "");
            int playIndex = strContent2.IndexOf("<PLAY>");
            int playEndIndex = strContent2.IndexOf("</PLAY>");
            string playContent = strContent2.Substring(playIndex + 6, playEndIndex - (playIndex + 6));
            string contentWithoutPlay = strContent2.Remove(playIndex, playEndIndex + 7);
            int speakerIndex = contentWithoutPlay.IndexOf("<SPEAKER>");
            int speakerEndIndex = contentWithoutPlay.IndexOf("</SPEAKER>");
            string speakerContent = contentWithoutPlay.Substring(speakerIndex + 9, speakerEndIndex - (speakerIndex + 9));
            string speechContent = contentWithoutPlay.Remove(speakerIndex, speakerEndIndex + 10);
            return playContent + "::" + speakerContent + "::" + speechContent;
}
</script>
 
Having defined the page level web method, let us see how we can consume it from inside the client-side script.
<script language="javascript" type="text/javascript" >
    function InvokeBiographyWebService()
    {
        var strInput = document.getElementById('textBoxName').value;  
        PageMethods.GetInfo(strInput, OnSuccess);   
    
    }
   
    function OnSuccess(result)
    {
      var strContent = result.split("::");
     
      document.getElementById('textBoxPlay').value = strContent[0];
      document.getElementById('textBoxSpeaker').value = strContent[1];
      document.getElementById('textAreaBio').value = strContent[2];
    }
 </script>

If you see the underlined content of the client side script that is only change that we have done on the code of the previous approach. However, it is bit tricky as it would create compilation error until you switched on the attribute “EnablePageMethods” of “asp:ScriptManager” element, as given in the following code snippet.

<asp:ScriptManager ID="ScriptManager1" EnablePageMethods="true" runat="server" >                   
</asp:ScriptManager>

The complete code is given below for the reference

<%@ Import Namespace="System.Web.Services" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>ThusSpakeShakespeare!!!</title>
    <script runat="server" language="C#" >
        private static com.xmlme.www.Shakespeare proxy = new com.xmlme.www.Shakespeare();  
          
        [WebMethod]
        public static string GetInfo(string strName)
        {
            string strContent = proxy.GetSpeech(strName);
            string strContent1 = strContent.Replace("<SPEECH>", "");
            string strContent2 = strContent1.Replace("</SPEECH>", "");
            int playIndex = strContent2.IndexOf("<PLAY>");
            int playEndIndex = strContent2.IndexOf("</PLAY>");
            string playContent = strContent2.Substring(playIndex + 6, playEndIndex - (playIndex + 6));
            string contentWithoutPlay = strContent2.Remove(playIndex, playEndIndex + 7);
            int speakerIndex = contentWithoutPlay.IndexOf("<SPEAKER>");
            int speakerEndIndex = contentWithoutPlay.IndexOf("</SPEAKER>");
            string speakerContent = contentWithoutPlay.Substring(speakerIndex + 9, speakerEndIndex - (speakerIndex + 9));
            string speechContent = contentWithoutPlay.Remove(speakerIndex, speakerEndIndex + 10);
            return playContent + "::" + speakerContent + "::" + speechContent;
        }
    </script>
    <script type="text/javascript" src="js/aghost.js"></script>
    <script type="text/javascript" src="js/eventhandlers.js"></script>
    <script language="javascript" type="text/javascript" >
    function InvokeBiographyWebService()
    {
        var strInput = document.getElementById('textBoxName').value;  
        PageMethods.GetInfo(strInput, OnSuccess);   
    
    }
   
    function OnSuccess(result)
    {
      var strContent = result.split("::");
     
      document.getElementById('textBoxPlay').value = strContent[0];
      document.getElementById('textBoxSpeaker').value = strContent[1];
      document.getElementById('textAreaBio').value = strContent[2];
    }
    </script>
</head>
<body>
    <form id="form2" runat="server">
        <asp:ScriptManager ID="ScriptManager1" EnablePageMethods="true" runat="server" >                   
        </asp:ScriptManager>
       
        <table width="500" style="background-color: Teal" >
            <tr>
            <td>
            <div id="wpfeControl1Host" >
            <script type="text/javascript">
                  new agHost("wpfeControl1Host", // hostElementID (HTML element to put WPF/E control into)
                             "wpfeControl1",     // ID of the WPF/E ActiveX control we create
                             "500",              // Width
                             "100",              // Height
                             "black",            // Background color
                             null,               // SourceElement (name of script tag containing xaml)
                             "plugin.xaml",      // Source file
                             "true",            // IsWindowless
                             "50",               // MaxFrameRate
                             null                // OnError handler   
                            );
            </script>
            </div>
            </td>
            </tr>
            <tr>
             <td align="center" style="background-color:Lime" >
             <div id="contentPart" >
                <input id="textBoxName" type="text" runat="server" style="width: 207px" value="Type content to be searched!!" />
                <input id="btnForTextBoxName" type="button" value="Click!!!" onclick="InvokeBiographyWebService();" />
                <br/>
                <input id="textBoxPlay" type="text" style="width: 207px" value="Play" />
                <br />
                <input id="textBoxSpeaker" type="text" style="width: 207px" value="Speaker" />
                <br />
                <textarea id="textAreaBio"  visible="false" style="width: 208px; height: 80px" ></textarea>
            </div> 
            </td>
            </tr>
            <tr>
            <td>
            <div id="wpfeControl2Host" >
            <script type="text/javascript">
                  new agHost("wpfeControl2Host", // hostElementID (HTML element to put WPF/E control into)
                             "wpfeControl2",     // ID of the WPF/E ActiveX control we create
                             "500",              // Width
                             "100",              // Height
                             "black",            // Background color
                             null,               // SourceElement (name of script tag containing xaml)
                             "plugin.xaml",      // Source file
                             "true",            // IsWindowless
                             "50",               // MaxFrameRate
                             null                // OnError handler   
                            );
            </script>
        </div>
            </td>
            </tr>
        </table>
        <div>
        </div>         
    </form>
</body>

</html>

And finally our client side method “InvokeBiographyWebService” is tied up with the “Click” event of the button.

<div id="contentPart" >
                <input id="textBoxName" type="text" runat="server" style="width: 207px" value="Type content to be searched!!" />
                <input id="btnForTextBoxName" type="button" value="Click!!!" onclick="InvokeBiographyWebService();" />
                <br/>
                <input id="textBoxPlay" type="text" style="width: 207px" value="Play" />
                <br />
                <input id="textBoxSpeaker" type="text" style="width: 207px" value="Speaker" />
                <br />
                <textarea id="textAreaBio"  visible="false" style="width: 208px; height: 80px"   ></textarea>
</div> 

Let us run the application and to see what we will get as the output.

Thus Spake Shakespeare

Apart from the content pulled off through client side invocation of remote web service, I hope that you might see the jazziness of the UI, though the animation and transformation techniques could not be depicted at all in that frozen snapshot Smile. The simple bells and whistles are provided through the piece of XAML and you can try out the following XAML file that creates those colorful frames, above and below to those normal html controls, to see the cool effects of basic and flexible animation. If the UI is not so cool, please remember that I am a not guy who is blessed with much aesthetic sense Smile.

<Canvas xmlns="http://schemas.microsoft.com/client/2007"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Loaded="javascript:root_Loaded" >
  <Canvas x:Name="button" Height="140" Width="550">   
    <Canvas.Background>     
      <SolidColorBrush x:Name="SolidColor" Color="white" Opacity ="20" >
      </SolidColorBrush>     
    </Canvas.Background>
    <Canvas.Triggers>
      <EventTrigger RoutedEvent="Canvas.Loaded" >
        <BeginStoryboard >
          <Storyboard>
            <ColorAnimation Storyboard.TargetName="SolidColor" Storyboard.TargetProperty="Color" From="white" To="lime" Duration="0:1:0" AutoReverse="True" RepeatBehavior="Forever"/>
          </Storyboard>
        </BeginStoryboard>
      </EventTrigger>
    </Canvas.Triggers>
    <Rectangle x:Name="ShakespeareShade" Opacity="100" Canvas.Top="0" Canvas.Left="0" Stroke="#FF8E8E8E" StrokeThickness="2" Height="100" Width="100" >
      <Rectangle.Fill> 
        <ImageBrush ImageSource="Shakespeare.jpg" Opacity="100" >
        </ImageBrush>
      </Rectangle.Fill>
      <Rectangle.RenderTransform>
        <TransformGroup>
            <RotateTransform x:Name="rotateShakespeare" Angle="45" CenterX="50" CenterY="50"/>          
        </TransformGroup>
      </Rectangle.RenderTransform>
      <Rectangle.Triggers>
        <EventTrigger RoutedEvent="Rectangle.Loaded" >
          <BeginStoryboard>
            <Storyboard>
              <DoubleAnimation Storyboard.TargetName="rotateShakespeare" Storyboard.TargetProperty="Angle" From="0" To="360" Duration="0:0:15" RepeatBehavior="Forever" />
            </Storyboard>
          </BeginStoryboard>
        </EventTrigger>
      </Rectangle.Triggers>
    </Rectangle>
      <Rectangle x:Name="Shakespeare" Opacity="100" Canvas.Top="0" Canvas.Left="10" Stroke="#FF8E8E8E" StrokeThickness="2" Height="100" Width="100" RadiusX="100" RadiusY="100"  MouseLeftButtonDown="javascript:MouseLeftButtonDownInShakespeare" >
        <Rectangle.Fill>
          <ImageBrush ImageSource="Hamlet01.jpg" Opacity="100" >
          </ImageBrush>
        </Rectangle.Fill>
        <Rectangle.RenderTransform>
          <TransformGroup>          
              <TranslateTransform x:Name="translateShakespeare" X="0" Y="0"/>          
          </TransformGroup>
        </Rectangle.RenderTransform>
        <Rectangle.Triggers>
          <EventTrigger RoutedEvent="Rectangle.Loaded" >
            <BeginStoryboard>
              <Storyboard>              
                <DoubleAnimation Storyboard.TargetName="translateShakespeare" Storyboard.TargetProperty="X" From="0" To="400" Duration="0:0:5" AutoReverse="True" RepeatBehavior="Forever" />
              </Storyboard>
            </BeginStoryboard>           
          </EventTrigger>
        </Rectangle.Triggers>
      </Rectangle>
    <Rectangle x:Name="HamletShade" Opacity="100" Canvas.Top="0" Canvas.Left="400" Stroke="#FF8E8E8E" StrokeThickness="2" Height="100" Width="100" >
      <Rectangle.Fill>
        <ImageBrush ImageSource="Shakespeare01.jpg" Opacity="100" >
        </ImageBrush>
      </Rectangle.Fill>
      <Rectangle.RenderTransform>
        <TransformGroup>
          <RotateTransform x:Name="rotateShakespeare1" Angle="45" CenterX="50" CenterY="50"/>
        </TransformGroup>
      </Rectangle.RenderTransform>
      <Rectangle.Triggers>
        <EventTrigger RoutedEvent="Rectangle.Loaded" >
          <BeginStoryboard>
            <Storyboard>
              <DoubleAnimation Storyboard.TargetName="rotateShakespeare1" Storyboard.TargetProperty="Angle" From="360" To="0" Duration="0:0:15" RepeatBehavior="Forever" />
            </Storyboard>
          </BeginStoryboard>
        </EventTrigger>
      </Rectangle.Triggers>
    </Rectangle>
    <Rectangle x:Name="Hamlet" Opacity="100"  Canvas.Top="0" Canvas.Left="410" Stroke="#FF8E8E8E" StrokeThickness="2" Height="100" Width="100" RadiusX="100" RadiusY="100" MouseLeftButtonDown="javascript:MouseLeftButtonDownInHamlet" >
      <Rectangle.Fill>
          <ImageBrush ImageSource="Macbeth.jpg" Opacity="100" >
          </ImageBrush>       
      </Rectangle.Fill>
      <Rectangle.RenderTransform>
        <TransformGroup>         
        <TranslateTransform x:Name="translateHamlet" X ="0" Y="0"/>        
        </TransformGroup>
      </Rectangle.RenderTransform>
      <Rectangle.Triggers>
        <EventTrigger RoutedEvent="Rectangle.Loaded">
          <BeginStoryboard>
            <Storyboard>             
              <DoubleAnimation Storyboard.TargetName="translateHamlet" Storyboard.TargetProperty="X" From="0" To="-400"  Duration="0:0:5" AutoReverse="True" RepeatBehavior="Forever" />
            </Storyboard>
          </BeginStoryboard>
        </EventTrigger>
      </Rectangle.Triggers>
    </Rectangle>
  </Canvas>
</Canvas>

It will be really interesting to see how the above XAML file is linked to an html control so that they can be rendered in a browser. The following script actually embeds an AactiveX control that renders the WPF/E contents in the browsers and our xaml file – plugin.xaml is linked to the control.

<script type="text/javascript">
new agHost("wpfeControl1Host", "wpfeControl1", "500", "100", "black", null, "plugin.xaml", "true", "50",  null );
</script>

This script actually creates the ActiveX control object tags. This function “agHost” has been defined in a file named agHost.js that exists in the folder “js” in the solution. As ASP.NET Ajax application solution will not contain this folder, we have to create that folder and make sure that it contains aghost.js file and another javascript file eventhandlers.js that contains custom written event handlers. The aghost.js file will basically contain the following code, which is specific to IE browser.  

if((navigator.appVersion.indexOf('MSIE') != -1)) {  
   
    try {
      
        var WPFE = new ActiveXObject("AgControl.AgControl.0.8");
       
        innerHTML = '<object id="'+id+'" width="'+width+'" height="'+height+'" classid="CLSID:32C73088-76AE-40F7-AC40-81F62CB2C1DA">';
       
      if (sourceelement != null) {
          innerHTML += ' <param name="SourceElement" value="'+sourceelement+'" />';
      }
      if (source != null) {
          innerHTML += ' <param name="Source" value="'+source+'" />';
      }
      if (framerate != null) {
          innerHTML += ' <param name="MaxFrameRate" value="'+framerate+'" />';
      }
      if (errorhandler != null) {
          innerHTML += ' <param name="OnError" value="'+errorhandler+'" />';
      }
      if (backgroundcolor != null) {
          innerHTML += ' <param name="BackgroundColor" value="'+backgroundcolor+'" />';
      }
      if (windowlessmode != null) {
          innerHTML += ' <param name="WindowlessMode" value="'+windowlessmode+'" />';
      }
      innerHTML += '<\/object>';
   
      }

It means that we can embed the ActiveX control for WPF/E in the web page, using the following code snippet too.

<object id="wpfeControl1" width="500" height="100" classid="CLSID:32C73088-76AE-40F7-AC40-81F62CB2C1DA">
         <param name="SourceElement" value="null" />
         <param name="Source" value="plugin.xaml" />
         <param name="MaxFrameRate" value="50" />
         <param name="OnError" value="null" />
         <param name="BackgroundColor" value="black" />
         <param name="WindowlessMode" value="true" />

</object>           

Another file – eventhandlers.js may contain the event handling methods that will be wired with the events of html controls of the web pages, as depicted below.

function root_Loaded(sender, args) {
    var button = sender.findName("button");
    button.mouseEnter = "javascript:handleMouseEnter";
    button.mouseLeave = "javascript:handleMouseLeave";
    button.mouseLeftButtonUp = "javascript:handleMouseUp";
    button.mouseLeftButtonDown = "javascript:handleMouseDown";
}

function MouseLeftButtonDownInShakespeare(sender, eventArgs) {   
   
    var shakespeare = sender.findName("Shakespeare");
    shakespeare.radiusX = "50";
    shakespeare.radiusY = "50";
    shakespeare.width = "50";
    shakespeare.height = "50";   
   
   
}


function MouseLeftButtonDownInHamlet(sender, eventArgs) {
   
    var hamlet = sender.findName("Hamlet");
    hamlet.radiusX = "50";
    hamlet.radiusY = "50";
    hamlet.width = "50";
    hamlet.height = "50";
   
        
}

When we integrate WPF/W along with ASP.NET Ajax supporting pages, we have to make sure that the solution also references the dll – Microsoft.Web.Preview.dll.

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Please key in the two words you see in the box to validate your identity as an authentic user and reduce spam.

Subscribe to this blog's feed

Follow us on

Blogger Profiles

Infosys on Twitter