Powershell hr

I saw this on reddit (or possibly hackernews) the other day that produces a simple banner line in bash.

So here's a powershell version:

function hr($txt)
{
  $ui = (Get-Host).UI.RawUI
  $width = $ui.WindowSize.width
  $n = ($width / $txt.Length) -1
  $bar = "$txt" *  $n
  echo $bar
}

Year in Review

An extremely busy and quite interesting year for all the right reasons:

  • Played around with a few new languages: go, ruby, D, Java.
  • Lines of C++ and Python written: lost count!
  • Some Android and OS X development.

Go is an enjoyable little language with lightweight and simple concurrency, great library support and fast compilations times. Coming from a C++ background, building the entire Go compiler suite from source in seconds is excellent.

Pycharm has been a revelation for Python development. Debugging, testing and framework support all built in.

Python virtual envs

A simple function to bootstrap Python virtualenvs in Powershell. Add it to your profile and then call it as:

create_virtualenv c:\temp\myvirtualenv

Script is here:

function create_virtualenv([string] $virtualenv_path)
{
    #  I assume that python and 7z are on your path.
    $python = "python.exe"
    $z = "7z.exe"
    $temp_dir="C:\temp"

    if (-not(Test-Path $temp_dir)) {
        mkdir $temp_dir
    }

    #  For some reason 7zip doesn't like the path that comes from $env:TEMP
    $downloaded_archive = $temp_dir + "\virtualenv.tar.gz"
    $temp_virtualenv = $temp_dir + "\temporary_virtualenv"
    $url = "https://github.com/pypa/virtualenv/tarball/develop"

    #  Get virtual env
    Write-Host -fore green "Downloading virtual env from $url"
    $client = new-object System.Net.WebClient
    $client.DownloadFile($url, $downloaded_archive)

    #  Unzip it
    Write-Host -fore green "Unzipping $downloaded_archive"
    & "$z" x -y -o$temp_dir $downloaded_archive > $null

    #  Extract tar
    $tarfile = $downloaded_archive -replace '.gz', ''
    if (-not(Test-Path $tarfile)) {
        Write-Host -fore red "Failed to extract to $tarfile"
        return 1
    }
    Write-Host -fore green "Extacting from tarball $tarfile into $temp_virtualenv"
    $output = "-o" + $temp_virtualenv
    & "$z" x -y $tarfile $output > $null

    if (-not(Test-Path $temp_virtualenv)) {
        Write-Host -fore red "Failed to extract to $temp_virtualenv"
        return 1
    }

    #  Find the virtual env python script in the temporary virtual env we downloaded
    $search_for = "virtualenv.py"
    $virtualenv = (Get-ChildItem -Path $temp_virtualenv -Filter $search_for -Recurse).FullName

    if (-not(Test-Path $virtualenv)) {
        Write-Host -fore red "Could not find the virtual env script $virtualenv"
        return
    }

    Write-Host -fore green "Creating virtual env $virtualenv_path"
    if (Test-Path $virtualenv_path) {
        Write-Host -fore yellow "The path $virtualenv_path already exists, deleting it"
        Remove-Item -Force -Recurse $virtualenv_path
    }
    & $python $virtualenv $virtualenv_path

    #  Clean up the temporary virtual env
    Write-Host -fore green "Cleaning up"
    Remove-Item -Force -Recurse $temp_virtualenv
    Write-Host -fore green "Finished - now run the activate script in $virtualenv_path\Scripts"
}

A Basic Rakefile

I don't like spending too much time setting up a new automated build, and rakefiles are an effective way to capture a lot of the boring repetition. Also, in the spirit of "don't repeat yourself", I have a rakefile that that I have been gradually building over the past few months. For my purposes, it now just requires a single edit to use in a new project.

It is now at the point where, if you stick with the fairly standard Visual Studio project layout (this is the only implicit hidden logic in the script), all I have to do each time is change the name of the solution I want to build, and drop the solution at the top of my folder tree.

The file can be downloaded from here.

