Wednesday, October 10, 2007

Wrox Blox and Silverlight Animation

Wrox has launched a new series of technology shorts named 'Wrox Blox' (formerly eBriefs). More in depth than your typical article yet more concise than your average book, these technology shorts are great for getting up to speed quickly on new technologies and subjects of interest.

 

From the Wrox website:

"Written by the best programmers in the their fields, Wrox Blox are short informational pieces and code that will help get you up-to-speed and keep you up-to-date."

 

As part of the launch, I have contributed a technology short titled Silverlight 1.0 Animation. It teaches you how to create animations using Microsoft's new platform for building rich Internet applications — Silverlight. It also introduces animation concepts and answers questions such as when and why you want to use animation in your Web development. I hope this short, and the associated code, will give you a good foundation for creating animations. More importantly, I hope you enjoy it!

 

Three other shorts have also been released as part of the Wrox Blox launch:

  • Leverage LINQ in ASP.NET 3.5 Projects, by Roger Jennings
  • iPhone and iPod Touch Programming, by Richard Wagner
  • Building a Photo Gallery with Adobe AIR, by Todd. A. Anderson

....and of course, many more interesting shorts to come!

Thursday, August 09, 2007

Silverlight: Useful Tips for Working with the Latest Bits and Tool Previews

The latest bits were just released for Silverlight 1.0 and 1.1 along with updated previews of Visual Studio 2008 Beta 2Silverlight Tools for Visual Studio 2008 Beta 2, Expression Blend August Preview, ASP.NET Futures, etc. That's a lot of updates - and a lot of hassle - but definitely well worth the effort.

 

Of course, with every refresh of bits and update of tools, there's bound to be a few issues. Here are a few links to articles written by some very smart people out there in the "blogoshpere" that I found valuable and in some cases quite helpful:

 

So you just installed Visual Studio 2008 Beta 2 and you don't get intellisense or colorization for your JavaScript code? Yup, this happened to me too. Check out this post on the webdevtools blog.

 

Ok, if you had intellisense issues and read the last post, they should be working now. Once the JavaScript editor is playing nice, why not improve the intellisense a bit for Silverlight now? Check out this project from Justin-Josef:  

 

Are your AJAX applications are not behaving in Visual Studio 2008 Beta 2? First, check your project's target framework and then read this article if you are still having issues. 

 

Are you working with PNG or GIF files in your Silverlight application? Experiencing seemingly unexplainable issues with your Silverlight Image control? If so, read this post by Rob Conery (about 1/2 way down the page) (Note: I had these issues as well and the answer was 'JPEGs')

 

If you don't enjoy editing your Silverlight project's JavaScript files in notepad from Expression Blend, here's a nice workaround posted by Microsoft Evangelist Jaime Rodriguez.

 

If you want your 'split views' to display vertically in Visual Studio 2008, Scott Gu has your answer.

 

Finally, a word about input and hit-testing. If you have a Silverlight element overlaying another element, but you want the element underneath to capture input, what do you do? Jaime Rodriguez writes about the IsHitTestVisible property, which will solve your problems.

Tuesday, July 03, 2007

Silverlight: Resizing a Canvas and its Child Elements - Together

I was reading through the Silverlight forums this evening when I came across a seemingly obvious question, that had an answer that surpised me. Well, it probably wouldn't have surprised anyone else, but hey...

The question asked was basically 'When I resize a Canvas object, how do I resize all of the child elements of the Canvas as well?'.

My first thought was to iterate over the children collection of the Canvas, increasing each child elements Width and Height properties. And so I set off to try it out. Sure enough it worked, but the child elements started overlapping each other as they increased in size, which wasn't part of the plan. So I simply reset the Canvas.Top and Canvas.Left properties as well, and that worked, problem solved. Well, there is a much simpler way. Another developer posted the answer, which should have been obvious: Simply use a ScaleTransform to scale the Canvas, and all children will scale as well. Ahhh yes, much easier!

The following code shows both methods, and I know which one I will use next time I need to resize a canvas and its child elements:

The XAML:
<Canvas  
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="640"
Height="480"
>
<
Canvas
x:Name="CanvasToResize"
Width="272"
Height="168"
Background="#FF2D3F81"
RenderTransformOrigin="0.0,0.0"
MouseLeftButtonDown="resize"
>
<
Canvas.RenderTransform>
<
ScaleTransform
x:Name="scale"
ScaleX="1"
ScaleY="1"
/>
</
Canvas.RenderTransform>
<
Rectangle
Fill="#FFFF1919"
Stroke="#FFFFFFFF"
Width="72"
Height="32"
Canvas.Left="32"
Canvas.Top="24"
/>
<
Ellipse
Fill="#FFDB6969"
Width="40"
Height="40"
Canvas.Left="136"
Canvas.Top="88"
/>
<
Path
Fill="#FFFFFFFF"
Stretch="Fill"
Stroke="#FF000000"
Width="65"
Height="1"
Canvas.Left="199.5"
Canvas.Top="149.5"
Data="M320,248 L384,248"
/>
</
Canvas>
</
Canvas>

JavaScript - Method #1: Manual Resize:
var resizeBy = 1.1;

function resize(sender, args) {

var canvas = sender.findName("CanvasToResize");

for (var i=0; i<canvas.children.count; i++) {

var child = canvas.children.getItem(i);

child.Width = child.Width*resizeBy;
child.Height = child.Height*resizeBy;

var top = child.getValue("Canvas.Top");
var left = child.getValue("Canvas.Left");

child.setValue("Canvas.Top", top*resizeBy);
child.setValue("Canvas.Left", left*resizeBy);
}

canvas.Width = canvas.Width*resizeBy;
canvas.Height = canvas.Height*resizeBy;
}

JavaScript - Method #2: Scale to Resize
var resizeBy = 1.1;

function resize(sender, args) {

var st = sender.findName("scale");

var scaleX = st.getValue("ScaleX");
var scaleY = st.getValue("ScaleY");

st.setValue("ScaleX", scaleX*resizeBy);
st.setValue("ScaleY", scaleY*resizeBy);
}

Monday, April 30, 2007

Silverlight Makes a Big Splash Today...

If you are paying attention to the Silverlight blog-o-sphere today, you know that there were a lot of big announcements at Mix 07!

Tim Sneath posted a bunch of screencasts on Silverlight highlighting some cool features and how-to's. (my favorites are the Video Overlay and Video Scrubbing tutorials).

Mike Harsh announced on his blog that CLR support is now ready for a test drive in Silverlight. I've been excitedly waiting for this for quite some time. A few other exciting links can be found here as well.

Laurence Moroney also announced a new Silverlight community is online http://silverlight.net/Default.aspx and it looks like there's some great content there.

Keep an eye on the blogs... more great info is sure to be popping up quickly as Silverlight momentum seems to be picking up!

Sunday, January 07, 2007

Microsoft Offers Free SharePoint 2007 eClinics

Microsoft is offering two free 2-hour eClinics on the following Office 2007 subjects:


  • Microsoft Windows SharePoint Services 3.0 (WSS)

  • Microsoft Office SharePoint Server 2007 (MOSS)



These are an excellent way to get up to speed quickly on the capabilities of these new and upgraded products.

Here are the links.. hope you enjoy:

Clinic 5045: Inside Look at Developing with Microsoft® Windows® SharePoint® Services 3.0

Clinic 5046: Inside Look at Building and Developing Solutions with Microsoft® Office SharePoint® Server 2007

Thursday, December 07, 2006

WPF Composite Transformations - Order Matters

Today, I got it in my head that I wanted to create some code that could break apart an image and then reconstruct it using animation. I wanted the image to simply “tear” apart, piece by piece, one piece at a time. Using WPF I was able to make this happen.

 

The code to do this is actually very straight forward. In XAML, I simply created a Grid object and gave it a few column and row definitions. The columns and rows will determine how my image gets “sliced”. In the code-behind, I simply grab a source image (defined as a resource in XAML), iterate the rows and columns of the Grid, and place a canvas in each cell using an ImageBrush to paint the canvas background. Using the ImageBrush Viewbox property, you can set the correct area of the image to place into each canvas, essentially recreating the entire image piece by piece.

 

With an image sliced, the final step was simply to create a TranslateTransform and a RotateTransform, and then animate the individual slices. I wanted the image slices to “drop” one at a time, so, in order to stagger the animation for each image slice, I simply set the BeginTime property for the animation to a value which takes into account which slice we’re animating. 

 

Of course, nearing completion, I did stumble on one "gotcha". My transformations just weren't working properly. What I’m doing is translating (moving) each image slice down along the y-axis. At the same time I’m applying a rotation to simply give a nice "falling down" effect. Each transformation is added to a TransformGroup object, and applied to the image slice (a.k.a. the canvas.RenderTransform property). Long story short, it just wasn’t working correctly, and after much frustration, I stumbled on the fact that if I added my RotateTransform to the TransformGroup prior to adding the TranslateTransform, all worked well. Back to the MSDN I went, and found my answer - order matters when creating composite transformations.

 

