Using RxDart and Flutter to Create A Timer

Final product

Hi Everyone! Very long time without any content 😢 This year, as you can probably imagine, was very hectic. Something had to give, and unfortunately, it was this creative outlet that lost out. Let’s jump straight into something completely different!

I’ve been learning Flutter and mobile development, which has been very exciting! Today, I wanted to share with you a little example of a timer app built using Flutter with RxDart as a state management solution. Basically, that means not using the standard setState command in the Stateful Widget.

To kick things off, you’ll want to add the latest version of RxDart into your pubspec.yaml file. At the time of writing, this looks like the following:

rxdart: ^0.24.1

In the main.dart file, it can be kept pretty much as a blank slate. Only making sure to replace the home of the Scaffold with a new view. I like to create a separate folder for my ‘ui’ and my ‘state’.

We’ll kick off first by looking at the timer_state.dart that will be using RxDart. We need to create a new class, which I will call ‘TimerState’. In this new class, we need to create the following variables:

  • Private Stopwatch
  • BehaviorSubject of type String to track the timer display
  • BehaviorSubject of type bool to track whether the timer is running or not
  • A string for the initial display value set to ’00:00′
  • A bool for the initial running state of the timer, set to false

For the class builder, we need to build the two BehaviorSubjects. We’ll do that by creating the following class constructor:

TimerState({this.initialDisplay, this.initialIsRunning}) {
    _subjectDisplay = BehaviorSubject<String>.seeded(this.initialDisplay);
    _subjectIsRunning = BehaviorSubject<bool>.seeded(this.initialIsRunning);
}

Next, we need to create the two Streams that we can watch for changes to the timer display and whether the timer is running or not. We’ll use these in the UI:

Stream<String> get timerObservable => _subjectDisplay.stream;

Stream<bool> get isRunningObservable => _subjectIsRunning.stream;

To finish off the TimerState class, we need to build quite a few functions to help use the timer/stopwatch. Here’s a list below:

  1. Public function for starting the timer
  2. Private function for running the timer every second
  3. Private function for keep the timer running
  4. Private function for formatting the elapsed time to a minutes/seconds format
  5. Public function for pausing the timer
  6. Public function for resetting the timer
  7. Public function for resuming the timer
  8. Public dispose function to remove the BehaviorSubjects to avoid memory leaks

For simplisity sakes, here is the full class below:

import 'dart:async';
import 'package:rxdart/rxdart.dart';

///The class for the timer
class TimerState {
  ///The Stopwatch object for running the timer
  final Stopwatch _sWatch = Stopwatch();

  ///Creating a BehaviorSubject of type String for the Stopwatch display
  BehaviorSubject<String> _subjectDisplay;

  ///Creating a BehaviorSubject of type bool for the IsRunning variable
  BehaviorSubject<bool> _subjectIsRunning;

  ///The initial String value for the Stopwatch display
  String initialDisplay = '00:00';

  ///The initial bool value for the IsRunning variable
  bool initialIsRunning = false;

  ///The class builder
  ///Takes in the local initialDisplay and initialIsRunning to seed the
  ///subject behaviours
  TimerState({this.initialDisplay, this.initialIsRunning}) {
    _subjectDisplay = BehaviorSubject<String>.seeded(this.initialDisplay);
    _subjectIsRunning = BehaviorSubject<bool>.seeded(this.initialIsRunning);
  }

  ///A Stream for the Stopwatch String value
  Stream<String> get timerObservable => _subjectDisplay.stream;

  ///A Stream for the IsRunning bool value
  Stream<bool> get isRunningObservable => _subjectIsRunning.stream;

  ///Start the timer
  ///
  ///If the timer already has a value, it continues where it left off. Otherwise,
  ///it starts counting from 00:00
  void startTimer() {
    _subjectIsRunning.value = true;
    _sWatch.start();
    _startTimer();
  }

  ///Runs the Stopwatch every second
  void _startTimer() {
    Timer(Duration(seconds: 1), _keepRunning);
  }