The rakefile provides:

  • nuget package restore via rake setup
  • nuget self updating via rake update_nuget
  • automatic test location, and running via xunit (end your test names as .Tests.dll)
  • debug and release builds.
  • adding the git hash to assemblies.

The task list:

rake all                # Clean, rebuild and test entire build
rake build              # Build everything
rake clean              # Clean entire build
rake debug:build        # Build
rake debug:clean        # Clean
rake debug:tests        # Run tests
rake default            # Default is to build everything
rake release:build      # Build
rake release:clean      # Clean
rake release:tests      # Run tests
rake restore_packages   # Restore nuget packages
rake restore_xunit      # Restore xunit from nuget
rake setup              # Set up everything if checked out for the first time
rake tests              # Run all tests
rake update_nuget       # Update nuget
rake version            # Add git hash to assemblies

Currently, I am not a big fan of dependencies, especially since in this case, rake setup will go to nuget.org repeateedly, I do not need it to run every time I type rake tests.

On the other hand, tests require builds, so that is added as a task dependency.

I have chosen to use namespaces to nest the builds, with the build type as the outer namespace, rather than the reverse, i.e. rake debug:build rather than rake build:debug. This tends to lead to a more discoverable hierarchy of tasks.

In order to use it, change:

SOLUTION=File.join(ROOT, 'MY_SOLUTION_HERE.sln' )

and then:

rake setup
rake all

and you're done. In fact these are the only two steps you need to add into your automated build system, and being able to repeat your automated build on your developer PC is "A Good Thing".

Rake and albacore tasks are fairly boilerplate, so the resulting rakefile is extremely simple. The file in all its glory:

#  Call
#
#    rake setup
#
#  to restore nuget packages, and grab xunit

require 'albacore'
require 'find'

################################
#  Helper methods
################################

#  Find any tests that end with '.tests.dll' for the given build type
#  assuming that is built into bin/<build_type>
def find_tests(root, build_type)
    paths = []
    re = Regexp.new(".*/bin/#{build_type}.*Tests\.dll$", Regexp::IGNORECASE)
    Find.find(root) do |path|
        paths << path if path =~ re
    end
    return paths
end

################################
#  Variables/configuration
################################

#  Define the root relative to this file.
ROOT=File.expand_path('.', File.dirname(__FILE__))

PACKAGES_DIR=File.join(ROOT, 'packages')

#  What we're building
SOLUTION=File.join(ROOT, 'MY_SOLUTION_HERE.sln' )

#  Nuget binary
NUGET_EXE = File.join(ROOT, '.nuget/nuget.exe')

#  Nuget args to get XUnit
NUGET_XUNIT_PARAMS = "install xunit.runners -Version 1.9.1 -OutputDirectory " + PACKAGES_DIR

#  Path to xunit (gets installed by nuget)
XUNIT_EXE = File.join(PACKAGES_DIR, "xunit.runners.1.9.1/tools/xunit.console.clr4.x86.exe")

#  Path to xunit tests - auto discovered by convention
XUNIT_DEBUG_TESTS = find_tests(ROOT, 'Debug')
XUNIT_RELEASE_TESTS = find_tests(ROOT, 'Release')

#  Multi-cpu compile, no logo, low verbosity
COMPILER_SWITCHES = {
    'M' => true,
    'verbosity' => 'quiet',
    'nologo' => true
}

################################
#  Common top-most tasks.
################################


#  Relative to this file
desc 'Restore nuget packages'
task :restore_packages do
    FileList["**/packages.config"].each do |filepath|
        sh "#{NUGET_EXE} install #{filepath} -OutputDirectory #{PACKAGES_DIR}"
    end
end


desc "Restore xunit from nuget"
exec :restore_xunit do |cmd|
    cmd.command = NUGET_EXE
    cmd.parameters = NUGET_XUNIT_PARAMS
end

desc "Update nuget"
exec :update_nuget do |cmd|
    cmd.command = NUGET_EXE
    cmd.parameters = "update -Self"
