image The other day my wife had volunteered to help out with the Halloween festivities at my son’s school. As part of this, she wanted to organize a game of bingo for the kids. I promised to help and went online to search for some halloween bingo cards that could be generated or printed out. Unfortunately my searches weren’t fruitful. I found ones with numbers or words, but nothing that allowed me to uses images – which I thought would make it more fun for the young kids.

I thought about using Excel to generate the cards or maybe Word but that didn’t look to be as straightforward as I ‘d hope it might be. Then I thought “What about writing a quick WFP app?”. I hadn’t tried printing from WPF first, so I knocked up a quick app to try that out. Once that proved straightforward I just had to write some simple XAML to layout the card, get some halloween clipart from office online and write a little code to load and randomize the images.

So here’s what I ended up with. Each time you run it the app you get a “random” card and every time you click “Print” a new card is generated so you can just keep printing until you have enough cards.

The kids loved it. We gave a few prizes for the kids that got a line first and then we let the kids keeping playing until everybody had filled their card. Note that I deliberately chose to include every image on every card, so everybody filled out their card and shouted “house” at the same time. Of course it would be quite easy to change this behaviour.

If you ever want to do something like this yourself, just launch Visual Studio and create a new WPF application. Here’s what my window1.xaml looked like.

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="UntitledProject4.Window1"
    x:Name="Window"
    Title="Bingo"
    Width="850" Height="900">

    <StackPanel x:Name="LayoutRoot">
        <Border x:Name="BingoCard" Margin="50,50,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" BorderBrush="#FF000000" BorderThickness="5,5,5,5">
            <ItemsControl ItemsSource="{Binding ElementName=Window, Path=Images}" >
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Border Width="142" Height="142" BorderBrush="#FF000000" BorderThickness="5,5,5,5">
                            <Image Width="Auto" Height="Auto" Source="{Binding Path=FileName}" Margin="10,10,10,10"/>
                        </Border>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Border>
        <Button HorizontalAlignment="Left" Margin="20,20,0,44" Width="97" Height="30" Content="Print" Click="OnPrint" IsDefault="True"/>
    </StackPanel>
</Window>

and here’s the corresponding C# file.

using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;

namespace UntitledProject4
{
    public class ImageData
    {
        public ImageData(string fileName)
        {
            this.FileName = fileName;
        }

        public string FileName { get; set; }    
    }

    public partial class Window1
    {
        /// <summary>
        /// The list of images that are databound to.
        /// </summary>
        private ObservableCollection<ImageData> m_images = new ObservableCollection<ImageData>();
        
        /// <summary>
        /// The print dialog. We store it as a member variable so any printer settings are preserved
        /// across multiple prints.
        /// </summary>
        private PrintDialog m_prtDlg = new PrintDialog();

        /// <summary>
        /// Initialize a instance of the Window1 class, that holds a bingo card.
        /// </summary>
        public Window1()
        {
            // Load the images - in this case I'm just loading them from c:\temp\halloween
            for (int i = 1; i <= 25; i++)
            {
                m_images.Add(new ImageData(@"C:\temp\halloween\" + i.ToString() + ".bmp"));
            }

            // Randomize them.
            this.RandomizeImages();

            this.InitializeComponent();
        }

        /// <summary>
        /// Gets the collection of images to use for the bingo card.
        /// </summary>
        public ObservableCollection<ImageData> Images
        {
            get
            {
                return m_images;
            }
        }

        /// <summary>
        /// Called to print the bingo card. The images are randomized after every print.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The event arguments</param>
        private void OnPrint(object sender, RoutedEventArgs e)
        {
            if (m_prtDlg.ShowDialog() == true)
            {
                m_prtDlg.PrintVisual(this.BingoCard, "Bingo");
            }

            RandomizeImages();
        }

        /// <summary>
        /// Randomly shuffle the images
        /// </summary>
        private void RandomizeImages()
        {
            Random random = new Random();
            int imagesCount = m_images.Count;
            for (int i = 0; i < imagesCount; i++)
            {
                int randomIndex = random.Next(0, imagesCount);
                ImageData imageData = m_images[randomIndex];
                m_images[randomIndex] = m_images[i];
                m_images[i] = imageData;
            }
        }
    }
}

 

Hopefully the code should be self explanatory.