  ///Keeps the Stopwatch running and updates the Stopwatch display BehaviourSubject
  void _keepRunning() {
    //Stop the timer from overflowing, max value should be 99:99
    if (_sWatch.elapsed.inMinutes >= 100) {
      pauseTimer();
      return;
    }
    if (_sWatch.isRunning) {
      _startTimer();
    }
    _subjectDisplay.sink.add(_formatStopWatch(_sWatch));
  }

  ///Formats the StopWatch elapsed time into the 00:00 format
  String _formatStopWatch(Stopwatch _swatch) {
    final String _inMinutes =
        (_swatch.elapsed.inMinutes % 60).toString().padLeft(2, '0');
    final String _inSeconds =
        (_swatch.elapsed.inSeconds % 60).toString().padLeft(2, '0');

    return "$_inMinutes:$_inSeconds";
  }

  ///Pause the timer and stop on the current display from being changed
  void pauseTimer() {
    _subjectIsRunning.value = false;
    _sWatch.stop();
  }

  ///Reset the timer display and the Stopwatch object
  void resetTimer() {
    _subjectIsRunning.value = false;
    _subjectDisplay.sink.add('00:00');
    _sWatch.reset();
    _sWatch.stop();
  }

  ///Resumed the timer and continue updating the Stopwach display
  void resumeTimer() {
    _subjectIsRunning.value = true;
    _sWatch.start();
    _startTimer();
  }

  ///Dispose of the BehaviorSubjects
  void dispose() {
    _subjectDisplay.close();
    _subjectIsRunning.close();
  }
}

This class and the app in general could be a lot simpler, but I chose to keep the pause, resume and reset options. Only the best content here 😂

Finally, lets move onto the actual UI 🥳

So, we will need a StatefulWidget as we will be creating an instance of the TimerState class which will need disposing. All the items in my view that needed to change, such as the buttons and the display, were built using the StreamBuilder widget. This is an awesome little widget that listens to a Stream and then changes the output based on a builder function. Here is a example:

StreamBuilder(
    stream: _timerState.timerObservable,
    builder: (context, snapshot){
        if(snapshot.hasData){
            return Text(snapshot.data)
        }else{
            return Text('No data')
        }
    }
)

Flutter widget trees can get quite confusing to look at, so imma just dump it here for you to look at 🤦‍♂️

import 'package:flutter/material.dart';
import 'package:rxdart_timer/blocs/timer_state.dart';

class TimerView extends StatefulWidget {
  @override
  _TimerViewState createState() => _TimerViewState();
}

class _TimerViewState extends State<TimerView> {
  //Create a new instance of the timer for the stateful widget
  TimerState _timerState = TimerState();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          ///Stream builder to watch the timer observable stream from the BLOC
          StreamBuilder(
            stream: _timerState.timerObservable,
            builder: (context, snapshot) {
              ///Checking if the Stream has data
              ///
              ///For some reason, the seeded option isn't working in the BLOC
              if (snapshot.hasData) {
                return Text(snapshot.data);
              } else {
                return Text('00:00');
              }
            },
          ),
          const SizedBox(
            height: 40,
          ),

          ///The start button built using a StreamBuilder
          ///
          ///Checked if the timer is running and sets a seperate bool. The button then
          ///checks this bool and either allows the startTimer function to be ran or
          ///simple disabled the button
          StreamBuilder(
            stream: _timerState.isRunningObservable,
            builder: (context, snapshot) {
              bool _running = snapshot.hasData ? snapshot.data : false;
              return FlatButton(
                color: Colors.green,
                onPressed: _running ? null : _timerState.startTimer,
                child: Text('Start'),
              );
            },
          ),