end


desc 'Set up everything if checked out for the first time'
task :setup => [:restore_xunit , :restore_packages]


desc 'Clean entire build'
task :clean => ["release:clean", "debug:clean"]


desc 'Run all tests'
task :tests => ["debug:tests", "release:tests"]


desc 'Build everything'
task :build => ["debug:build", "release:build"]


desc 'Clean, rebuild and test entire build'
task :all => [:clean, :build, :tests]


desc 'Add git hash to assemblies'
task :version do |t|
    clean_clone = `git diff --shortstat #{ROOT}`.empty?

    if clean_clone
        abbrev_commit = `git log -1 --pretty="%h" #{ROOT}`.delete("\n")
        assemblies = []
        Find.find('.') do |path|
            assemblies << File.expand_path(path) if path =~ /AssemblyInfo\.cs$/
        end

        assemblies.each do |assembly|
            text = File.read(assembly)
            File.open(assembly, 'w') { |f|  f.write(text.gsub(/development\-version/, abbrev_commit)) }
        end
    else
        puts 'You have working copy changes'
    end
end


desc 'Default is to build everything'
task :default => :all


################################
#  Debug tasks
################################


namespace 'debug' do
    desc 'Build'
    msbuild :build do |msb|
        msb.properties = { :configuration => :Debug }
        msb.targets = [ :Build ]
        msb.other_switches = COMPILER_SWITCHES
        msb.solution = SOLUTION
    end

    desc 'Clean'
    msbuild :clean do |msb|
        msb.properties = { :configuration => :Debug }
        msb.targets = [ :Clean ]
        msb.other_switches = COMPILER_SWITCHES
        msb.solution = SOLUTION
    end

    desc 'Run tests'
    xunit :tests => "debug:build" do |xunit|
        xunit.command = XUNIT_EXE
        xunit.assemblies = XUNIT_DEBUG_TESTS
    end
end


################################
#  Release tasks
################################


namespace 'release' do
    desc 'Build'
    msbuild :build do |msb|
        msb.properties = { :configuration => :Release, :platform => 'Any CPU' }
        msb.targets = [ :Build ]
        msb.other_switches = COMPILER_SWITCHES
        msb.solution = SOLUTION
    end

    desc 'Clean'
    msbuild :clean do |msb|
        msb.properties = { :configuration => :Release, :platform => 'Any CPU' }
        msb.targets = [ :Clean ]
        msb.other_switches = COMPILER_SWITCHES
        msb.solution = SOLUTION
    end

    desc 'Run tests'
    xunit :tests => "release:build" do |xunit|
        xunit.command = XUNIT_EXE
        xunit.assemblies = XUNIT_RELEASE_TESTS
    end
end

If you have never used rakefiles before, simply download the latest version of Ruby, followed by gem install albacore in a command prompt, and that is it.

XAML-Only Font ComboBox

This is a repost of my CodeProject article

Comments can be found there.

Introduction