You can read about it here:

 

http://msdn2.microsoft.com/en-us/library/system.windows.media.transformgroup.aspx

 

The results (click image for larger view):

 

The code to recreate the solution is below. If you swap the order of the RotateTransform and TranslateTransform, you'll see the behavior changes quite a bit.

 

Window1.xaml

 

<Window x:Class="PictureTearDown.Window1"

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  Title="Image Effect"

  Width="725" Height="440"

  >

 

  <Window.Resources>

    <Image x:Key="TheImage" Source="Images\Crystal.jpg"/>

  </Window.Resources>

 

  <StackPanel ClipToBounds="True"

    Margin="10" Orientation="Vertical">

 

    <Border CornerRadius="5" Width="{Binding

      ElementName=TheImage,   

      Path=Width}" Height="{Binding ElementName=TheImage,

      Path=Height}"

      BorderBrush="Black" BorderThickness="0.5"  

      Background="Transparent" ClipToBounds="True">

 

      <Grid Name="ImageGrid"

        Width="{Binding ElementName=TheImage, Path=Width}"

        Height="{Binding ElementName=TheImage, Path=Height}"

        Margin="10" HorizontalAlignment="Center"

        VerticalAlignment="Center">

 

        <Grid.ColumnDefinitions>

          <ColumnDefinition Width="*"/>

          <ColumnDefinition Width="*"/>

          <ColumnDefinition Width="*"/>

          <ColumnDefinition Width="*"/>

          <ColumnDefinition Width="*"/>

          <ColumnDefinition Width="*"/>

        </Grid.ColumnDefinitions>

 

        <Grid.RowDefinitions>

          <RowDefinition Height="*"/>

          <RowDefinition Height="*"/>

          <RowDefinition Height="*"/>

          <RowDefinition Height="*"/>

          <RowDefinition Height="*"/>

          <RowDefinition Height="*"/>

          <RowDefinition Height="*"/>

          <RowDefinition Height="*"/>

        </Grid.RowDefinitions>

 

      </Grid>

 

    </Border>

 

    <Button Width="100" Click="Animate">Animate!</Button>

 

  </StackPanel>

 

</Window>

 

Window1.xaml.cs

 

using System;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Media;

using System.Windows.Media.Animation;

 

namespace PictureTearDown

{

 

  public partial class Window1 : System.Windows.Window

  {

 

    public Window1()

    {

      InitializeComponent();

      SliceAndDice();

    }

 

    private void SliceAndDice()

    {

      Image image = (Image)this.Resources["TheImage"];

 

      int rows = this.ImageGrid.RowDefinitions.Count;

      int cols = this.ImageGrid.ColumnDefinitions.Count;

 

      // --- slice image

      double cellH = image.Source.Height / rows;

      double cellW = image.Source.Width / cols;

 

      for (int row = 0; row < rows; row++)

      {

        for (int column = 0; column < cols; column++)

        {

          Canvas canvas = new Canvas();

 

          canvas.Height = cellH;

          canvas.Width = cellW;

 

          canvas.SetValue(Grid.ColumnProperty, column);

          canvas.SetValue(Grid.RowProperty, row);

 

          ImageBrush ib = new ImageBrush(image.Source);

 

          float sizeCols = 1.0f / cols;

          float sizeRows = 1.0f / rows;

          ib.Viewbox = new Rect(column * sizeCols, row * 

            sizeRows,

            sizeCols, sizeRows);

          ib.ViewboxUnits =

            BrushMappingMode.RelativeToBoundingBox;

 

          canvas.Background = ib;

          this.ImageGrid.Children.Add(canvas);

        }

      }

    }

 

    private void Animate(object sender, RoutedEventArgs e)