          ///A row for containing the StreamBuilder for the Pause button and a
          ///regular Reset button
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              ///Using a StreamBuilder to build the Pause button
              ///
              ///I build another bool, similar to the strart button, to check whether the
              ///button should be enabled.
              StreamBuilder(
                stream: _timerState.isRunningObservable,
                builder: (context, snapshot) {
                  bool _running = snapshot.hasData ? snapshot.data : false;
                  return Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      FlatButton(
                        color: Colors.red[600],
                        onPressed: _running ? _timerState.pauseTimer : null,
                        child: Text('Pause'),
                      ),
                    ],
                  );
                },
              ),

              ///Seperating the Pause and Stop button by 30 pixels
              const SizedBox(
                width: 30,
              ),

              ///Reset button, always enabled
              FlatButton(
                color: Colors.yellow[700],
                onPressed: _timerState.resetTimer,
                child: Text('Reset'),
              ),
            ],
          )
        ],
      ),
    );
  }

  ///Disposes of the _timerState
  @override
  void dispose() {
    _timerState.dispose();
    super.dispose();
  }
}

There we go, I hope you can see all of the code goes together. Basically, adding on top of Darts built in Streams to make them easier to manage and subscribe to 🙂

On top of all the code in the post, here is a link to my GitHub repo with all the code. You might notice a few differences as well.

Comment if you have any issues 💕

WPF Material Design Toggle Button

Another short post today, here I modified the code for my Apple Style Toggle Button so that it more resembled the MaterialDesign toggle button. I thought of this whilst playing with the YouTube autoplay button. Here is my code:

<Style TargetType="{x:Type ToggleButton}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <Viewbox>
                    <Border x:Name="Border" CornerRadius="10"
                            Background="#FFE2E2E2"
                            Width="40" Height="20">
                        <Border.Effect>
                            <DropShadowEffect ShadowDepth="0.5" Direction="0" Opacity="0.3" />
                        </Border.Effect>
                        <Ellipse x:Name="Ellipse" Fill="#FF909090" Stretch="Uniform"
                                 Margin="-8 -4"
                                 Stroke="Gray" StrokeThickness="0.2"
                                 HorizontalAlignment="Stretch">
                            <Ellipse.Effect>
                                <DropShadowEffect BlurRadius="10" ShadowDepth="1" 
                                                  Opacity="0.3" Direction="260" />
                            </Ellipse.Effect>
                        </Ellipse>
                    </Border>
                </Viewbox>
                <ControlTemplate.Triggers>
                    <EventTrigger RoutedEvent="Checked">
                        <BeginStoryboard>
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetName="Ellipse"
                                                    Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)"
                                                    To="#FF0069F3"
                                                    Duration="0:0:0.05"
                                                    AccelerationRatio="0.7"
                                                    DecelerationRatio="0.3"/>
                                <ThicknessAnimation Storyboard.TargetName="Ellipse"
                                                    Storyboard.TargetProperty="Margin"
                                                    To="20 -4 -8 -4"
                                                    Duration="0:0:0.15" 
                                                    AccelerationRatio="0.7"
                                                    DecelerationRatio="0.3"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                    <EventTrigger RoutedEvent="Unchecked">
                        <BeginStoryboard>
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetName="Ellipse"
                                                    Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)"
                                                    To="#FF909090"
                                                    Duration="0:0:0.05" 
                                                    AccelerationRatio="0.7"
                                                    DecelerationRatio="0.3"/>
                                <ThicknessAnimation Storyboard.TargetName="Ellipse"
                                                    Storyboard.TargetProperty="Margin"
                                                    To="-8 -4"
                                                    Duration="0:0:0.15"
                                                    AccelerationRatio="0.7"
                                                    DecelerationRatio="0.3"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

And finally, here is a little GIF of the toggle button:

Enjoy!

Apple Style Toggle Button in WPF

A nice short post here, I wanted to share with you some code I recently used to create an Apple-style toggle button for WPF applications. I was quite surprised with how easy this was to make. Obviously it isn’t perfect but it makes do for my applications.

This is the style that I used:

<Style TargetType="{x:Type ToggleButton}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <Viewbox>
                    <Border x:Name="Border" CornerRadius="10"
                            Background="#FFFFFFFF"
                            Width="40" Height="20">
                        <Border.Effect>
                            <DropShadowEffect ShadowDepth="0.5" Direction="0" Opacity="0.3" />
                        </Border.Effect>
                        <Ellipse x:Name="Ellipse" Fill="#FFFFFFFF" Stretch="Uniform"
                                 Margin="2 1 2 1"
                                 Stroke="Gray" StrokeThickness="0.2"
                                 HorizontalAlignment="Stretch">
                            <Ellipse.Effect>
                                <DropShadowEffect BlurRadius="10" ShadowDepth="1" Opacity="0.3" Direction="260" />
                            </Ellipse.Effect>
                        </Ellipse>
                    </Border>
                </Viewbox>
                <ControlTemplate.Triggers>
                    <EventTrigger RoutedEvent="Checked">
                        <BeginStoryboard>
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetName="Border"
                                                    Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
                                                    To="#FF4CD661"
                                                    Duration="0:0:0.1" />
                                <ThicknessAnimation Storyboard.TargetName="Ellipse"
                                                        Storyboard.TargetProperty="Margin"
                                                        To="20 1 2 1"
                                                        Duration="0:0:0.1" />
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                    <EventTrigger RoutedEvent="Unchecked">
                        <BeginStoryboard>
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetName="Border"
                                                    Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
                                                    To="White"
                                                    Duration="0:0:0.1" />
                                <ThicknessAnimation Storyboard.TargetName="Ellipse"
                                                        Storyboard.TargetProperty="Margin"
                                                        To="2 1 2 1"
                                                        Duration="0:0:0.1" />
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Using this, I am able to have any size toggle button I want and it will still look the same. Thank goodness for view boxes! 🙌

Here is a little GIF showing the toggle button in action. I left it to just resize depending on the actual window to demonstrate it’s scalability:

Enjoy!

Using app.xaml and ResourceDictionaries for Cleaner WPF Customisation

When I first started making WPF forms, I was using PowerShell. This was a good starting point I think as I felt really comfortable with PowerShell which let me experiment more freely and break things in a way that I still felt comfortable.

However, this also meant putting all of my XAML into a single string. This might not sound too bad, but when you have five buttons all with slightly different templates and behaviours, the code quickly becomes messy and hard to read. Note that XAML is easy to read in the first place 😫 I was no stranger to having code that looked like this:

<Border Grid.Column="0" Grid.Row="1" Background="#FFE87E31" HorizontalAlignment="Right" Width="25" Height="25" Margin="0,0,2,0" CornerRadius="20"  BorderBrush="White" BorderThickness="1">
                <Label Name="Search_Button" Cursor="Hand" Foreground="White" Content="🔍" FontSize="12" Width="25" Height="27" Margin="-1.667,-1.667,-0.334,-0.334" />
            </Border>
<!-- OBJECT PANEL AND OBJECTS -->
            <Border HorizontalAlignment="Stretch" Grid.Column="0" Grid.Row="2" VerticalAlignment="Stretch" Background="#FF34495F" >
                <ScrollViewer VerticalScrollBarVisibility="Auto">
                    <StackPanel>
        <!-- ALL OPTION OBJECTS HERE -->
                        <Border Height="35">
                            <Border.Style>
                                <Style>
                                    <Setter Property="Border.Background" Value="#FF34495F"/>
                                    <Style.Triggers>
                                        <Trigger Property="Border.IsMouseOver" Value="True">
                                            <Setter Property="Border.Background" Value="#FF1F2A36" />
                                        </Trigger>
                                    </Style.Triggers>
                                </Style>
                            </Border.Style>
                            <Label Name="General_Information_Button" Cursor="Hand" VerticalContentAlignment="Center" Foreground="White" Content="General Information" FontFamily="Century Gothic" FontSize="14" />
                        </Border>

Horrible I know…

But when I moved to use C# with WPF, I found that I could have separate resources that could be used by multiple controls at the same time. You do this by adding a resource dictionary to the app.xaml file inside the WPF project.

Here is a quick example of what I mean. I created a Styles folder in the root of my WPF project and added a new ResourceDictionary(WPF). I called this resource dictionary “TextStyles” and it looks like this:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="FontFamily" Value="Arial"/>
        <Setter Property="FontSize" Value="28"/>
        <Setter Property="FontWeight" Value="Bold"/>
        <Setter Property="Foreground" Value="White"/>
    </Style>