I needed a simple ComboBox to select a FontFamily in a WPF application (I don't care about the font-weight). After some searching I found Pete O'Hanlon's article, describing what I wanted.

So why another (short!) article? The first commenter in the article suggested this:

<ListBox ItemsSource="{Binding Source={x:Static Member=Fonts.SystemFontFamilies}}">
      <ListBox.ItemTemplate>
          <DataTemplate>
              <Label FontFamily="{Binding .}" Content="{Binding Source}" />
          </DataTemplate>
      </ListBox.ItemTemplate>
  </ListBox>

as an alternative, which made me think about combining the two as a pure XAML solution that you can cut and paste (as Pete's code has a tiny bit of code-behind).

In order to create the XAML solution I found out a few interesting things that I thought I would share in a real example (as I'm still getting to grips with the many facets of WPF).

Font chooser in use

Font chooser menu

Show Me The XAML!

Here it is, in its entirety:

<ComboBox
            xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
            ItemTemplate="{DynamicResource FontTemplate}">
      <ComboBox.Resources>

          <CollectionViewSource x:Key="myFonts" Source="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
              <CollectionViewSource.SortDescriptions>
                  <ComponentModel:SortDescription PropertyName="Source" />
              </CollectionViewSource.SortDescriptions>
          </CollectionViewSource>

          <Style x:Key="FontStyle">
              <Setter Property="Control.FontFamily" Value="{Binding Source}" />
              <Setter Property="Control.FontSize" Value="16" />
          </Style>

          <DataTemplate x:Key="FontTemplate">
              <StackPanel VirtualizingStackPanel.IsVirtualizing="True">
                  <TextBlock Style="{StaticResource FontStyle}"
                             Text="{Binding Source}"
                             ToolTip="{Binding Source}" />
              </StackPanel>
          </DataTemplate>

      </ComboBox.Resources>

      <ComboBox.ItemsSource>
          <Binding Source="{StaticResource myFonts}" />
      </ComboBox.ItemsSource>
  </ComboBox>

You should be able to cut'n'paste this directly into your code. You would then bind the ComboBox's SelectedValue to a property of your choice. The SelectedValue is of type System.Windows.Media.FontFamily.

What is Going On?

There are several things going on that we need to describe. Beware! More verbose XAML!

A Sorted List of Fonts

Skipping directly to the ComboBox.Resources section: we get the full collection of system fonts. However, by default they only come partially sorted (by FamilyName), so we sort them into our own collection called myFonts. We do this by importing the ComponentModel namespace via this XAML markup:

xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"

and then create our own collections sorted by the Source property (which is the font family name):

<CollectionViewSource x:Key="myFonts" Source="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
      <CollectionViewSource.SortDescriptions>
          <ComponentModel:SortDescription PropertyName="Source" />
      </CollectionViewSource.SortDescriptions>
  </CollectionViewSource>

Data Template

We declare a simple template that renders the fonts in their own type face, and provides a tooltip, within the ComboBox.

Static Resources

Lastly we bind the ComboBox.ItemsSource to our sorted collection of fonts, myFonts using the long-hand XAML binding. Why do we do this last, and not directly as a ComboBox attribute?

The ItemsSource attribute requires that it is bound to a static resource. Suppose we do this:

<ComboBox
            xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
            ItemTemplate="{DynamicResource FontTemplate}">
            ItemsSource="{Binding Source={StaticResource myFonts}}">

We get an exception thrown:

"Cannot find resource named 'myFonts'. Resource names are case sensitive." as myFonts has not yet been declared."

We could of course move our font collection to the UserControl/Window/Application Resources section, however in this example we only have one font combo box, so it is nice to have it within the ComboBox.Resources section.

You might also try setting the ItemsSource to reference myFonts dynamically, via:

<ComboBox
            xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
            ItemTemplate="{DynamicResource FontTemplate}">
            ItemsSource="{Binding Source={DynamicResource myFonts}}">

This also fails with the exception:

"A 'DynamicResourceExtension' cannot be set on the 'Source' property of type 'Binding'. A 'DynamicResourceExtension' can only be set on a DependencyProperty of a DependencyObject."

So in answer to our question: as XAML has a 'one-pass compiler', a StaticResource has to be declared lexically before it is referenced: if we declare the binding last, then we can create our sorted list of fonts StaticResource, within ComboBox.Resources, and then bind to it within the XAML of the ComboBox, hence this piece of XAML:

<ComboBox.ItemsSource>
      <Binding Source="{StaticResource myFonts}" />
  </ComboBox.ItemsSource>

Using This XAML Snippet.

As mentioned above, if you intending on using this XAML (and using the font combobox multiple times), move the sorted font collection:

<CollectionViewSource x:Key="myFonts" Source="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
    ...
</CollectionViewSource>

into your Application/Window/UserControl Resources section, and put this attribute:

xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"

into the corresponding XAML document root.

A Final Word on Safe Font Usage.

Never, ever, choose Comic Sans. Ever.