{"id":16101,"date":"2010-12-26T00:01:00","date_gmt":"2010-12-26T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2010\/12\/26\/weekend-scripter-create-a-holiday-greeting-using-powershell-and-wpf\/"},"modified":"2010-12-26T00:01:00","modified_gmt":"2010-12-26T00:01:00","slug":"weekend-scripter-create-a-holiday-greeting-using-powershell-and-wpf","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/weekend-scripter-create-a-holiday-greeting-using-powershell-and-wpf\/","title":{"rendered":"Weekend Scripter: Create a Holiday Greeting Using PowerShell and WPF"},"content":{"rendered":"<p><span style=\"font-size:10.0pt\">&nbsp;<\/span><span style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><b><span style=\"font-size:10.0pt\">Summary:<\/span><\/b><span style=\"font-size:10.0pt\"> Microsoft PFE Chris Bellee from Australia shows how to use Windows PowerShell and WPF to create a holiday greeting.<\/span><\/p>\n<p><span style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><span style=\"font-size:10.0pt\">Microsoft Scripting Guy Ed Wilson here. It is almost the end of the year and we have decided to devote some posts to the holiday season. We even have guest bloggers from around the world to share some holiday spirit. Today is our last day and we will finish with a bang. Welcome Chris Bell&eacute;e from Australia. <\/span><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5226.WES-12-26-10-01.jpg\" border=\"0\" \/><span style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><span style=\"font-size:10.0pt\">Chris Bellee is a Premier Field Engineer for Microsoft. He is based in Sydney, Australia. He teaches a very popular Windows PowerShell class to Microsoft Premier Customers.<\/span><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8117.WES-12-26-10-02.jpg\" border=\"0\" \/><span style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><span style=\"font-size:10.0pt\">In this holiday season blog post, I am going to cover a subject most Windows PowerShell console-based administrators will rarely have a reason to explore. Windows Presentation Foundation (more commonly known as WPF) is the latest graphical sub system for rendering GUI&rsquo;s in Microsoft Windows and gives a wealth of features for designing advanced graphical user interfaces.<\/span><\/p>\n<p><span style=\"font-size:10.0pt\">The Microsoft MSDN site describes WPF as:<\/span><\/p>\n<p><span style=\"font-size:10.0pt\">&ldquo;&hellip;a next-generation presentation system for building Windows client applications with visually stunning user experiences. With WPF, you can create many different stand-alone and browser-hosted applications.&rdquo;<\/span><\/p>\n<p><span style=\"font-size:10.0pt\">Because this is a Windows PowerShell article, I will be looking at how to use the WPF object model from Windows PowerShell script and manipulate its objects to create an animated GUI. WPF is a very large topic so I will only cover some core WPF concepts.<\/span><\/p>\n<ul>\n<li>Adding layout controls to a WPF window <\/li>\n<li>Wiring event handlers to objects <\/li>\n<li>Creating a basic keyframe animation <\/li>\n<li>Working with image resources <\/li>\n<li>Positioning &amp; styling controls <\/li>\n<li>Updating a control&rsquo;s contents by using a timer object <\/li>\n<\/ul>\n<p><span style=\"font-size:10.0pt\">The example script creates a form without the ubiquitous window form &lsquo;look and feel&rsquo;, because WPF enables us to create borderless and non-square forms with ease. Anyone already familiar with WPF will know that a WPF layout can be created completely using the XAML mark-up language, a relative of XML. In fact, most WPF examples on the web use XAML exclusively, but in this post I will focus on leveraging the .NET object model directly through Windows PowerShell script to create the layout, controls, content and styling.<\/span><\/p>\n<p><span style=\"font-size:10.0pt\">The first problem that you hit when you work with WPF from Windows PowerShell is something called &lsquo;Threaded Apartments&rsquo;. Which, to my surprise, are not a new form of suspended living accommodation, but how COM components can be accessed. There are two apartment types, single-threaded (STA) &amp; multithreaded (MTA), from here it all becomes a bit technical but all we have to know is that the Windows PowerShell console runs in MTA mode &amp; WPF controls can only run in STA mode. This presented a problem in Windows PowerShell V1, but in V2 we have some options to successfully host WPF objects.<\/span><\/p>\n<ul>\n<li>Use the Windows PowerShell V2 ISE (Integrated Scripting Environment or &lsquo;Graphical Windows PowerShell&rsquo;) to run the script. The ISE is written using WPF objects &amp; therefore runs in the correct apartment model by default.<\/li>\n<li>Start a new PowerShell.exe console specifying the <b>&ndash;STA<\/b> switch.<\/li>\n<li>Create a new PowerShell STA background runspace &amp; execute the script there<br \/><span style=\"font-size:10.0pt\">One can find lots of articles detailing how to do this, but I find the easiest way is to just use the Windows PowerShell ISE, as I almost exclusively use it for script development anyway.<\/span><\/li>\n<\/ul>\n<p><span style=\"font-size:10.0pt\"><\/span><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-size:10.0pt\">Ok, let&rsquo;s get WPF-ing.<\/span><\/p>\n<p><span style=\"font-size:10.0pt\">The first thing we have to do is create a WPF window object. This will be the parent of all the controls we will add later. There are so many members implemented on every WPF object I&rsquo;ll only demonstrate a few. Here is the code to create a new window object that contains a canvas object surrounded by a web 2.0 style rounded corner border control.<span>&nbsp; <\/span>The window doesn&rsquo;t have the typical modal form controls, is transparent and cannot be resized or moved. The following figure shows the result of the following code.<\/span><\/p>\n<blockquote>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># create a WPF window object &amp; set some of its properties<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objWindow = New-Object system.windows.window<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objWindow.Name = &#8220;Window1&#8221;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objWindow.AllowsTransparency = $true<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objWindow.background = &#8220;Transparent&#8221; <\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objWindow.WindowStyle = &#8220;None&#8221;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objWindow.sizeToContent = &#8220;WidthAndHeight&#8221;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objWindow.WindowStartupLocation = &#8220;CenterScreen&#8221;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objWindow.ResizeMode = &#8220;NoResize&#8221;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># create a border control<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objBorder = New-Object system.windows.controls.border<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objBorder.BorderBrush = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Colors]::White)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objBorder.borderThickness = 10<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objBorder.width = 710<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objBorder.height = 480<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objBorder.cornerRadius = 7<span>&nbsp;&nbsp; <\/span><\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># create a canvas control<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objCanvas = new-object system.windows.controls.canvas<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objCanvas.background = New-Object ` System.Windows.Media.SolidColorBrush([System.Windows.Media.Colors]::Black)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># add the canvas as a child of the border control<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objBorder.child = $objCanvas<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># add the border control to the WPF window object&rsquo;s content property <\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objWindow.content = $objBorder<\/span><\/span><\/p>\n<\/blockquote>\n<p><span style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3731.WES-12-26-10-03.jpg\" border=\"0\" \/><span style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><span style=\"font-size:10.0pt\"><\/span><\/p>\n<p><span style=\"font-size:10.0pt\">To allow the form to be moved or even closed, we have to add a couple of event handlers, because setting the <b>WindowStyle<\/b> property set to <i>None<\/i> removed the familiar minimize, maximize &amp; title bar form controls. <\/span><\/p>\n<blockquote>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># add an event handler to allow the window to be dragged using the left mouse button<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$eventHandler_LeftButtonDown = [Windows.Input.MouseButtonEventHandler]{$this.DragMove()}<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objWindow.Add_MouseLeftButtonDown($eventHandler_LeftButtonDown)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># create event handler to close the window when a right-click is detected in the window<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$eventHandler_RightButtonDown = [Windows.Input.MouseButtonEventHandler]{$this.close()}<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objWindow.Add_MouseRightButtonDown($eventHandler_RightButtonDown)<\/span><\/span><\/p>\n<\/blockquote>\n<p><span style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><span style=\"font-size:10.0pt\">The <b>[Windows.Input.MouseButtonEventHandler]<\/b> class is used to respond to mouse events. The script block following the class name uses <b>$this.DragMove()<\/b> to refer to the object&rsquo;s behavior when &lsquo;wired-up&rsquo; using the window object&rsquo;s <b>MouseLeftButtonDown<\/b> event. In this case is to allow the window to be dragged when left-clicked on.<\/span><\/p>\n<p><span style=\"font-size:10.0pt\">Note that &lsquo;<b>Add_<\/b>&rsquo; is prefixed to the event name, and the event handler object that was created on the previous line is passed as the method argument.<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">After the form has been defined, it is time to add some actual content. The aim of this example is to cross-fade a series of photographs in an endless loop. To do this we have to add the images to the canvas control &amp; animate them using a basic &lsquo;fade-in \/ fade-out&rsquo; keyframe sequence. Each image is then offset from the previous image in the timeline by a few seconds, to complete the effect. <\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">I created a couple of functions to handle importing the images &amp; creating the 5 keyframe animations as they are the same in every respect other than the image they animate. WPF uses a storyboard object to contain multiple animations and start them in parallel.<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">There is definitely a lot going on in the <b>CreateAnimation<\/b> function but it basically consists of the following steps:<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<ul>\n<li><span lang=\"EN-AU\" style=\"font-size:10.0pt\">Load a bitmap file into a new Image control object by using the <b>loadBitMap<\/b> helper function<\/span><\/li>\n<li><span lang=\"EN-AU\" style=\"font-size:10.0pt\">Register the image control name with the canvas object so that we can reference it later on<\/span><\/li>\n<li><span lang=\"EN-AU\" style=\"font-size:10.0pt\">Add the image control to the canvas object&rsquo;s collection of child objects<\/span><\/li>\n<li><span lang=\"EN-AU\" style=\"font-size:10.0pt\">Create a <b>doubleAnimationUsingKeyframes<\/b> object which allows us to control the image object&rsquo;s properties over time using keyframes. The <b>repeatbehavior<\/b> property is set to <b>Forever<\/b> to create an endless loop.<\/span><\/li>\n<li><span lang=\"EN-AU\" style=\"font-size:10.0pt\">Create 3 keyframes, specifying the target property&rsquo;s start value and length.<\/span><\/li>\n<li><span lang=\"EN-AU\" style=\"font-size:10.0pt\">Add the keyframes to the animation<\/span><\/li>\n<li><span lang=\"EN-AU\" style=\"font-size:10.0pt\">Set the storyboard&rsquo;s target name &amp; property to animate, in this case we are animating the image&rsquo;s <b>Opacity<\/b> property.<\/span><\/li>\n<li><span lang=\"EN-AU\" style=\"font-size:10.0pt\">Add the completed animation object to the storyboard object.<\/span><\/li>\n<\/ul>\n<blockquote>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">Function CreateAnimation {<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">param (<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$AnimationName,<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$ImagePath,<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$AnimationDuration,<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$BeginTime,<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$Storyboard,<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$intIn,<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$intHold,<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$intOut<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># create a temporary image object<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$tmpImage = loadBitMap $ImagePath $AnimationName<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># register the temporary image so we can find it later on<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objCanvas.RegisterName($tmpImage.Name, $tmpImage)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">[void]$objCanvas.Children.Add($tmpImage)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># create a double keyframe animation object and set its duration, start time and looping behaviour<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objDoubleKeyFrameAnimation = new-object `<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">system.windows.media.animation.doubleanimationusingkeyframes<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objDoubleKeyFrameAnimation.duration = $(new-timespan -Seconds $AnimationDuration)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objDoubleKeyFrameAnimation.repeatbehavior = &#8220;Forever&#8221;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objDoubleKeyFrameAnimation.Name = $AnimationName<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objDoubleKeyFrameAnimation.BeginTime = $BeginTime<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># create 3 keyframes to control the image opacity over time<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objLinearKeyFrameIn = New-Object system.windows.media.animation.LinearDoubleKeyFrame(1, $(new-timespan -Seconds $intIn))<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objLinearKeyFrameHold = New-Object system.windows.media.animation.LinearDoubleKeyFrame(1, $(new-timespan -Seconds $intHold))<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objLinearKeyFrameOut = New-Object system.windows.media.animation.LinearDoubleKeyFrame(0, $(new-timespan -Seconds $intOut))<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># add the keyframes to the animation<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">[void]$objDoubleKeyFrameAnimation.keyframes.add($objLinearKeyFrameIn)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">[void]$objDoubleKeyFrameAnimation.keyframes.add($objLinearKeyFrameHold)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">[void]$objDoubleKeyFrameAnimation.keyframes.add($objLinearKeyFrameOut)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># set the animation target object<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">[System.Windows.Media.Animation.Storyboard]::SetTargetName($objDoubleKeyFrameAnimation,$tmpImage.Name)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># set the property to animate &#8211; in this case the opacity property<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">[System.Windows.Media.Animation.Storyboard]::SetTargetProperty($objDoubleKeyFrameAnimation, `<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$(New-Object System.Windows.PropertyPath([System.Windows.Controls.Image]::OpacityProperty)))<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># add the animation to the storyboard<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$Storyboard.Children.Add($objDoubleKeyFrameAnimation)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">}<\/span><\/span><\/p>\n<\/blockquote>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\"><\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">The next step is to actually use the <b>CreateAnimation<\/b> function to, well, create some animations! This is achieved by using the <b>Get-ChildIten<\/b> cmdlet to return the .jpg image paths which are then piped to a <b>ForEach-Object<\/b> cmdlet. Each animation is identical other than the source image and timeline offset values supplied.<\/span><\/p>\n<blockquote>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># create the animations &amp; add them to the storyboard<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># set a counter variable to control where each successive animation begins in the timeline<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$i = 0<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># return the .jpg image files<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">(Get-ChildItem -Path &#8220;.\\*&#8221; -Filter &#8220;*.jpg&#8221;) | <\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># loop through each image, calling the CreateAnimation function<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">ForEach-Object {<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">CreateAnimation -storyboard $objStoryboard -AnimationName $_.baseName -imagePath $_.fullName -animationDuration 16 -beginTime &#8220;0:0:$i&#8221; -intIn 2 -intHold 3 -intOut 6<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># increment each animation start point by 3 seconds<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$i += 3<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">}<\/span><\/span><\/p>\n<\/blockquote>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">Next, we have to create a grid control to hold the weather data returned from the <b>Get-WeatherWebService<\/b> function. As the name suggests, this function calls a web service by using the <b>New-WebServiceProxy<\/b> cmdlet to retrieve current weather information. The function parses the returned XML into a custom <b>PSObject<\/b> which is used to add the weather data to label controls on the WPF form. <\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">A WPF grid control is similar to an HTML table. It enables the structured layout of child elements in a row\/column manner. The following code creates a new grid object and adds 2 rows and 2 columns.<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<blockquote>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># create a grid control<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$grd = New-Object system.windows.controls.grid<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$grd.Width = 140<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># create grid rows &amp; columns<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$row1 = new-object system.windows.controls.rowdefinition<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$row1.height = &#8220;Auto&#8221;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$row2 = new-object system.windows.controls.rowdefinition<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$row2.height = &#8220;Auto&#8221;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$col1 = new-object system.windows.controls.columndefinition<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$row2.height = &#8220;Auto&#8221;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$col2 = new-object system.windows.controls.columndefinition<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$row2.height = &#8220;Auto&#8221;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># add rows &amp; columns to the grid<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$grd.RowDefinitions.add($row1)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$grd.RowDefinitions.add($row2)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$grd.ColumnDefinitions.add($col1)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$grd.ColumnDefinitions.add($col2)<\/span><\/span><\/p>\n<\/blockquote>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">We can now add child elements to a particular row or column in the grid. Here, a label control is created to hold the current temperature and an image control holds an icon representing the current sky conditions. A grid&rsquo;s child items can also be configured to span several rows or columns by calling a child item&rsquo;s <b>SetValue()<\/b> method. In the extract below, the <b>$lblWeather<\/b> label control is set to span 2 columns.<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<blockquote>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># get the latest weather data for Sydney from the web service<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$conditions = Get-WeatherWebService -strCountry &#8220;Australia&#8221; -strCity &#8220;Sydney&#8221;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># create a label control to hold the weather data<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$lblWeather = New-Object System.Windows.Controls.Label<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># fill the label content with the current temperature<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$lblWeather.content = $Conditions.temp.tolower()<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$lblWeather.FontSize = 34<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$lblWeather.FontWeight = &#8220;Bold&#8221;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$lblWeather.opacity = 1<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$lblWeather.Foreground = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Colors]::White)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># set the weather data to span 2 columns of the grid control<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$lblWeather.SetValue([system.windows.controls.grid]::ColumnSpanProperty,2)<\/span><\/span><\/p>\n<\/blockquote>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">Child controls can also be positioned relative to their parents. The <b>[system.windows.controls.canvas]<\/b> class&rsquo;s <b>SetRight()<\/b>, <b>SetTop()<\/b>, <b>SetBottom()<\/b> &amp; <b>SetLeft()<\/b> static methods are used to position the label relative to its parent, the canvas object.<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<blockquote>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># position the weather label control<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">[system.windows.controls.canvas]::SetRight($lblWeather, 20)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">[system.windows.controls.canvas]::SetTop($lblWeather, 20)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">[system.windows.controls.grid]::SetColumn($lblWeather,0)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># add the weather label to the grid control<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$grd.Children.add($lblWeather)<\/span><\/span><\/p>\n<\/blockquote>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">WPF objects can also have many styling options configured. Here we create a new color gradient object and add it to the label control&rsquo;s &lsquo;background&rsquo; property, creating an orange to yellow gradient fade effect.<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<blockquote>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"><\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"><\/span><\/span><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<\/blockquote>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">The final interesting piece of code is how we can update the content of a WPF object continually. The script uses this ability to display the number of days, hours, minutes &amp; seconds until the New Year. To do this, we create a <b>$countDownUpdate scriptblock<\/b> that contains a <b>New-Timespan<\/b> cmdlet. This calculates the difference between now and midnight on 31<sup>st<\/sup> December 2010, then sets this value on the content property of a label control.<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<blockquote>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># scriptblock that gets called every second by the timer object<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># updates the &#8216;content&#8217; property of the countdownlabel<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$countDownUpdate = {<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$ts = New-TimeSpan -Start $([datetime]::now) -End $([datetime]&#8221;12\/31\/2010 00:00:00&#8243;)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># find the countdown label control<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$lbl = $objCanvas.FindName(&#8220;countDown&#8221;)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$lbl.content = &#8220;$($ts.Days) . $($ts.Hours) : $($ts.Minutes) : $($ts.Seconds)&#8221;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">}<\/span><\/span><\/p>\n<\/blockquote>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">To update the countdown every second, we add code to the canvas control&rsquo;s <b>Loaded<\/b> event to add a new <b>DispatcherTimer<\/b> object with a Tick interval of 1 second and set each Tick event to call the <b>$countDownUpdate scriptblock<\/b>. Note the use of the call <b>(&amp;)<\/b> operator to execute the script block using Windows PowerShell&rsquo;s command mode.<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<blockquote>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># add event handler to the canvas<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># when the canvas&#8217;s &#8216;loaded&#8217; event is fired, start the timer<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objCanvas.Add_Loaded({<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$timer = new-object System.Windows.Threading.DispatcherTimer<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$timer.Interval = [TimeSpan]&#8221;0:0:1&#8243;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># on each tick of the timer, execute the scriptblock using the call operator (&amp;)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$timer.Add_Tick({&amp;$CountDownUpdate})<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># start the timer<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$timer.Start()<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">})<\/span><\/span><\/p>\n<\/blockquote>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">Finally, there are a couple of important lines of code needed to complete the script. The first starts the animation objects that are contained within the storyboard object. The second displays the WPF window.<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<blockquote>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># start the animation storyboard<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objStoryboard.begin($objCanvas)<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">&nbsp;<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\"># show the WPF window<\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN-AU\" style=\"font-size: 10pt\"><span style=\"font-family: courier new,courier\">$objWindow.ShowDialog()<\/span><\/span><\/p>\n<\/blockquote>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">I hope this post has demonstrated a little of the huge amount of power the Windows Presentation Foundation system provides to Windows forms and how Windows PowerShell can leverage it. All that remains is to wish you all a Happy New Year from &lsquo;Down Under&rsquo;!<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><span lang=\"EN-AU\" style=\"font-size:10.0pt\">Thank you, Chris, for sharing with us.&nbsp;I have included all the files that Chris used to produce his&nbsp;WPF post card on the <a href=\"http:\/\/cid-0edd289b085ed6f1.skydrive.live.com\/redir.aspx?page=browse&amp;resid=EDD289B085ED6F1!106&amp;type=6&amp;Bsrc=EMSHOO&amp;Bpub=SN.Notifications\">Scripting Guys Sky Drive<\/a>. &nbsp;This concludes holiday guest blogger week. Join me tomorrow as I start the final week of 2010. <\/span><\/p>\n<p><span style=\"font-size:10.0pt\">I invite you to follow me on <\/span><a target=\"_blank\" href=\"http:\/\/bit.ly\/scriptingguystwitter\"><span style=\"font-size:10.0pt\">Twitter<\/span><\/a><span style=\"font-size:10.0pt\"> or <\/span><a href=\"http:\/\/bit.ly\/scriptingguysfacebook\"><span style=\"font-size:10.0pt\">Facebook<\/span><\/a><span style=\"font-size:10.0pt\">. If you have any questions, send email to me at <\/span><a target=\"_blank\" href=\"mailto:scripter@microsoft.com\"><span style=\"font-size:10.0pt\">scripter@microsoft.com<\/span><\/a><span style=\"font-size:10.0pt\"> or post them on the <\/span><a target=\"_blank\" href=\"http:\/\/bit.ly\/scriptingforum\"><span style=\"font-size:10.0pt\">Official Scripting Guys Forum<\/span><\/a><span style=\"font-size:10.0pt\">. See you tomorrow. Until then, peace.<\/span><\/p>\n<p><span style=\"font-size:10.0pt\">&nbsp;<\/span><\/p>\n<p><b><span style=\"font-size:10.0pt\">Ed Wilson, Microsoft Scripting Guy<\/span><\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>&nbsp;&nbsp; Summary: Microsoft PFE Chris Bellee from Australia shows how to use Windows PowerShell and WPF to create a holiday greeting. &nbsp; Microsoft Scripting Guy Ed Wilson here. It is almost the end of the year and we have decided to devote some posts to the holiday season. We even have guest bloggers from around [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[215,71,56,3,4,61,100],"class_list":["post-16101","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-chris-bellee","tag-graphical","tag-guest-blogger","tag-scripting-guy","tag-scripting-techniques","tag-weekend-scripter","tag-windows-powershell-ise"],"acf":[],"blog_post_summary":"<p>&nbsp;&nbsp; Summary: Microsoft PFE Chris Bellee from Australia shows how to use Windows PowerShell and WPF to create a holiday greeting. &nbsp; Microsoft Scripting Guy Ed Wilson here. It is almost the end of the year and we have decided to devote some posts to the holiday season. We even have guest bloggers from around [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/16101","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=16101"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/16101\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=16101"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=16101"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=16101"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}