Tuesday, August 11, 2009

Five Minute Silverlight 3 Aides-Memoire #5 – Based on Styles

    <UserControl.Resources>
<
Style x:Key="ColouredContentControlStyle" TargetType="ContentControl">
<
Setter Property="Height" Value="30" />
<
Setter Property="Width" Value="100" />
<
Setter Property="Margin" Value="10" />
<
Setter Property="FontFamily" Value="Verdana" />
<
Setter Property="FontSize" Value="14" />
<
Setter Property="FontStyle" Value="Italic" />
</
Style>

<
Style x:Key="ColouredCheckBoxStyle" BasedOn="{StaticResource ColouredContentControlStyle}" TargetType="CheckBox">
<
Setter Property="Width" Value="154" />
</
Style>

<
Style x:Key="RedButtonStyle" BasedOn="{StaticResource ColouredContentControlStyle}" TargetType="Button">
<
Setter Property="Foreground" Value="Red" />
</
Style>
<
Style x:Key="GreenButtonStyle" BasedOn="{StaticResource ColouredContentControlStyle}" TargetType="Button">
<
Setter Property="Foreground" Value="Green" />
</
Style>
<
Style x:Key="YellowCheckBoxStyle" BasedOn="{StaticResource ColouredCheckBoxStyle}" TargetType="CheckBox">
<
Setter Property="Foreground" Value="Yellow" />
</
Style>
<
Style x:Key="OrangeRadioButtonStyle" BasedOn="{StaticResource ColouredContentControlStyle}" TargetType="RadioButton">
<
Setter Property="Foreground" Value="Orange" />
<
Setter Property="Width" Value="190" />
<
Setter Property="FontWeight" Value="Bold" />
</
Style>
</
UserControl.Resources>
<
Grid x:Name="LayoutRoot">
<
Grid.Background>
<
LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="Black" Offset="0"/>
<
GradientStop Color="White" Offset="1"/>
</
LinearGradientBrush>
</
Grid.Background>
<
StackPanel Orientation="Vertical" Margin="20">
<
Button Style="{StaticResource RedButtonStyle}" Content="Red Button" />
<
CheckBox Style="{StaticResource YellowCheckBoxStyle}" Content="Yellow CheckBox" />
<
RadioButton Style="{StaticResource OrangeRadioButtonStyle}" Content="Orange RadioButton" />
<
Button Style="{StaticResource GreenButtonStyle}" Content="Green Button" />
</
StackPanel>
</
Grid>


FiveMinuteSilverlight3_5

Five Minute Silverlight 3 Aides-Memoire #4 – Merged Resource Dictionaries

ButtonStyles.xaml:

<ResourceDictionary 
x:Name="ButtonsStyles"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<
Style x:Key="RedButton" TargetType="Button">
<
Setter Property="Foreground" Value="Red" />
<
Setter Property="Height" Value="30" />
<
Setter Property="Width" Value="100" />
<
Setter Property="Margin" Value="10" />
</
Style>
<
Style x:Key="GreenButton" TargetType="Button">
<
Setter Property="Foreground" Value="Green" />
<
Setter Property="Height" Value="30" />
<
Setter Property="Width" Value="100" />
<
Setter Property="Margin" Value="10" />
</
Style>
</
ResourceDictionary>


CheckBoxStyles.xaml:




<ResourceDictionary 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="CheckBoxStyles"
>
<
Style x:Key="YellowCheckBox" TargetType="CheckBox">
<
Setter Property="Foreground" Value="Yellow" />
<
Setter Property="Height" Value="30" />
<
Setter Property="Margin" Value="10" />
</
Style>
<
Style x:Key="OrangeCheckBox" TargetType="CheckBox">
<
Setter Property="Foreground" Value="Orange" />
<
Setter Property="Height" Value="30" />
<
Setter Property="Margin" Value="10" />
</
Style>
</
ResourceDictionary>


MainPage.xaml:




<UserControl.Resources>
<
ResourceDictionary>
<
ResourceDictionary.MergedDictionaries>
<
ResourceDictionary Source="ButtonStyles.xaml" />
<
ResourceDictionary Source="CheckBoxStyles.xaml" />
</
ResourceDictionary.MergedDictionaries>
</
ResourceDictionary>
</
UserControl.Resources>
<
Grid x:Name="LayoutRoot">
<
Grid.Background>
<
LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="Black" Offset="0"/>
<
GradientStop Color="White" Offset="1"/>
</
LinearGradientBrush>
</
Grid.Background>
<
StackPanel Orientation="Horizontal">
<
Button Grid.Row="0" Grid.Column="0" Style="{StaticResource RedButton}" Content="Red Button" />
<
Button Grid.Row="0" Grid.Column="1" Style="{StaticResource GreenButton}" Content="Green Button" />
<
CheckBox Grid.Row="1" Grid.Column="0" Style="{StaticResource YellowCheckBox}" Content="Yellow CheckBox" />
<
CheckBox Grid.Row="1" Grid.Column="1" Style="{StaticResource OrangeCheckBox}" Content="Orange CheckBox" />
</
StackPanel>
</
Grid>

FiveMinuteSilverlight3_4

Thursday, August 06, 2009

Five Minute Silverlight 3 Aides-Memoire #3 – Element-to-element binding

    <Grid x:Name="LayoutRoot">
<
Grid.RowDefinitions>
<
RowDefinition Height="*" />
<
RowDefinition Height="40" />
</
Grid.RowDefinitions>
<
Grid.ColumnDefinitions>
<
ColumnDefinition />
<
ColumnDefinition />
</
Grid.ColumnDefinitions>
<
Grid.Background>
<
LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="Black" Offset="0"/>
<
GradientStop Color="White" Offset="1"/>
</
LinearGradientBrush>
</
Grid.Background>
<
Image Grid.Column="0" HorizontalAlignment="Left" Margin="50,50,0,0" Width="200" Height="200" Source="OpenfeatureLizardSquare.jpg">
<
Image.Projection>
<
PlaneProjection x:Name="ImagePlaneProjection"/>
</
Image.Projection>
</
Image>
<
Slider Value="{Binding RotationX, Mode=TwoWay, ElementName=ImagePlaneProjection}" Grid.Row="1" Grid.Column="0" x:Name="XSlider" Minimum="-360" Maximum="360" Margin="50,10" />
<
Slider Value="{Binding RotationY, Mode=TwoWay, ElementName=ImagePlaneProjection}" Grid.Row="1" Grid.Column="1" x:Name="YSlider" Minimum="-360" Maximum="360" Margin="50,10" />
</
Grid>

FiveMinuteSilverlight3_3

Wednesday, August 05, 2009

Five Minute Silverlight 3 Aides-Memoire #2 – Pixel Effects

    <Grid x:Name="LayoutRoot">
<
Grid.ColumnDefinitions>
<
ColumnDefinition />
<
ColumnDefinition />
</
Grid.ColumnDefinitions>
<
Grid.Background>
<
LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="Black" Offset="0"/>
<
GradientStop Color="White" Offset="1"/>
</
LinearGradientBrush>
</
Grid.Background>
<
Image HorizontalAlignment="Left" Grid.Column="0" Margin="50,50,0,0" Width="200" Height="200" Source="OpenfeatureLizardSquare.jpg">
<
Image.Effect>
<
BlurEffect Radius="15"/>
</
Image.Effect>
</
Image>
<
Image HorizontalAlignment="Left" Grid.Column="1" Margin="50,50,0,0" Width="200" Height="200" Source="OpenfeatureLizardSquare.jpg">
<
Image.Effect>
<
DropShadowEffect ShadowDepth="10"/>
</
Image.Effect>
</
Image>
</
Grid>

FiveMinuteSilverlight3_2

Five Minute Silverlight 3 Aides-Memoire #1 – 3D Projection

    <Grid x:Name="LayoutRoot">
<
Grid.Background>
<
LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="Black" Offset="0"/>
<
GradientStop Color="White" Offset="1"/>
</
LinearGradientBrush>
</
Grid.Background>
<
Image HorizontalAlignment="Left" Margin="50,50,0,0" Width="200" Height="200" Source="OpenfeatureLizardSquare.jpg">
<
Image.Projection>
<
PlaneProjection RotationX="-25" RotationY="45"/>
</
Image.Projection>
</
Image>
</
Grid>

FiveMinuteSilverlight3_1

Wednesday, June 10, 2009

3D projections in Silverlight 3 Beta – part 2

In my previous post on 3D projections in Silverlight 3 Beta, I created a basic PhotoCube that had images on the faces of a rotating cube created using the new 2D/3D PlaneProjection capabilities available in Silverlight 3.

BasicCube BasicPhotoCube