    {

      Image image = (Image)this.Resources["TheImage"];

 

      int item = 0;

      int zindex = 100;

      double animationSpeed = 1000;

 

      int rows = this.ImageGrid.RowDefinitions.Count;

      int cols = this.ImageGrid.ColumnDefinitions.Count;

 

      double cellH = image.Source.Height / rows;

      double cellW = image.Source.Width / cols;

 

      for (int row = 0; row < rows; row++)

      {

        for (int column = 0; column < cols; column++)

        {

          Canvas canvas = (Canvas)this.ImageGrid.Children

            [item];

          canvas.SetValue(Canvas.ZIndexProperty, zindex);

          canvas.ClipToBounds = false;

 

          // --- the order which transforms are added to a

          // --- transformgroup is important!

          TransformGroup group = new TransformGroup();

          RotateTransform rotate = new RotateTransform();

          TranslateTransform translate = new TranslateTransform

            ();

          group.Children.Add(rotate);

          group.Children.Add(translate);

          canvas.RenderTransform = group;

 

          int delay = 100 * item;

 

          // --- translate cell canvas

          DoubleAnimation translateAnimation = new

            DoubleAnimation(image.Source.Height*2,

              TimeSpan.FromMilliseconds(animationSpeed));

          translateAnimation.BeginTime = new TimeSpan(0, 0, 0,

            0, delay);

          translateAnimation.AutoReverse = true;

          translate.BeginAnimation

            (TranslateTransform.YProperty,

            translateAnimation);

 

          // --- rotate cell.canvas.background.imagebrush

          rotate.CenterX = cellH / 2;

          rotate.CenterY = cellW / 2;

          DoubleAnimation rotAnimation = new DoubleAnimation

            (720,

            TimeSpan.FromMilliseconds(1000));

          rotAnimation.BeginTime = new TimeSpan(0, 0, 0, 0,

            delay);

          rotAnimation.AutoReverse = true;

          rotate.BeginAnimation(RotateTransform.AngleProperty,

            rotAnimation);

 

          item++;

          zindex--;

        }

      }

    }

 

  }

}

 

 

 





Technorati Tags:
, , , ,

Wednesday, November 01, 2006

WPF Routed Event Model - Part 1

WPF introduces a rich new event model for windows development. In windows forms development, an event is directed to a single object, usually the control that initiated the event. In WPF, events are routed through the Visual Tree, offering far greater flexibility in what we can do with the event and which element or elements can respond to it.

 

To understand the WPF Routed Event Model, we first need to understand how WPF views UI elements and the relationships between them. WPF introduces a new composition model in which UI elements contain other elements. WPF also introduces the the Visual Tree, which represents the hierarchy of UI elements which result from the new composition model. Let’s look at a sample element hierarchy:

 

--Window

---- Grid

------ Canvas

-------- TextBlock

 

As shown above, parent-child relationships are formed between the elements. The Window is a top-level element which contains a Grid, which contains a Canvas, which contains a TextBlock.

 

Let’s create a window in XAML with a few elements and wire-up a simple event which will show a MessageBox to the user when the TextBlock is clicked. Note that TextBlock does not have a Click event, so we’ll use a MouseDown event instead. Utilizing our element hierarchy above the simplified XAML and event wire-up looks like this:

 

<Window x:Class="RoutedEvents.SimpleRoutedEvents"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="Simple Routed Events" Height="300" Width="300"

       Name="window1"

> 

 <Grid Name="grid1">

  <Canvas Name="canvas1" Background="Khaki" Width="100" Height="100">

   <TextBlock Name="textblock1" Foreground="Red" Canvas.Top="40"   Canvas.Left="25" MouseDown="textblock1_MouseDown">Press Me!</TextBlock>

  </Canvas>

 </Grid>

</Window>

 

In our code behind we’d place the following event handler:

 

private void textblock1_MouseDown(object sender, RoutedEventArgs e)

{

    MessageBox.Show("TextBlock Clicked");

}

 

On the surface, this is very similar to wiring an event to a Windows Forms control. In our XAML code we have declared a MouseDown event attribute for our TextBlock element and in our code-behind we’ve defined the MouseDown event handler. When we run the example, the MessageBox is displayed as expected when we mouse down on the TextBlock. Not much to it and not very exciting so far but it gets better. Now that we understand how WPF views content (as a hierarchical element tree) and how we can wire an event to an element, we can talk about how WPF works with routed events.

 

The WPF routed event model allows us to take advantage of the fact that our window elements participate in a hierarchy or parent-child relationships. Since WPF knows the relationships, it can ‘route’ the event message up and down the Visual Tree, and all of our elements have an opportunity to handle the message. To clarify a bit, by “all elements”, I mean all elements from the top-level container element (usually Window or Page) down to the element that generated the event.

 

The WPF Event Model defines three types of event routing strategies:

 

Bubbling