</ResourceDictionary>

Perfect, I then added this to my app.xaml file which now looks like this:

<Application x:Class="WPFUI.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WPFUI">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Styles/TextStyles.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

From now on, all the text blocks in the WPF application would use the Arial font, use font size 28, be bold and also be white.

Important Notes:

  1. It’s important to add your resource dictionaries to the app.xaml file in the correct order. You want to work from top to bottom. Meaning you don’t want a resource dictionary using something from another resource dictionary below it. For example, if you are using custom colours and want your text blocks to be that colour, you would put your colour resource first and then your text block resource.
  2. If you don’t want a resource to be used everywhere in your application if you style inside the resource dictionary a name. Then you can reference the style inside your WPF XAML code.

Enjoy!

Multiple Sounds in WPF Application

So I’m making a small WPF game, which you can find here by the way 😛, and I come across a confusing and infuriating issue. WPF has two built-in audio players: SoundPlayer and MediaPlayer.

SoundPlayer: Can play audio files that are embedded resources in the application, however, this can only play one sound at once.

MediaPlayer: Cannot play audio files that are embedded resources in the application, however, this can play multiple sounds at once.

I think you can see the issue I was having. I wanted to play some background music in my game as well as some sound effects for certain actions in the game. This meant the easiest way to get this functionality was to use both of these options and save the background music to the local disk as a temporary file. Not as clean as I wanted, but it will do I guess 🤷‍♂️

First, I set up my SoundPlayer as this was the simplest one of the two. I created a new SoundPlayer object using the embedded resource:

private static readonly SoundPlayer _soundOne = new SoundPlayer(WPF.Properties.Resources.soundOne);

Then when I wanted this sound to play, I would simply use:

_soundOne.Play();

Simple enough for the SoundPlayer… Now we move onto the MediaPlayer.

First, I make sure that my audio file is set as an Embedded Resource under the Build Actions in the file’s properties in Visual Studio:

You can see from this, that I have a WAV file called “backgroundmusic.wav” which I need to save to disk, play continuously and then delete once the form is closed.

Now that we have done this, we can create the method for saving the embedded WAV file to the %temp% location on disk. This is how I did that:

public static void SaveMusicToDisk(){
    //This sets up a new temporary file in the %temp% location called "backgroundmusic.wav"
    using (FileStream fileStream = File.Create(Path.GetTempPath() + "backgroundmusic.wav")){
        
        //This them looks into the assembly and finds the embedded resource
        //inside the WPF project, under the assets folder
        //under the sounds folder called backgroundmusic.wav
        //PLEASE NOTE: this will be different to you
        Assembly.GetExecutingAssembly().GetManifestResourceStream("WPF.Assets.Sounds.backgroundmusic.wav").CopyTo(fileStream);
    }
}

Great we have now saved the embedded resource to disk, how can we play this now? 🤷‍♂️

We play this by creating a new MediaPlayer object and using the temp file location to play the audio:

//Create a new MediaPlayer object
private static readonly MediaPlayer _backgroundMusic = new MediaPlayer();

public static void StartBackgroundMusic(){
    //Open the temp WAV file saved in the temp location and called "backgroundmusic.wav"
    _backgroundMusic.Open(new Uri(Path.Combine(Path.GetTempPath(), "backgroundmusic.wav")));
    //Add an event handler for when the media has ended, this way
    //the music can be played on a loop
    _backgroundMusic.MediaEnded += new EventHandler(BackgroundMusic_Ended);
    //Start the music playing
    _backgroundMusic.Play();
}

My BackgroundMusic_Ended method looks like this and just makes sure that the music is always restarted once it has finished:

private static void BackgroundMusic_Ended(object sender, EventArgs e){
    //Set the music back to the beginning
    _backgroundMusic.Position = TimeSpan.Zro;
    //Play the music
    _backgroundMusic.Play();
}