I thought I’d do a bit more and add some animation to the cube’s rotation and also do away with the slider and rotate the cube using the mouse. I thought it would be nice if the cube rotated slowly all the time, regardless of any mouse interaction, so the first thing to do was to set up an animation to do that. Because I want to be able to manipulate this animation whilst the various mouse events are firing to allow me to control of the cube’s rotation independent of the ‘background’ rotation, I need to define it at the page level:

private Storyboard slowStoryboard;
readonly Dictionary<Image, double> yRotations = new Dictionary<Image, double>();
private double mouseStart;
private bool mouseIsDown;

public PhotoCubePage()
{
InitializeComponent();

SetUpRotations();
slowStoryboard = CreateSlowRotation();
slowStoryboard.Begin();
}


private Storyboard CreateSlowRotation()
{
Storyboard newStoryboard = new Storyboard { Duration = new Duration(TimeSpan.FromSeconds(15)) };
newStoryboard.RepeatBehavior = RepeatBehavior.Forever;


    foreach (var imageRotation in yRotations)
{
var animation = new DoubleAnimation { Duration = new Duration(TimeSpan.FromSeconds(15)), BeginTime = new TimeSpan(0) };
newStoryboard.Children.Add(animation);
Storyboard.SetTarget(animation, (imageRotation.Key.Projection as PlaneProjection));
Storyboard.SetTargetProperty(animation, new PropertyPath("(PlaneProjection.RotationY)"));
animation.To = imageRotation.Value + 360;
}

return newStoryboard;
}


private void SetUpRotations()
{
foreach (Image image in ImageGrid.Children.Cast<Image>())
{
yRotations.Add(image, ((PlaneProjection)image.Projection).RotationY);
image.MouseLeftButtonDown += ImageMouseLeftButtonDown;
image.MouseLeftButtonUp += ImageMouseLeftButtonUp;
image.MouseMove += ImageMouseMove;
}
}



The animation is performed on each image as it will be for the mouse control, but a 15 second duration makes the cube turn slowly and I want a full rotation of 360 degrees. Setting the RepeatBehavior to Forever ensures that the animation doesn’t stop when it completes a complete rotation. The setup of the Dictionary of Images and their initial RotationY values has been moved to a method to tidy it up, and I now hook up the mouse events here as I need to respond to these on each image surface.



In the mouse down event I record the X-position of the mouse, pause the slow-moving animation and capture the mouse. In the mouse move event, I calculate the mouse movement in the Y-direction and do an rough calculation based on the width of a face of the cube and the amount of mouse movement, to decide on the animation of the faces of the cube which I feed into the cube animation method. In the mouse up event I release mouse capture and restart the slow rotation of the cube.



private void ImageMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
mouseStart = e.GetPosition(this).X;
slowStoryboard.Pause();
mouseIsDown = ((UIElement)sender).CaptureMouse();
}

private void ImageMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
mouseIsDown = false;
ReleaseMouseCapture();
slowStoryboard = CreateSlowRotation();
slowStoryboard.Begin();
}

private void ImageMouseMove(object sender, MouseEventArgs e)
{
if (mouseIsDown)
{
double mouseMovement = mouseStart - e.GetPosition(this).X;
double rotation = mouseMovement / FrontImage.Width * 90;
AnimateRotation(rotation, 1);
}
}

private void AnimateRotation(double movement, double seconds)
{
var duration = new Duration(TimeSpan.FromSeconds(seconds));
var storyboard = new Storyboard { Duration = duration };

foreach (var imageRotation in yRotations)
{
var animation = new DoubleAnimation { Duration = duration, BeginTime = new TimeSpan(10) };
storyboard.Children.Add(animation);
Storyboard.SetTarget(animation, (imageRotation.Key.Projection as PlaneProjection));
Storyboard.SetTargetProperty(animation, new PropertyPath("(PlaneProjection.RotationY)"));
animation.To = ((PlaneProjection)imageRotation.Key.Projection).RotationX + imageRotation.Value + movement;
animation.EasingFunction = new ElasticEase { EasingMode = EasingMode.EaseOut, Oscillations = 3, Springiness = 6 };
}

storyboard.Begin();
}



Just so that all the code is shown, here is the fairly simple XAML for the page:



    <Grid x:Name="LayoutRoot" Background="DarkGray">
<
Grid x:Name="ImageGrid" >
<
Image x:Name="FrontImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/silverlight.jpg">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationY="0"/>
</
Image.Projection>
</
Image>
<
Image x:Name="LeftImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/OpenfeatureLizardSquare.jpg">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationY="90"/>
</
Image.Projection>
</
Image>
<
Image x:Name="BackImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/Waterfall.jpg">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationY="180"/>
</
Image.Projection>
</
Image>
<
Image x:Name="RightImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/Tulip.jpg">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationY="270"/>
</
Image.Projection>
</
Image>
</
Grid>
</
Grid>



 



