Updating Controls From Timers Example

Jun 30, 2011 at 8:56 PM

Again, feel free to use this any way you want as an example in future drops. However, the first button only updates the bound data, and the control doesn't change. Based on some serious time spent researching, I need one of two things; (1) [the WPF way] the custom object (or maybe just it's properties) needs to implement IObservable, or (2) [the older Form's way] anytime the custom object's BoundLabelContent Property is changed, the code needs to update the binding expression for the label lbl_Bound.  (2) seems like a lot of code, and (1) is just a bit above my current skill level in Powershell.

What is the best way to get the Bound label to update when the timer updates the BoundLabelContent Property? If the answer is (as I think) to implement IObservable, can you point me to any examples?

 

# Define a test function that creates a custom object with Properties.
# WPF Controls need Properties to bind to
function New-TestObj {
  Param (
    $BoundLabelContent
  )
  new-object psObject -Property @{BoundLabelContent = $BoundLabelContent}
}

# Create a custom object with the property "BoundLabelContent"
$data = New-TestObj "1st Bound Label Initial Content"

# Here is where we create the visual window
New-Window -Title 'Updating Controls from Timer Jobs' -SizeToContent "WidthAndHeight"  {
  # Use a DockPanel to hold all the Controls
  Dockpanel -Name dp -LastChildFill {
    # A button at the TOP - pressing it starts a timer job. Note the button's tag value is set to 1
    Button "Change Bound Label" -Name btn_1 -Tag 1 -Dock Top
    # A button at the Bottom - pressing it starts another, different, timer job. Note the button's tag value is set to 2
    Button "Change UnBound Label" -Name btn_2 -Tag 2 -Dock Bottom
    # A stackpanel to hold the labels
    Stackpanel -Name sp {
      # The first label is bound to the custom object $data created above
      Label -Name lbl_Bound -DataBinding @{ Content = Binding -Path BoundLabelContent -Source $data}
      # The second label is not bound to anything, and it's initial Content is declared here
      Label -Name lbl_UnBound -Content 'Original Unbound Content'
    } 
  } 
} -On_Loaded {

  # This is the main windows On_Loaded Event handler scriptblock
  write-host ("window On_Loaded Event handler starting. window: {0}" -f $window.Title)
  
  # Create scriptblocks that can be shared among event handlers. 
  #  Note that this can also be a good place for handlers for child controls in ItemTemplates but that's for another example
  #  ShowUI makes available a Scripts hashtable in the top-level window's Resources dictionary, which we will use
  $scripts = Get-Resource -Visual $window -Name Scripts
  
  # BtnOnClick is the On-Click handler (scriptblock) for both buttons. Handlers attached to a control run in the same thread as the control, hence this
  #  scriptblock is allowed to access and manipulate the visual controls directly
  $scripts.BtnOnClick = {
  
    # In a control's event handler , $this is the control that fired the event
    # above you saw that when each button was declared, that button had a unique value put into it's tag
    write-host ("BtnOnClick Handler starting. {0} tagged with {1}" -f $this.Name, $this.Tag)

    # Turn off the button to prevent multiple presses while the timer is running
    $this.IsEnabled = $false
    $mybuttonnumber = $this.Tag

    # Create a Timer and associate the TimerOnTick scriptblock (below) with the timer.
    # This type of timer [DispatcherTimer] runs on the UI thread, and it's scriptblock can update visual controls
    $window.Resources.Timers."Run-$mybuttonnumber" = (New-Object Windows.Threading.DispatcherTimer).PSObject.BaseObject
    $window.Resources.Timers."Run-$mybuttonnumber".Interval = "0:0:1"
    $window.Resources.Timers."Run-$mybuttonnumber".add_Tick($window.Resources.Scripts.TimerOnTick)
    # IMPORTANT NOTE: By assigning something to the timer's tag, you can pass an argument to the scriptblock
    $window.Resources.Timers."Run-$mybuttonnumber".Tag = $mybuttonnumber
    $window.Resources.Timers."Run-$mybuttonnumber".Start()
  }
  
  # TimerOnTick is the On-Tick handler for the timers
  $scripts.TimerOnTick = {
    # In a timer's event handler , $this is the timer that fired the event
    # above you saw that when this scriptblock is attached to the timer, that timer had a unique value (the button's tag's contents) put into the timer's tag
    write-host ("TimerOnTick Handler [{0}] starting. Tagged with {1}" -f $this, $this.Tag)
    
    # One cool thing about timers is that their scriptblocks keep context when they run, so the values of variables are available on each tick
    # Each time through, $iterations will get increased, and these are seperate for the two timers that get created 
    $iterations++
    
    # The buttons in this example are named btn_<digit>. The Button's tag contains the digit. ShowUI creates a variable $<control'sName> that points to the actual control
    # So the following will make $myButton point to the button that was pressed
    $mybuttonNumber = $this.Tag
    $myButton = iex -Command "`$btn_$mybuttonNumber"

    # Do different things based on which button was pressed
    switch ($mybuttonNumber) {
      # For button 1: update the bound data's value, and the button's content, and reset the values at iteration modulo 10
      1 {$data.BoundLabelContent = $myButton.Content = $iterations = $iterations % 10} 
      # For button 2: update the 2nd label's content directly, and the button's content, and reset the values at iteration modulo 5
      2 {$lbl_UnBound.Content = $myButton.Content = $iterations = $iterations % 5}
    }

  } # End of the timertick event handler
  
  # Add the BtnOnClick scriptblock to both buttons 
  Add-EventHandler $btn_1 Click (Get-Resource -Visual $window -Name "scripts").BtnOnClick
  Add-EventHandler $btn_2 Click (Get-Resource -Visual $window -Name "scripts").BtnOnClick
#$Host.EnterNestedPrompt()        

}-show

Write-Debug ("At showui exit, data.BoundLabelContent is {0}" -f $data.BoundLabelContent)