Now that the music is playing continuously on a loop, how do we stop the music, dispose of the object and delete the temp WAV file from disk?

To stop and dispose of the MediaPlayer object is pretty easy, you can use:

public static void StopBackgroundMusic(){
    //Stops the music 
    _backgroundMusic.Stop();
    //Disposes of the MediaPlayer object
    _backgroundMusic.Close();
}

Now we go about removing the WAV file from the disk as we don’t want this to remain on the user’s computer once they have exited the application:

public static void DeleteMusicFromDisk(){
    File.Delete(Path.Combine(Path.GetTempPath(), "backgroundmusic.wav"));
}

DONE! ✔️

With all the above code and a little tinkering to fit your specific setup, you should be able to use multiple sounds at once without an issue. Enjoy!

Saving Embedded WPF Resources as Files on Disk

In this post, I am going to show you how you can save an embedded resource as a file on disk. This is useful in certain scenarios. For example, I used this to save some background music onto the computer as the native MediaPlayer couldn’t use the application paths.

First things first, you want to make sure that you have set the Build Action to Embedded Resource in the properties for each of the items:

Once you have done that, we can go about creating a method to get the resource and save it to file. For me, that is as simple as:

public static void SaveMusicToDisk(){
    //This creates a temp file in the %temp% directory called backgroundMusic.wav
    using (FileStream fileStream = File.Create(Path.GetTempPath() + "backgroundMusic.wav")){
        
        //This looks into the assembly and gets the resource by name
        //For this to work, you need to use the full application path to the resource
        //You get this by using your project name following by the folder tree to your item
        Assembly.GetExecutingAssembly().GetManifestResourceStream("WPF.Assets.Sounds.backgroundmusic.wav").CopyTo(fileStream);
    }
}

So that saves the file, what if I want to delete the file once the user wants to close the application?

For that I would use:

public static void DeleteMusicFromDisk(){
    //This looks into the %temp% folder and deletes the file called "backgroundMusic.wav"
    File.Delete(Path.Combine(Path.GetTempPath(), "backgroundMusic.wav"));
}

Peeeeerfect! 🎉 Does everything I need 😊

So, what if I wanted to save all the resources?

For that I could put all the resources into a string array and loop through them like this:

public static void SaveAllResources(){
    
    //Gets all the resources associated with the assembly and puts them into an array
    string[] resources = Assembly.GetExecutingAssembly().GetManifestResourceNames();

    foreach (string resource in resources)
    {
        //Create a new file in the %temp% for each resource
        using (FileStream fileStream = File.Create(Path.GetTempPath() + resource))
        {
            //Get the resource and save it to the current file
            Assembly.GetExecutingAssembly().GetManifestResourceStream(resource).CopyTo(fileStream);
        }
    }
}

I hope you learn something or found this helpful. Enjoy!

PowerShell Classes and Class Lists

I found that I could use classes in PowerShell similar to how I use them in C#. I instantly wanted to play with this and I thought I would share this as well.

To create a class in PowerShell, it’s as simple as:

#Person class
class PersonClass{
	[String]$Name
	[Int]$Age
}

This allows a “Person” to be created that has the attributed of a name and an age. Simple stuff.

Say I wanted to have a bunch of these “Person”s in a list, a “People” list if you will. Then I could do something like this:

#Creating a list to hold the people using the PersonClass
$People = New-Object 'System.Collections.Generic.List[PSObject]'

#Creating a new person
$newPerson = [PersonClass]::new()
$newPerson.Name = "Roy Orbison"
$newPerson.Age = "24"

#Adding the new person to the people list
$People.Add($newPerson)

What if I wanted to add something like a “Pets” attribute onto the person? Well, I could create a new class to hold a framework for each pet and create a new list attribute in the PersonClass. Here is my PetClass:

#Pet class
class PetClass{
    [String]$Name
    [Int]$Age
    [String]$Color
}

And here is how I add it to my PersonClass so that I can have a list of pets for each user:

#Person class
class PersonClass{<br>    [String]$Name
    [Int]$Age
    [PetClass[]]$Pets
}

Now its really simple to create a list of people with a list of any pets that they might have. Stitching this all together, it looks like this:

#Person class
class PersonClass{
	[String]$Name
	[Int]$Age
    [PetClass[]]$Pets
}

#Pet class
class PetClass{
    [String]$Name
    [Int]$Age
    [String]$Color
}

#Creating a list to hold the people using the PersonClass
$People = New-Object 'System.Collections.Generic.List[PSObject]'

#Creating a new person
$newPerson = [PersonClass]::new()
$newPerson.Name = "Roy Orbison"
$newPerson.Age = "24"

#Adding pets to the new person
for ($i = 0; $i -le 5; $i++){
    $newPet = [PetClass]::new()
    $newPet.Name = $i
    $newPet.Age = $i + 2
    $newPet.Color = "Brown"

    #Adding the pet to the new person
    $newPerson.Pets += $newPet
}

#Adding the new person to the people list
$People.Add($newPerson)

Above you can see that I have created a new person called “Roy Orbison” with an age of “24” and I have added five pets. The pet names and age aren’t really accurate but it’s good enough for this demonstration.

Continuing from this, I could add as many users as I want or even create new classes to add extra framework information for existing classes.

Searching this information isn’t as straight forward in PowerShell as it is in C# but it’s still quite easy. You can see how I get a list of all the pets that Roy Orbison has below:

$People | Where-Object {$_.Name -eq "Roy Orbison"} | Select-Object -ExpandProperty Pets

Upon finishing this, I realised that it would have been much more appropriate to do the users and albums, instead of pets. But I’m far too lazy to change what I already have…

Enjoy!

Caliburn.Micro Notify Change for Static Property

Normally in Caliburn.Micro and MVVM in general, you would have non-static properties that would be updated and notified like this:

private string _name;

public string Name
{
    get { return _name; }
    set
    {
        _name = value;
        NotifyOfPropertyChange(() => Name);
    }
}

However, if I had a static property, for example, if I wanted a property that could be accessed by another namespace, I would need to implement my own event handler as the default NotifyOfPropertyChange doesn’t operate on static properties.

This is the new EventHandler that I implemented:

public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
private static void NotifyStaticPropertyChanged(string propertyName)
{
    StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
}

Finally, in order to use it. I would use this instead of the first example for non-static properties:

private string _name;

public string Name
{
    get { return _name; }
    set
    {
        _name = value;
        NotifyStaticPropertyChanged(() => Name);
    }
}

I hope this helps someone. Enjoy!

Caliburn.Micro MVVM Boolean To Visibility Converter

Say I wanted to toggle the visibility of a WPF object in an MVVM way, what would I need to do?

Here is what I currently have:

  • ViewModels
    • ShellViewModel
    • LoadingViewModel
  • Views
    • ShellView
    • LoadingView

When the application is loaded, the ShellViewModel is used to display the LoadingView in a ContentControl object. I have a button on there that I want to become visible after the LoadingView has been activated for 5 seconds.

What I need to do is created a custom class that has two methods: a way of converting a boolean to a visibility; and a way of converting a visibility to a visibility.

So I created a new folder in my tree called “Converters“. In here, I create a new class called “BooleanToVisiblityConverter“.  Here is what my tree now looks like:

  • ViewModels
    • ShellViewModel
    • LoadingViewModel
  • Views
    • ShellView
    • LoadingView
  • Converters
    • BooleanToVisibilityConverter

In my BooleanToVisibilityConverter class, I inherited from IValueConverter and added the necessary two methods. You can see the entire class below:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;

namespace Project.Converters
{
    /// <summary>
    /// Contains the converter and convertback methods for the boolean to visibility conversions
    /// </summary>
    public sealed class BooleanToVisibilityConverter : IValueConverter
    {
        /// <summary>
        /// Used to convert a boolean to a visibility
        /// </summary>
        /// <param name="value">This is the boolean input</param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns>Returns a visibility</returns>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (!(value is bool))
            {
                //If there is an issue with the input, return collapsed
                return Visibility.Collapsed;
            }
            return (bool)value ? Visibility.Visible : Visibility.Collapsed;
        }