The result is that the cube turns slowly whilst the mouse is not being used to manipulate it and while the mouse is used to drag it the cube responds with animated movement; I use one of the pre-canned easing functions to give the animation a ‘springy’ feeling at the end of each movement. Notice that the animation method calculates the move-to value for each animation by adding the original position of the image/face to the movement value AND the current value of the RotationY which has been affected by the slow rotation.



And here’s the result:






You may have noticed that although I have called the thing I am rotating a cube and in the first post there were actually six images, I have done away with the top and bottom of my cube in the latest mark-up. This is because they are not really visible whilst the ‘cube’ is rotating on just the Y-axis; I had intended to make it a proper cube and allow rotation along 2 axes (which would have given me full articulation of the cube, or even 3 – to allow me to move the cube in and out of the screen), but there is a problem when it comes to allowing movement in more that one direction.



The problem lies in the fact that once an image plane has been rotated along one axis, rotation in another one happens in relation to the original position, not the new one, so that the faces that become the sides of the cube, the back of the cube, the top and the bottom all need to be transformed in different ways to maintain the illusion of a solid shape in 3D space. For example, after the original Y-axis rotation to setup the cube’s sides and the X-axis rotation to setup the cube’s top and bottom, to rotate the cube along the X-axis, the sides need to be rotated along the Z-axis (i.e. the one that runs through their centre) and this assumes that they aren’t transformed further in the Y-axis themselves, by a slider or my attempts at animating them with mouse movement. In fact the maths for calculating the positions of the faces of the cube are quite complex; from dipping into Charles Petzold’s book: 3D Programming for Windows and quickly getting bogged down with quaternions and rotation matrices, it was clear the maths were beyond the scope of this post, so I chickened out and did away with the top and bottom of the cube and abandoned the notion of full articulation (for now). So we have ended up what might be better called a ‘PhotoPrism’ that rotates only along the Y-axis.



This new name did give me the idea for a further refinement of the cube: if the thing is a prism, there is no reason why it can’t have a variable number of faces, from 3 upwards – the maths for calculating the positioning and angles of rotation for the faces would be much easier.



Hmm, watch this space…

Wednesday, May 20, 2009

3D projections in Silverlight 3 Beta

I had a quick play with the new Plane Projection feature in the Silverlight 3 beta. Like many others, my first experiments were with taking a rectangular image and flipping it around the X, Y & Z axes (these suffixes will be denoted by * for mentions of properties from now on to save typing the variants). It is useful to think of these as long poles running in the respective directions with objects being transformed around these ‘spindles’ using the Rotation* properties. The CenterOfRotation* properties allow a displacement from the centre of the axis so that an element traces a circle around an axis when rotated rather than flips on that axis, so:

        <Image Stretch="Uniform" Source="images/Tulip.jpg" Opacity="1" Height="154" Width="154">
<
Image.Projection>
<
PlaneProjection RotationY="120" />
</
Image.Projection>
</
Image>
<
Image Stretch="Uniform" Source="images/Leaves.jpg" Opacity="0.5" Height="154" Width="154">
<
Image.Projection>
<
PlaneProjection RotationY="240" />
</
Image.Projection>
</
Image>
<
Image Stretch="Uniform" Source="images/Leaves.jpg" Height="154" Width="154">
<
Image.Projection>
<
PlaneProjection RotationY="120" CenterOfRotationZ="100"/>
</
Image.Projection>
</
Image>
<
Image Stretch="Uniform" Source="images/Tulip.jpg" Height="154" Width="154">
<
Image.Projection>
<
PlaneProjection RotationY="240" CenterOfRotationZ="100" />
</
Image.Projection>
</
Image>
<
Path Data="M77,0 L77,154" Fill="#FF000000" StrokeThickness="1" Stroke="#FF000000" />


 



Produces the following:



PlaneProjectionSample1



 



The first 2 images rotate around the Y axis (I have added a Path line to mark its position and altered their opacity slightly to make things clearer), but the second 2 have had their Z positions (in and out of the screen, as it were) brought forward by 100 before being rotated around the Y axis. The grey background is the Grid that holds the images and you can see that the changes to the images’ projections can take them beyond their containing element.



