June 2007 - Posts

The key to scaling your WPF elements in a specific direction other than down and to the right are the CenterX and CenterY properties of the ScaleTransform.  It's easy enough to set these manually, but that's just lazy and asking for long term trouble.  Instead, decide how you want it to animate and bind it to something.  For instance, I have a touch screen keyboard I'm working on that I'd like to animate Up and to the Right when you are holding down the key.  Here's how I did it:

<Page xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  <Button Margin="5,5,5,5" Width="50" Height="50" x:Name="btnKey">
    My Button
    <Button.RenderTransform>
      <ScaleTransform x:Name="buttonscale" ScaleX="1" ScaleY="1" CenterX="0" CenterY="{Binding ElementName=btnKey, Path=ActualHeight}" />
    </Button.RenderTransform>
    <Button.Triggers>
      <EventTrigger RoutedEvent="Button.Click">
        <BeginStoryboard>
          <Storyboard>
            <DoubleAnimation Storyboard.TargetName="buttonscale" Storyboard.TargetProperty="(ScaleTransform.ScaleX)" To="1.5" Duration="0:0:0.25" AutoReverse="True"/>
            <DoubleAnimation Storyboard.TargetName="buttonscale" Storyboard.TargetProperty="(ScaleTransform.ScaleY)" To="1.5" Duration="0:0:0.25" AutoReverse="True"/>
          </Storyboard>
        </BeginStoryboard>
      </EventTrigger>
    </Button.Triggers>
  </Button>
</Page>

The key is that I bound the CenterX proeprty to the button's ActualHeight property.  If you wanted to scale from some midpoint though, you'll have to specify a Converter in your bind syntax.  This Converter technique is often cited in the ubiquitous "Reflection trick".

with 2 comment(s)
Filed under: ,

NOTE: Impatience just leads to frustration.

When I first starting playing with PowerShell here at the office, I was all excited to start adding little cmdlets to my personal profile and add to my stellar productivity (tongue firmly planted in check).  This would hopefully reduce the net loss incurred by my learning PowerShell in the first place. 

So I took the first step and created my empty profile:

ps> notepad $profile

and added a nice little cmdlet to send email.  This will replace that exe tool I've always used to send emails in batch files or just whenever I need to at the command prompt.

$defaultSendMailHost = "emcrs71"
$defaultSendMailFrom = email@email.com

function send-mail( [string] $to, [string] $subject, [string] $body )
{
     $smtp = new-object System.Net.Mail.SmtpClient
     $smtp.Host = $defaultSendMailHost
     $email = new-object System.Net.Mail.MailMessage
     $email.From = $defaultSendMailFrom
     $email.To.Add( $to )
     $email.Subject = $subject
     $email.Body = $body
     $smtp.Send( $email )
}

Start it up again and WHAM... 

File \\<server>\<username>\My Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 cannot be loaded. The file \\<server>\<username>\My Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 is not digitally signed. The script will not execute on the system. Please see "get-help about_signing" for more details.

Sign my scripts?!  How dare you!  I was too annoyed to deal with it and took the ever so appalling step of changing the PowerShell Short cut to point to a local folder on my C drive.  Please don't stop reading as I've learned from my mistakes and have rectified my ways.

A few weeks later I ran across the PowerShell Community Extensions, which I highly recommend PowerShell-er install and, in fact, replaced my send-mail cmdlet above.  PSCX has an option to install it's own profile which I chose, but after that I started receiving the Digital Signing guff. 

In the end, I did what any good PowerShell-er with a growing Profile should do.  I created a cmdlet to sign my profile.  Now I can open PS, type edit-profile (or use my ep alias), save it, and hit sign-profile (sp alias) and I'm good to go.  Here it is:

function edit-profile()
{
    notepad $profile
}

function sign-profile()
{
    dir $Profiledir\*.ps1 | foreach-object { sign-script $_.FullName }
}

function sign-script( $scriptsource )
{
    get-item $scriptsource
    Set-AuthenticodeSignature $scriptsource @(Get-ChildItem cert:\CurrentUser\My -codesigning)[0]
}

set-alias sm Send-SmtpMail
set-alias ep edit-profile
set-alias sp sign-profile

Now there is a little part I left out and that's creating yourself a certificate, but Scott Hanselman has a very good post that will walk you through this.

Technorati Profile
with no comments
Filed under:

One of my play projects with WPF is a photo viewer for the pictures we put out on http://cromwellhaus.com.  One of the views is a montage of the latest photos with a random RotateTransform Angle applied to each image's RenderTransform.  Getting the view itself set up as cake, but when I attempted to apply the random angle to each image, it wasn't so random.