The event message traverses the Visual Tree upwards from the element that originated the event to the top-level element (Window or Page)

Tunneling

The event message traverses the Visual Tree downwards from the top-level container (Window or Page) to the target element, which is the element that originated the event.

Direct

Only the element that raised the event can handle the event. Similar to Windows Forms event handling.

 

There is a naming convention for Bubbling and Tunneling event names defined in the WPF framework. Tunneling events are prefixed with ‘Preview”, so using MouseDown as an example, the Bubble event would be named MouseDown, and the tunneling event would be named PreviewMouseDown. Bubble and Tunnel event strategies usually are provided in pairs, when one exists you will usually find the other.

 

Let’s modify our code so we can see the event routing strategies in action. First the XAML:

 

<Window x:Class="RoutedEvents.SimpleRoutedEvents"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="Simple Routed Events" Height="300" Width="300"

       Name="window1" PreviewMouseDown="GenericRoutedEventHandler" MouseDown="GenericRoutedEventHandler"

    >

       <Grid Name="grid1" PreviewMouseDown="GenericRoutedEventHandler" MouseDown="GenericRoutedEventHandler">

              <Canvas Name="canvas1" Background="Khaki" Width="100" Height="100" PreviewMouseDown="GenericRoutedEventHandler" MouseDown="GenericRoutedEventHandler">

                     <TextBlock Name="textblock1" Foreground="Red" Canvas.Top="40" Canvas.Left="25" PreviewMouseDown="GenericRoutedEventHandler" MouseDown="GenericRoutedEventHandler">Press Me!</TextBlock>

              </Canvas>

    </Grid>

</Window>

 

We’ve simply added PreviewMouseDown and MouseDown event declarations to each of our elements, and pointed them all at a generic event handler so we can watch what happens. The code for our generic event handler follows:

 

private void GenericRoutedEventHandler(object sender, RoutedEventArgs e)

{

string name = ((FrameworkElement)sender).Name.ToString();

Console.WriteLine(name + " " + e.RoutedEvent.Name.ToString() + " " + e.RoutedEvent.RoutingStrategy.ToString());

}

 

Our generic event handler simply writes to the console the name of the element, the name of the event, and the event strategy. Running the example yields the following results:

 

window1 PreviewMouseDown Tunnel

grid1 PreviewMouseDown Tunnel

canvas1 PreviewMouseDown Tunnel

textblock1 PreviewMouseDown Tunnel

textblock1 MouseDown Bubble

canvas1 MouseDown Bubble

grid1 MouseDown Bubble

window1 MouseDown Bubble

 

In our results we can see that although we clicked the TextBlock, event handlers fired for all of our elements. We ‘tunneled’ from our top level element (Window) down to our target element (TextBlock) and then ‘bubbled’ back up to our top level element again. What’s interesting about this is that all elements are notified of the event occurance and all elements have a chance to handle the event. This is an important concept in WPF because events are not tied to individual controls as in Windows Forms programming, but rather there is an event notification system that alerts all elements in a container that an event occurred, and any element may participate in handling that event.

 

How can we use this? Let’s assume we want our Window container element to show the MessageBox on behalf of any of its contained elements. The message box will show the name of the element that originated the event. (This is just a generic example, in real life we might create a control that contains multiple visual elements, like a border, text, and rectangle, but we want a click event for the entire control.) We can simply add a handler for the window elements MouseDown event and show the message box. The modified XAML follows:

 

<Window x:Class="RoutedEvents.SimpleRoutedEvents"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="Simple Routed Events" Height="300" Width="300"

       Name="window1" MouseDown="window1_MouseDown"

    >

       <Grid Name="grid1">

              <Canvas Name="canvas1" Background="Khaki" Width="100" Height="100">

                     <TextBlock Name="textblock1" Foreground="Red" Canvas.Top="40" Canvas.Left="25">Press Me!</TextBlock>

              </Canvas>

    </Grid>

</Window>

 

And our modified code-behind looks like so:

 

private void window1_MouseDown(object sender, RoutedEventArgs e)

{

  string name = ((FrameworkElement)e.OriginalSource).Name.ToString();

  MessageBox.Show(name);

}

 

And there you have it, a quick intro to WPF event routing. In the interest of keeping this post simple, I purposely avoided the use of a Button element as there are more details to the routing model base classes that have an effect on the Button element. I will address that soon in part 2.

 

You can read more about events on the  MSDN

 

 

 

 

 

 




Technorati Tags:
, , , , ,