Armed with this small amount of knowledge, I wanted to see if I could make a photocube and rotate it with animations or sliders so that the photos on all sides of the cube could be seen. Here’s the code:



<Grid.RowDefinitions>
<
RowDefinition Height="30" />
<
RowDefinition Height="*" />
</
Grid.RowDefinitions>
<
StackPanel x:Name="ControlsPanel" Grid.Row="0" Orientation="Horizontal">
<
TextBlock Text="Y Rotation : " Margin="2" VerticalAlignment="Center" />
<
Slider x:Name="YSlider" Margin="2" VerticalAlignment="Center" Minimum="0" Maximum="360" Width="150" Value="0" ValueChanged="YSlider_ValueChanged"/>
</
StackPanel>
<
Grid x:Name="ImageGrid" Grid.Row="1">
<
Image x:Name="FrontImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/Front.png">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationY="0"/>
</
Image.Projection>
</
Image>
<
Image x:Name="LeftImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/Left.png">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationY="90"/>
</
Image.Projection>
</
Image>
<
Image x:Name="BackImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/Back.png">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationY="180"/>
</
Image.Projection>
</
Image>
<
Image x:Name="RightImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/Right.png">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationY="270"/>
</
Image.Projection>
</
Image>
<
Image x:Name="TopImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/Top.png">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationX="90" />
</
Image.Projection>
</
Image>
<
Image x:Name="BottomImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/Bottom.png">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationX="270" />
</
Image.Projection>
</
Image>
</
Grid>




public partial class PhotoCubePage : Page
{
readonly Dictionary<Image, double> yRotations = new Dictionary<Image, double>(6);

public PhotoCubePage()
{
InitializeComponent();

foreach (Image image in ImageGrid.Children.Cast<Image>())
{
yRotations.Add(image, ((PlaneProjection)image.Projection).RotationY);
}
}

private void YSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
foreach (var imageRotation in yRotations)
{
((PlaneProjection) imageRotation.Key.Projection).RotationY = imageRotation.Value + ((Slider) sender).Value;
}
}
}


 



Produces the following:



BasicCube



 



I used basic images with text on them so I could verify the positioning and rotation of the cube’s sides, but it looks nicer with some images:



BasicPhotoCube



 



Next time – some mouse handling to rotate the cube, some animation easing for the cube’s movement and why it is much harder to rotate the cube along more than one axis…

Thursday, March 19, 2009

The new features in Silverlight 3 – 3D Projections

Lots of interesting new stuff in the beta of Silverlight 3 – the 2D plane into 3D projection property that has been added to the UIElement class is obviously going to provide lots of opportunity to enhance control templates with mouse over and selected states in VSM. It is pretty easy to knock up a poor-man’s carousel type effect with a few buttons and a template with PlaneProjection transitions:

QuickCarousel 

<ControlTemplate TargetType="Button">
        <Grid x:Name="grid">
            <
Grid.Projection>
                <
PlaneProjection CenterOfRotationY="1" />
            </
Grid.Projection>
            <
vsm:VisualStateManager.VisualStateGroups>
                <
vsm:VisualStateGroup x:Name="CommonStates">
                    <
vsm:VisualStateGroup.Transitions>
                        <
vsm:VisualTransition GeneratedDuration="00:00:00.2000000" To="MouseOver"/>
                        <
vsm:VisualTransition From="MouseOver" GeneratedDuration="00:00:00.5000000"/>
                    </
vsm:VisualStateGroup.Transitions>
                    <
vsm:VisualState x:Name="Normal">
                        <
Storyboard>
                            <
DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="grid" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)">
                                <
EasingDoubleKeyFrame KeyTime="00:00:00" Value="-85"/>
                            </
DoubleAnimationUsingKeyFrames>
                        </
Storyboard>
                    </
vsm:VisualState>
                    <
vsm:VisualState x:Name="MouseOver">
                        <
Storyboard>

etc...
</ControlTemplate>

<
Grid x:Name="LayoutRoot" Background="White" Width="400">
<
Grid.Resources>
</
Grid.Resources>
    <
Grid.RowDefinitions>
        <
RowDefinition />
        <
RowDefinition />
    </
Grid.RowDefinitions>
    <
Grid.ColumnDefinitions>
        <
ColumnDefinition />
        <
ColumnDefinition />
        <
ColumnDefinition />
        <
ColumnDefinition />
        <
ColumnDefinition />
    </
Grid.ColumnDefinitions>
    <
