March 2008 - Posts

We have a piece of content on this site we're working on that comes from an outside system instead of Sitecore.  The client has requested that when they are previewing the site, this particular piece of content also be shown as a preview.  Luckily, the outside system allows us to request the content by date.  But I was unsure of how to find out if the site was in preview mode or if it was, what date was being previewed.

Unsure no more!  Jens on the SDN foum came through with this extremely simple solution...  Sitecore.Configuration.State.Previewing (boolean) and Sitecore.Configuration.State.PreviewDate (DateTime).  They do exactly what you'd expect.

No idea how I would have ever found those if it weren't for the SDN forum, though.  Thanks, Jens!

Filed under:

Now, I want to do something a bit more dynamic.  How about we call a web service, get some dynamic data back, and render something based on that data?  In this post, I will build a simple example in order to clearly illustrate the concepts.  But at the end, I will post an example of something a bit more useful to show what can be done!

First off, let's define a simple web service.  I used VS2008's New Item wizard and selected "Web Service" from the dialog.  Then, I replaced HelloWorld() with my own GetData() method.  Here's what the code behind for MyService.asmx looks like:

[WebService(Namespace = "http://tempuri.org/")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

[ToolboxItem(false)]

// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.

[System.Web.Script.Services.ScriptService]

public class MyService : System.Web.Services.WebService

{

    [WebMethod]

    [ScriptMethod]

    public string[] GetData()

    {

        string[] data = new[] { "first data", "second data", "third data" };

        return data;

    }

}

Note I also have uncommented the ScriptService attribute on my class and added a ScriptMethod attribute to my method.  Calling web services from Javascript is a whole lot easier using ASP.NET AJAX.  You'll need to have the ASP.NET AJAX 1.0 Extensions installed for this to work, but it's worth it.  Oh, I'm also using the new array initialization syntax from C# 3.0 in case you've never seen it before.  A real world application would probably query a database or something.

In order to call the web service from Javascript, ASP.NET AJAX requires us to place a ScriptManager on the page with a ServiceReference pointing to the .asmx.  Here's what mine looks like:

<asp:ScriptManager ID="ScriptManager1" runat="server">

    <Services>

        <asp:ServiceReference Path="~/MyService.asmx" />

    </Services>

</asp:ScriptManager>

Thankfully, VS2008's web application project template already set up my web.config to use ASP.NET AJAX.  If you are not using VS2008, you may need to follow the directions at http://asp.net/AJAX/Documentation/Live/ConfiguringASPNETAJAX.aspx to ensure your web.config is set up properly.

At this point, I can call my web service from Javascript and react to whatever data comes back.  The question is - what should it do?  For this post, I think I'll just put up a Silverlight TextBlock and rotate through the web service data when the user clicks on the text.  Here's the XAML:

<Canvas xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

 

    <TextBlock x:Name="MyTextBlock" FontSize="10" FontFamily="Arial" Canvas.Top="10" Canvas.Left="10" Loaded="MyTextBlockLoaded" MouseLeftButtonDown="MyTextBlockClick" />

 

</Canvas>

First, I will retrieve the data from the web service once the text block has loaded by using the Loaded event.  Let me show you the Javascript and then I will explain it...

var index;

var data;

 

function MyTextBlockLoaded( sender, args )

{

    MyService.GetData( GetDataSuccess, GetDataFailed );

}

 

function GetDataSuccess( result )

{

    data = result;

    index = 0;

    UpdateTextBlock();

}

 

function GetDataFailed()

{

    alert( 'Call to MyService.GetData() failed!' );

}

 

function UpdateTextBlock()

{

    var textblock = document.getElementById( '<%= Silverlight1.ControlId %>' ).content.findName( 'MyTextBlock' );

    textblock.Text = data[index];

}

First I declare a variable to hold the current index into the data array and a variable to hold the data itself.  The implementation of the MyTextBlockLoaded() event handler is calling the web service using the proxy built by ASP.NET AJAX.  Note that because this is an asynchronous call, you have to pass in event handlers for both success and failure - that is where my GetDataSuccess() and GetDataFailed() functions come from.  On success, I store the data, set the initial index to 0, and call the function to set the text in the TextBlock.  Note I'm using document.getElementById() to find the Silverlight viewer.  I'm using some inline server-side script to get the Id of the Silverlight viewer from my control (covered in a previous post) to pass to getElementById().  The viewer object has a content property upon which I can call findName() (also covered in a previous blog post) to get a reference to the TextBlock.

The last thing to do is handle the MouseLeftButtonDown event on the TextBlock so that the text changes when the user clicks on it.  Here's that Javascript function:

function MyTextBlockClick( sender, args )

{

    if ( index >= data.length-1 )

    {

        index = 0;

    }

    else

    {

        index = index + 1;

    }

 

    UpdateTextBlock();

}

Here, I increment the index and when the index reaches the end of the list, I reset it to 0 to go back to the beginning.  Then I call UpdateTextBlock() to display the new text.

You can see this example in action at http://www.killeverything.com/zak/Silverlight1/WebService1.aspx.

At the beginning of this post, I promised to show a more useful example.  How about a Silverlight app that displays headlines from an RSS feed and lets you click through to the articles?  If you've been reading my Silverlight posts, there shouldn't be anything too new going on here.  I have just combined several of the concepts I've been learning and blogging about into a single Silverlight app.  You can see it running here: http://www.killeverything.com/zak/Silverlight1/WebService2.aspx and the code for both the simple example and the RSS headline viewer are attached to this post.  If you're a .NET head, you might want to look at the source for the web service where I used LINQ to XML to make parsing the RSS feed easy as cake.  Oh, and I want to point out the clipping I had to add to the TextBlock.  Support for MaxHeight and MaxWidth may be coming in Silverlight 2.0, but clipping is how it's done for now...

Putting a Silverlight viewer on a web page is a lot of work.  You have to import the Silverlight.js file (and make sure it's deployed to the web server), create a div to contain the viewer, and write some Javascript code to call Silverlight.createObject().  To me, this is just begging for an ASP.NET control - so I whipped up one of my own.

So what do I want my control to do for me?  Let's start at the top with the Silverlight.js file.  I want this to be an embedded resource so that I don't always have to remember to copy it in to a new project.  If you're not familiar with this technique, it's been around since .NET 2.0 - keep up! :)  You just throw the Silverlight.js file into your project and set it's build action to "Embedded Resource".  Here's a screenshot:

image

Add it to your project's AssemblyInfo.cs like this:

// Embeded Resources

[assembly: System.Web.UI.WebResource("knw.Silverlight.Silverlight.js", "application/x-javascript")]

Then my control's code tells the page where to find it like this:

Page.ClientScript.RegisterClientScriptInclude("Silverlight.js", Page.ClientScript.GetWebResourceUrl(this.GetType(), "knw.Silverlight.Silverlight.js"));

Here is a link to an article on Code Project that explains the technique in a bit more detail.

Next, I want my control to render the container div for the Silverlight viewer.  This is pretty straightforward - I just override Render() and use the HtmlTextWriter that's passed in.  Here's what it looks like:

protected override void Render(HtmlTextWriter writer)

{

    base.Render(writer);

 

    writer.AddAttribute("id", ID + "Div");

    writer.RenderBeginTag(HtmlTextWriterTag.Div);

    writer.RenderEndTag();

}

Note I'm just concatenating "Div" onto the existing ID of my control when building the div's id, but that should make it unique.

The only thing that's left is the call to Silverlight.createObject().  There are a few variables in this call that I thought could be exposed as properties of my control.  Specifically, the path to the Xaml document, the height and width of the viewer, and the background color.  I'm using C# 3.0, so I took advantage of another language feature - automatic properties.  Here's what that code looks like:

public string Xaml { get; set; }

public int Height { get; set; }

public int Width { get; set; }

public Color BackgroundColor { get; set; }

Lastly, I need to build the actual javascript to create the Silverlight viewer and register it with the page to run as a startup script.  Like this:

string createObjectScript = string.Format("<script>Silverlight.createObject( \"{0}\", document.getElementById( \"{1}Div\" ), \"{2}Ctrl\", {{ width:'{3}', height:'{4}', inplaceInstallPrompt:false,background:'{5}', isWindowless:'false', framerate:'24', version:'1.0' }},{{ onError:null,onLoad:null }},null );</script>", Xaml, ID, ID, Width, Height, ColorTranslator.ToHtml(BackgroundColor) );

 

Page.ClientScript.RegisterStartupScript( this.GetType(), ID + "Create", createObjectScript );

At first glance, that code looks pretty confusing.  All it's actually doing is using string.Format() to build the call to Silverlight.createObject().  {0} is the path to the Xaml, {1} is the ID of the control + "Div" (the id of container div), {2} is the ID of the control + "Ctrl" (a unique id for the viewer itself), {3} is the width, {4} is the height, and {5} is the background color (converted from a System.Drawing.Color using the System.Drawing.ColorTranslator class).  Oh yeah, and you have to escape curly braces when using string.Format() by doubling them.

Ok, that was a lot of work - what has it bought me?  Well, now when I want to use Silverlight on an .aspx page, all I have to do is add a reference to my DLL, register it at the top of the .aspx page, and put the control on the .aspx page somewhere.  Which looks like this:

<knw:Silverlight id="Silverlight1" runat="server" Xaml="MySilverlight.xaml" BackgroundColor="Gray" Height="200" Width="200" />

I will attach the code for both the control and an example of using it to this post.  I'll be using this control in future Silverlight posts here - plus, I do intend to extend it to do more in the future (as I learn more about Silverlight).  So, I'll try to post updates here from time to time.  I hope it's useful to someone! :)

Filed under: ,

Seems I've fallen way behind on my Silverlight blogging as of late.  A few weeks ago, I managed to get some simple animation working.  So, I guess I should blog about it.

Animation is done by transforming your object.  Each graphic element has a RenderTransform property to which you can assign a Transform object to modify your element in some way.  The different types of Transform objects include RotateTransform, SkewTransform, ScaleTransform, and TranslateTransform.  Let's look at an example.

<Ellipse MouseLeftButtonDown="EllipseClick" Height="50" Width="100" Canvas.Top="50" Canvas.Left="50" Fill="Black">

    <Ellipse.RenderTransform>

        <RotateTransform x:Name="EllipseXForm" Angle="90" />

    </Ellipse.RenderTransform>

</Ellipse>

This example is pretty boring - it just renders a 50 pixel high and 100 pixel wide ellipse, rotated 90 degrees (so that it instead appears 100 pixels high and 50 pixels wide).  However, it illustrates the effect that a RotateTransform will have on an object.  Also note that I have named the transform (the x:Name attribute).  The purpose of that will soon become apparent.

Animations are defined within objects called Storyboards.  Basically, a Storyboard defines a timeline of changes that should occur to properties of objects.  For my example, I want to change the Angle property of the RotateTransform object over time (so that the Ellipse rotates slowly as we watch).  Here is the Storyboard I have defined:

<Canvas.Resources>

    <Storyboard x:Name="EllipseStoryboard">

        <DoubleAnimation Storyboard.TargetName="EllipseXForm" Storyboard.TargetProperty="Angle" To="0" Duration="00:00:05" />

    </Storyboard>

</Canvas.Resources>

For this example, I have used a DoubleAnimation.  This is because the property I want to change ("Angle") is of type double.  There are many other animation objects available such as DecimalAnimation, Int32Animation, and ColorAnimation.  The Storyboard.Targetname attribute tells the animation which object it is changing (here's where I use the name I gave my transform earlier) and Storyboard.TargetProperty indicates what property will be changed.  The To attribute is the value the animation should end with and the Duration should be self-explanatory.  Note again, I have named my Storyboard so that I can reference it later.  Oh yeah, and the Storyboard is a resource, so it's defined inside the <Canvas.Resources /> element.

I'm going to need something to trigger the animation to begin.  I've chosen to do it using a mouse click on the ellipse.  So, I will need to add a MouseLeftButtonDown handler to the Ellipse.  Now it looks like this:

<Ellipse MouseLeftButtonDown="EllipseClick" Height="50" Width="100" Canvas.Top="50" Canvas.Left="50" Fill="Black">...

And I need an EllipseClick() function defined in Javascript, so here's what I came up with:

<script type="text/javascript">

    function EllipseClick( sender, args )

    {

        sender.findName("EllipseStoryboard").Begin();

    }

</script>

I placed this directly in my .aspx file for this example.  I don't think I've blogged about findName() before, but Silverlight elements define this method and it allows you to get a reference to any other Silverlight element by name.  Here, I am using it to get the "EllipseStoryboard" Storyboard and executing it's Begin() method.

You can see this example in action at http://www.killeverything.com/zak/Silverlight1/EllipseRotate1.aspx

It's not quite what I wanted, though.  The rotation is happening around the top left corner of the ellipse instead of the center.  Luckily, this was easy to fix - the RotateTransform has CenterX and CenterY properties that allow you to define the point around which the object rotates.  Knowing that the ellipse is 100 wide and 50 tall, this is what I changed the transform to look like:

<RotateTransform x:Name="EllipseXForm" Angle="90" CenterX="50" CenterY="25" />

You can see the finished example at http://www.killeverything.com/zak/Silverlight1/EllipseRotate2.aspx

I've also attached a ZIP with the files needed for these examples in case you want to play with them yourself.

Filed under: ,

It seems several people still haven't heard about Crestone, so thought I'd make a quick mention of it here...  The new Sitecore release (due out mid-year, hopefully) is code-named Crestone.  The beta program is just spinning up, but a few details have leaked out already.  64-bit support is one of the big features.  Also, in-line editing in web preview mode (I've seen this in other CMS's before and it's a huge selling point with customers), a new page layout tool, and a whole new security engine.

Yes, I'm going to apply for the beta program.  If I'm accepted, I won't be able to blog about it for awhile though, unfortunately.  As soon as I have more information to share though, I will!

with no comments
Filed under: ,