Using the XKCD color survey for something useful

4. mai 2010

Update:

I just installed Moonlight Preview 7 in Ubuntu and the color picker almost works. :-) You can browse the colors and select some, but searching doesn't work properly.

 

XKCD just published the results of a survey where people would try to name a bunch of colors. The result, published here, is fun to read and for me it was really inspiring.

One of the data sets that were published was a list of popular color names along with their equivalent HTML color values. Since playing with colors is fun, I decided to take this list and turn it into .Net color constants. In .Net you have lots of defined color constants like Color.Blue and Color.Cornsilk, but even the creative ones are not as interesting as the colors Vomit (brown/green), Dark Periwinkle (a bluish color), Battleship Grey (grey like a battleship) or BabyShitBrown (don't ask). 

Interesting colors

The result is the XKCDColors class. I made two versions, one that returns System.Drawing.Color values and one that returns System.Windows.Media.Color values. The first is used with the System.Drawing namespace and WinForms applications, while the second is used for WPF and Silverlight applications.

To visualize the colors I created a tiny Silverlight 4 application that lists them all. Open it here.

And if you want to download the color files you can find them here:

XKCDColors.zip (20.47 kb)

 

Update:

After a suggestion from Randall Munroe himself I added a search box to the application. Now you can enter the hex notation for a color and get out the closest named color.

kick it on DotNetKicks.com

.NET, Silverlight, WPF , ,

Pixel perfect positioning of list elements in WPF

21. januar 2010

This is something I had to look up several times myself, so I thought I should write it down for later reference.

I was building a simple level editor for SilverTile (more about that in another post later) and wanted to place the map tiles in a grid-like fashion. This could be done using my dynamic Grid control, but I wanted pixel perfect control of where the tiles were placed on screen. Note the gaps between the grid rows in the following screen shot.

Fail!

Fail! Note the gaps between the tiles!

To do this I would need to use a Canvas control and place each tile using the Canvas.Left and Canvas.Top properties to set the position.

Win! This is what we want. Pixel perfect placement of our images!

The challenge is that the Canvas control doesn’t support list binding directly, so you must use some other control to accomplish this. Luckily there is a solution available by using the ItemsControl class. The ItemsControl is a list control that lets you specify which control should be the root element. In our case we will use a Canvas control as the root and then use data binding to specify the values for the Canvas.Left and Canvas.Top properties.

Using the ItemsControl

To exemplify the usage of the ItemsControl I first created a simple view model for the application. This was simply a list of TileData objects where each describes one tile. Each TileData object has an image source and Left and Top properties. The Left and Top properties tell where the tile should be placed on screen. In a real application your view model would probably be more complex and possibly calculate the positions based on some other data.

// The TileData class describes a tile
public class TileData
{
    public Uri Image { get; set; }
    public double Left { get; set; }
    public double Top { get; set; }
}

Now, with the view model in place we can setup our application.

<Grid Name="Root" Loaded="Root_Loaded">
    <Grid.Resources>
        <Style x:Key="ContainerStyle">
            <Setter Property="Canvas.Left" Value="{Binding Left}" />
            <Setter Property="Canvas.Top" Value="{Binding Top}" />
        </Style>
    </Grid.Resources>
    <ItemsControl Name="map" ItemsSource="{Binding}" ItemContainerStyle="{StaticResource ContainerStyle}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas Width="{Binding Path=Root.ActualWidth}" Height="{Binding Path=Root.ActualHeight}" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Image Source="{Binding Image}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

Here we have a Grid as the root element of the application. I’ve used a grid since it will be sized dynamically when the window is resized, something the Canvas control doesn’t. We then have the ItemsControl that has its ItemsSource bound to our data context. The ItemsSource is the list of TileData objects that is used to describe the map.

And now we get to the interesting part: The ItemsControl.ItemsPanel property. This property defines which control should be the container into which all our list items are placed. By setting this to a Canvas control we end up with a layout where you can set your child controls position as you want.

Further we have the ItemsControl.ItemsTemplate that tells how you want each object in your ItemsSource to be rendered. In our case we render it as an Image control with its source bound to the Image property of your TileData object.

The final missing part is how we place our controls. The obvious way to do this would be to set the Canvas.Left and Canvas.Top properties of our Image control by binding them to the Left and Top properties of our TileData object. One would expect this to work but if you try it you will see that all our controls are placed at the top left corner of the canvas, with Canvas.Left = 0 and Canvas.Top = 0. The solution is to create a style that sets the Canvas.Left and Canvas.Top properties and to assign that to the ItemContainerStyle property of the ItemsContainer. By doing this you will see that our Image controls are placed correctly where we want them.

Using Snoop to find out what really happened here

To understand what really happened here we need to examine the control hierarchy our Xaml code generates. A good tool to do this is Snoop. It lets you attach to a running Wpf application to examine all properties of all generated controls.

Using Snoop to look at our applications control hierarchy

Look at the above screen shot. Here you can see the control hierarchy of the GridWin application. If you look down the tree you will find an ItemsControl control with a child Border control. The Border contains an ItemsPresenter Control that contains a Canvas. This Canvas control is the one we assigned to our ItemsControl.ItemsPanel property.

The Canvas contains a bunch of ContentPresenter controls that each contains one Image control. And this is the reason why we cannot set the Canvas.Left and Canvas.Top properties of our Image controls directly. Since the images are contained in the ContentPresenter controls we need to set the properties here instead. And this is what we do when we set the ItemContainerStyle property. The style is applied to the ContentPresenter which is placed at the correct position.

Using the code

I am using this approach in a level editor application for SilverTile that I will be writing about soon. The application stores the map as a list of Tile objects where each Tile knows which row and column they should be placed in. The Canvas.Left and Top properties are then bound to the X and Y properties that are caluclated by multiplying the row or column number with the tiles width or height in pixels. The tile list is used as the ItemsSource for an ItemsControl that renders the tiles at the wanted position.

The ItemsControl is available in Silverlight as well as in WPF, so this approach works just as well there. The only challenge is that the binding of the Canvas' size to the size of the Root grid cannot be done in Silverlight 3 since it doesn't support element to element binding. This is fixed in Silverlight 4 though, so there you should be able to use the exact same approach.

kick it on DotNetKicks.com

.NET, Silverlight, WPF , ,

A simpler (and dynamic) Grid control for WPF

6. november 2009

The last days I have been playing with WPF trying to get an understanding of how it works ”under the hood”. So I have written a bunch of tiny applications where I prototype a function. When building these applications I don’t care much about its user interface, but I still like to have my controls lined up neatly. To do this I usually use a grid control with some rows and columns. This usually works well, but the grids syntax gets tiresome to write and rewrite all the time.

Yesterday this annoyed me so much that I decided to fix it! The reason was not only the syntax, but also that I needed a grid that let me set the number of rows and columns dynamically using data binding. The result is the DynamicGrid control. It allows you to replace this code:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
        <ColumnDefinition/>
        <ColumnDefinition/>
        <ColumnDefinition/>
        <ColumnDefinition/>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
</Grid>

With this code:

<grid:DynamicGrid NumColumns="8" NumRows="8">       
</grid:DynamicGrid>

Nice! And it also allows you to use data binding to setup the number of rows or columns. Like this:

<grid:DynamicGrid NumColumns="{Binding ColumnCount}" NumRows="{Binding RowCount}">       
</grid:DynamicGrid>

So your grid can adapt to your data source. On top of all that, it even plays nice with the old grid-markup. So if you need a header row with a fixed size then you can insert it using a RowDefinition and let the DynamicGrid add the rest of the rows and columns for you automatically.

How does it work?

It turned out that implementing the grid was really easy. All I needed to do was to subclass the Grid class, add two new dependency properties and add the code that added the columns and rows automatically.

I created two new dependency properties named NumColumns and NumRows. They are both identical except for the naming:

public static readonly DependencyProperty NumColumnsProperty =
    DependencyProperty.Register("NumColumns", typeof(Int32), typeof(DynamicGrid));

public Int32 NumColumns
{
    get { return (Int32)GetValue(NumColumnsProperty); }
    set { SetValue(NumColumnsProperty, value); }
}

There is not much to note about them – we default to one column and one row.

I then wrote an override for the OnInitialized method:

protected override void OnInitialized(EventArgs e)
{
    base.OnInitialized(e);

    RecreateGridCells();
}

This will call the RecreateGridCells method that does the actual setup of the dynamic grid. Since it is called after the base grid is initialized we support its functionality.

The RecreateGridCells method does the custom setup:

private void RecreateGridCells()
{
    int numRows = NumRows;
    int currentNumRows = RowDefinitions.Count;

    while (numRows > currentNumRows)
    {
        RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
        currentNumRows++;
    }

    while (numRows < currentNumRows)
    {
        currentNumRows--;
        RowDefinitions.RemoveAt(currentNumRows);
    }

    int numCols = NumColumns;
    int currentNumCols = ColumnDefinitions.Count;

    while (numCols > currentNumCols)
    {
        ColumnDefinitions.Add(new ColumnDefinition{ Width = new GridLength(1, GridUnitType.Star) });
        currentNumCols++;
    }

    while (numCols < currentNumCols)
    {
        currentNumCols--;
        ColumnDefinitions.RemoveAt(currentNumCols);
    }

}

Here we read out the number of rows we want and the number of rows we have. If we need more rows we add them to the RowDefinitions collection and set their heights to *. The * means that they should scale automatically. If we have too many rows we remove them starting with the last one.

After setting up the rows we setup the columns the same way.

That is all there is to it!

So this code:

<Window x:Class="DynamicGridDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:grid="clr-namespace:DynamicGridDemo"
        Title="MainWindow" Height="350" Width="525">
    <grid:DynamicGrid NumColumns="2" NumRows="2">
        <Button Grid.Column="0" Grid.Row="0" Content="At 0,0" />
        <Button Grid.Column="0" Grid.Row="1" Content="At 0,1" />
        <Button Grid.Column="1" Grid.Row="0" Content="At 1,0" />
        <Button Grid.Column="1" Grid.Row="1" Content="At 1,1" />
    </grid:DynamicGrid>
</Window>

Gives you this result:

Let’s add a header row:

<Window x:Class="DynamicGridDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:grid="clr-namespace:DynamicGridDemo"
        Title="MainWindow" Height="350" Width="525">
    <grid:DynamicGrid NumColumns="2" NumRows="3">
        <Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Content="Header label here at 0,0 and 1,0" FontSize="20" Background="Yellow" />
        <Button Grid.Column="0" Grid.Row="1" Content="At 0,1" />
        <Button Grid.Column="0" Grid.Row="2" Content="At 0,2" />
        <Button Grid.Column="1" Grid.Row="1" Content="At 1,1" />
        <Button Grid.Column="1" Grid.Row="2" Content="At 1,2" />
    </grid:DynamicGrid>
</Window>

Gives this result:

And it even plays nice together with the old syntax. This is useful if you need a header row with a fixed size for example:

<Window x:Class="DynamicGridDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:grid="clr-namespace:DynamicGridDemo"
        Title="MainWindow" Height="350" Width="525">
    <grid:DynamicGrid NumColumns="2" NumRows="3">
        <Grid.RowDefinitions>
            <RowDefinition Height="40" />
        </Grid.RowDefinitions>
        <Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Content="Header label here at 0,0 and 1,0" FontSize="20" Background="Yellow" />
        <Button Grid.Column="0" Grid.Row="1" Content="At 0,1" />
        <Button Grid.Column="0" Grid.Row="2" Content="At 0,2" />
        <Button Grid.Column="1" Grid.Row="1" Content="At 1,1" />
        <Button Grid.Column="1" Grid.Row="2" Content="At 1,2" />
    </grid:DynamicGrid>
</Window>

This gives the following output (note the size of the first row):

I haven't done much testing of the code, so it may include some nasty bugs, but I think it may be useful. Either if you are prototyping an application or if you need a grid that sizes to your content.

Download the source code here: DynamicGridDemo.zip (77.44 kb) (Visual Studio 2010 b2, WPF4 required)

kick it on DotNetKicks.com

.NET, WPF ,