Button x:Name="Button1" Content="Fred" Grid.Column="0" Style="{StaticResource FlipButton}" />
    <
Button x:Name="Button2" Content="Barney" Grid.Column="1" Style="{StaticResource FlipButton}" />
    <
Button x:Name="Button3" Content="Wilma" Grid.Column="2" Style="{StaticResource FlipButton}" />
    <
Button x:Name="Button4" Content="Betty" Grid.Column="3" Style="{StaticResource FlipButton}" />
    <
Button x:Name="Button5" Content="Pebbles" Grid.Column="4" Style="{StaticResource FlipButton}" />
</
Grid>

The key thing is to target the UIElement’s Projection.ProjectionPlane property and to animate its RotationX, RotationY and RotationZ properties to transform the 2D plane of the element in the X, Y & Z dimensions. Setting the CentreOfRotationX, CentreOfRotationY & CentreOfRotationZ properties determines when the movement ‘hinges’ and the rotations are in degrees from the normal, untransformed state (i.e. 0 degrees).

Five minutes obviously doesn’t make for a control – but interesting to play with the effect and I expect to see it crop up in many a control and Silverlight app in the future.

Monday, January 12, 2009

Great Minds…

Having joined the millions of others who have downloaded and installed the Windows 7 beta recently, I noticed that one of the new features is the ability to have the desktop wallpaper change automatically at selectable intervals. This is functionality that I implemented in a little app that I wrote a few years ago as a bit of a C#.NET learning exercise and put up on my website. I called it the Openfeature WallpaperChanger and it is available here. WallpaperChanger

It allows you to pick a set of images to use as desktop wallpapers and set the interval after which the app will select the next one and make it the Windows wallpaper; it also allows you to randomise the order and set the tile/stretch options for filling the screen. I had always intended to rework it as a Vista gadget, but I guess there won’t be a need to if Microsoft are going to make the functionality a feature of the OS.

This is not the first time this has happened either – I had a hunt around but couldn’t find a copy of an app I wrote some time in the early 1990s which consisted of a 2-3 pixel wide UI bar that docked to one side of the Windows desktop and slide out to a larger surface when the mouse was moved over it to reveal a notepad icon, a printer icon and a wastepaper basket icon. Dragging files from the File Manager to the bar and dropping them on one of the icons would allow viewing, printing or deleting them. This was Windows 3/3.11 Workgroups and predated Windows 95 and the auto-hide taskbar by a year or two.

Now I’m not for a moment suggesting that Microsoft spies were snooping on my hobby developer activities on each of these occasions - the phrase “don’t flatter yourself, dear” comes to mind - but I do think certain ideas have their time and emerge spontaneously, meme-like, in more than one place at the same time.

And I guess anyway that there will be plenty of people who will claim that these sorts of features were already in the Apple Mac OS for others to steal long before they appeared in Windows and there will be others still who don’t reckon much to UX features like auto-hide, or cycling wallpapers anyway so maybe “fools seldom differ”.

Monday, November 24, 2008

Creating a Chord Factory in Silverlight 2

Years ago I created a Windows Forms application that displayed piano chords and musical scales graphically as a sort of pet project/learning exercise. It was written in VB6.0 (I said it was years ago) and contained an image of a piano keyboard and a musical stave. Selecting a root note, either by clicking a key or using a ComboBox, and picking a chord and/or scale, it would show the notes in the chord and/or scale on the keyboard and on the stave. It is still available for download here and will install and run under Vista - although as you can see from the image it fails to install the musical font used to display the clef symbols.

ChordFactory

The Openfeature Chord Factory main screen. That should be a G rather than an O.

As well as showing chords it also harnessed the power of the Windows Multi Media API to play them using the MIDI synthesizer of the PC sound card or to an attached MIDI device. Over time I added further enhancements such as chord-inversions, chord progressions and a help file. I was and still am quite proud of it.

I recently decided to revisit Chord Factory after all these years and have a look at re-implementing it in Silverlight 2 - using XAML to achieve better graphical presentation of the piano keyboard and musical stave and data binding to better store and represent the musical data for the chord shapes and scales. I suspected that the lack of access to the Windows API would make it necessary to find another way of playing the chords, but thought the the Silverlight MediaElement might offer a way around this limitation.

It's not quite finished yet, but I thought it might make the subject of a few posts on how I did it and I'm starting with this one. I have done quite a bit on it so far and the work-in-progress is already up on the web at my Openfeature web site here.

More to come...