Here's what happened and how to actually accomplish such a task:

I started out with an ItemsControl, rather than a ListView as most examples show, using a UniformGrid as the ItemsPanel.

<ItemsControl ItemTemplate="{StaticResource photoItem}" ItemsSource="{Binding Source={StaticResource dpPhotos}, XPath=/rss/channel/item}">
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <UniformGrid HorizontalAlignment="Stretch"  VerticalAlignment="Stretch" />
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
</ItemsControl>

I bound the ItemsSource to an XmlDataProvider pointing to the photos RSS feed for Ryan Jr. pictures

<XmlDataProvider x:Key="dpPhotos" Source="http://cromwellhaus.com/photos/baby/pictures_rss.aspx?Tags=Ryan+Jr&amp;AndTags=1" XmlNamespaceManager="{StaticResource rssMapping}"/>

I run our site using Community Server 2007 Express Edition which provides RSS meta extensions for more detailed feeds.  This required that I apply an XmlNamespaceManager to define these extension namespaces.  That's what this is for:

<XmlNamespaceMappingCollection x:Key="rssMapping">
  <XmlNamespaceMapping Uri="http://search.yahoo.com/mrss" Prefix="media" />
</XmlNamespaceMappingCollection>

Here is the DataTemplate used by the ItemsControl to display/render each image.  You'll see this referred to in above as {StaticResource photoItem}:

<DataTemplate x:Key="photoItem">
  <Image Margin="12,12,12,12" Source="{Binding Mode=Default, XPath=media:thumbnail/@url}" ToolTip="{Binding XPath=title}" Width="{Binding Mode=Default, XPath=media:thumbnail/@width}" Height="{Binding Mode=Default, XPath=media:thumbnail/@height}" />
</DataTemplate>

You can see the use of the XmlNamespaceMapping in the XPath=media:thumbnail/@url.  That had me stumped for a few seconds, but opening the project up in Expression Blend and re-adding the XmlDataProvider created the Mappings for me.  (Sidebar: VS 2008 does do this for you - thank goodness)

At this point, we are displaying thumbnails and we are certainly aware that I make poor color choices, but we're on our way.

Almost there...

Applying the RotateTransform is easy...

<Image Margin="12,12,12,12" Source="{Binding Mode=Default, XPath=media:thumbnail/@url}" ToolTip="{Binding XPath=title}" Width="{Binding Mode=Default, XPath=media:thumbnail/@width}" Height="{Binding Mode=Default, XPath=media:thumbnail/@height}">
  <Image.RenderTransform>
    <RotateTransform Angle="10" />
  </Image.RenderTransform>
</Image>

...and it's almost as easy to use the System.Random class to generate the angle.  Just add the following ObjectDataProvider as a Window or Application Resource and bind the Angle property to it:

<ObjectDataProvider x:Key="randomAngle" ObjectType="{x:Type system:Random}" MethodName="Next">
  <ObjectDataProvider.MethodParameters>
    <system:Int32>-12</system:Int32>
    <system:Int32>12</system:Int32>
  </ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
...
<RotateTransform Angle="{Binding Source={StaticResource randomAngle}" />

Well when you do this, you'll find that you get the same Angle for every image.  This is because you are actually binding to a single instance of the ObjectDataProvider.  To resolve this we actually have to embed the ODP in the transform itself as so:

<RotateTransform>
  <RotateTransform.Angle>
    <Binding>
      <Binding.Source>
        <ObjectDataProvider ObjectType="{x:Type system:Random}" MethodName="Next">
          <ObjectDataProvider.MethodParameters>
            <system:Int32>-12</system:Int32>
            <system:Int32>12</system:Int32>
          </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
      </Binding.Source>
    </Binding>
  </RotateTransform.Angle>
</RotateTransform>

Tada!image

Now you will find that your angle's aren't terribly "Random".  This is because you are actually asking for a new instance of the Random class each time.  You can tell the ODP within the DataTemplate to use the same instance each time by adding this to the Window Resources:

<ObjectDataProvider x:Key="randomAngle" ObjectType="{x:Type system:Random}"/>

and modifying the Angle binding to the following, telling the transform ODP to use a specific resource instance rather than just giving it a type to instantiate each time.

<ObjectDataProvider ObjectInstance="{StaticResource randomAngle}" MethodName="Next">
  <ObjectDataProvider.MethodParameters>
    <system:Int32>-12</system:Int32>
    <system:Int32>12</system:Int32>
  </ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
You can download a mini-version used to write this post here.
with 2 comment(s)
Filed under: ,