        /// <summary>
        /// Used to take a visibility and returns a visibility
        /// </summary>
        /// <param name="value">This is the boolean input</param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns>Returns a visibility</returns>
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (!(value is Visibility))
            {
                //If there is an issue wtih the input, return collapsed
                return Visibility.Collapsed;
            }
            return (Visibility)value == Visibility.Visible;
        }
    }
}

Next, in my LoadingView, I added “ xmlns:sp=”clr-namespace:Project.Converters” so that the view could use the converter namespace. This is what my view dependancies look like:

<UserControl x:Class="Project.Views.MainView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:cal="http://www.caliburnproject.org"
             xmlns:sp="clr-namespace:Project.Converters"
             xmlns:local="clr-namespace:Project.Views"
             mc:Ignorable="d" FontSize="14" FontFamily="/Project;component/Assets/Fonts/#Roboto"
             d:DesignHeight="550" d:DesignWidth="400">

I then added the binding to the visibility property of my button. You can see this below:

<Button Name="LoadingButton" Content="Press Me" Visibility="{Binding ButtonIsVisible, Converter={StaticResource BooleanConverter}, FallBackValue=Collapsed}"

This means that it will get its visibility value from the LoadingViewModel and if this fails, it will fall back to being collapsed.

We’re almost done. In the LoadingViewModel create a full property which will hold and change the value for the visibility. This needs to be named the same as the binding given in the LoadingView (i.e ButtonVisibility). You can see this below:

private bool _buttonIsVisible;

public bool ButtonIsVisible
{
    get {return _buttonIsVisible;
    set
    {
        if (value != _buttonIsVisible)
        {
            _buttonIsVisible = value;
            NotifyOfPropertyChange(() => ButtonIsVisible);
        }
    }
}

So now if you want the button to be visible, you can just update the ButtonIsVisible property in the LoadingViewModel. Here is an example below (Don’t actually do this):

public LoadingViewModel(){
    ButtonIsVisible = false;
    Task.Delay(5000);
    ButtonIsVisible = true;
}

I hope this helped you. Enjoy!

Setting Up Caliburn.Micro MVVM

This is a fairly lengthy post that shows how to set up an initial MVVM WPF form using Caliburn.Micro.

Let’s get started, we’ll open up Visual Studio and chose to create a new WPF:

Visual Studio -> New Project -> WPF App (.Net Framework)

I usually set the project name to something like WPFUI and my solution name to be something like MVVMProject or the actual product name. For example, Microsoft might use Microsoft Outlook. Probably not, but you get what I’m saying…

 

1)

First I will add Caliburn.Micro to my project. To do this go to Solution Explorer -> References -> Add NuGet Packages and search for Caliburn.Micro:

 

2)

Now we can delete the MainWindow.xaml

 

3)

Create a Views and ViewModels folder. This is where your viewmodels and views will call home.

 

4)

Create a new class in the ViewModels folder and call it ShellViewModel and make it public. You’ll also want to inherit from screen and add the using statement for Caliburn.Micro:

5)

Create a new window in the Views folder and call it ShellView

6)

Create a new class in the root directory called Bootstrapper and inherit from BootstrapperBase and add the using statement for Caliburn.Micro

7)

In App.xaml remove the StartupUri element and add the bootstrapper class as a resource by adding:

<ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary>
            <local:Bootstrapper x:Key="Bootstrapper" />
        </ResourceDictionary>
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

Your app.xaml should look like this:

 

8)

Now go back into your Bootstrapper class and add the following:

public Bootstrapper()
{
    Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
    DisplayRootViewFor<ShellViewModel>();
}

You’ll also need to add a using statement for System.Windows and ProjectName.ViewModels.

The program should now launch and show an empty white screen with “ShellView” as the window’s title. This is the first entry into a hopefully long series into the world of Caliburn.Micro.

Enjoy! 😊