Saturday 28 April 2012

Project X

Yet another new blog, this time it's a personal blog about...well everything and anything really. You can find it here: Project X Why not come by and say hello?

Friday 22 April 2011

In-Traction

My head has been hurting of late. As well as continuing to plug away at WPF, doing fun stuff like creating optimised vector graphics editors and custom controls, I am having to develop in XNA and Silverlight for Windows Phone 7, Objective-C and Cocoa for iPhone and somehow find room to at least keep abreast of Android and Java. Ouch!

It strikes me there are just too many devices, platforms and API's out there. Luckily I have some great people working with me and I can at least leave HTML5 and Android alone...for now.

In other news, DiceBox RPG has been doing really well. Its just a shame we can't make it's counterpart (DiceBox) free on the app store. If Microsoft would only get its act in gear and let its developers in the UK ad support their apps... Oh well, sure we wont have to wait too much longer.

Anyhow. I now find myself well and truly in the mobile arena and I'm not sure how much longer this blog has left in it. I guess you never know, I may well end up doing some GIS stuff again at some point but, outside of mobile, that is looking increasingly unlikely.

Having said that, a new and interesting blogging effort is underway. If you are at all interested in smartphones, tablets and the future of computing then why not pop in on the new site at www.in-traction.com

See you there.

Saturday 22 January 2011

An Expression Blend TabControl Style

After spending some time trawling the web and failing to find the style I was after, I decided to sit down and put one together myself.

A couple of hours later, and with a bit of help from Snoop, this is what I came up with; It’s a style for a tab control and, in-particular, a tab item, that goes some way to looking and feeling like the one used by Expression Blend.


The tabs are prevented from rearranging themselves into multiple lines by swapping out the TabPanel with a StackPanel.

To more closely resemble the style adopted by Blend the TabPanel could instead be replaced with the SqueezeTabPanel, which can be found on CodeProject. For the time being, this has been left as an exercise for the reader ;)

XAML:
 <Style TargetType="{x:Type TabControl}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel
Orientation="Horizontal"
Name="HeaderPanel"
Grid.Row="0"
Panel.ZIndex="1"
Margin="0,0,4,-1"
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"
Background="Transparent" />
<Border
Name="Border"
Grid.Row="1"
Background="LightGray"
CornerRadius="2"
KeyboardNavigation.TabNavigation="Local"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2" >
<ContentPresenter
Name="PART_SelectedContentHost"
Margin="4"
ContentSource="SelectedContent" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="#888888" />
<Setter TargetName="Border" Property="BorderBrush" Value="#AAAAAA" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type Button}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Path
x:Name="Cross"
Data="M0,0L6,6 M6,0L0,6z"
Stroke="LightGray"
StrokeThickness="1.7"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Cross" Property="Stroke" Value="White"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Cross" Property="Stroke" Value="LightGray"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid Margin="0,0,-12,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="24"/>
</Grid.ColumnDefinitions>
<Border
x:Name="Border"
Grid.Column="0"
Grid.ColumnSpan="2"
CornerRadius="3,0,0,0"
Background="#FF333333" />
<Path
x:Name="RoundedCorner"
Grid.Column="2"
Stretch="Fill"
Data="M6.5,2.6C4.767,0.973 2.509,0 0,0 0,0 0,19 0,19L23,19z"
Fill="#FF333333" />
<Button
x:Name="CloseButton"
Grid.Column="1"
Visibility="Collapsed"
Margin="0,0,0,0"
Width="6"
Height="6"/>
<ContentPresenter
x:Name="ContentSite"
TextElement.Foreground="White"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="12,2,12,2"
RecognizesAccessKey="True"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Panel.ZIndex" Value="100" />
<Setter TargetName="Border" Property="Background" Value="Gray" />
<Setter TargetName="RoundedCorner" Property="Fill" Value="Gray" />
<Setter TargetName="CloseButton" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="#EEEEEE" />
<Setter TargetName="RoundedCorner" Property="Fill" Value="#EEEEEE" />
<Setter Property="Foreground" Value="#888888" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Friday 31 December 2010

Bonehead

DiceBox seems to be doing pretty well, at least according to the stats coming in from AppHub.

So, as well as the DiceBox 1.1 update, work is underway for an RPG version that is already looking pretty awesome. Unfortunately, its still to early to release any details but a video preview should be available soon. To provide a home on the web for this exciting new endeavour, may I present to you...


The site should be slowly evolving over the following weeks and months, so be sure to keep an eye out.

Thursday 23 December 2010

And now for something completely different: DiceBox

This is bit of a shameless plug really...

Recently a couple of friends and myself have been playing around with the Windows Phone 7 developer tools.

The first fruits of this is DiceBox, a simple dice app, now available on the windows marketplace. If you don't have the Zune software installed you can see the blurb here

Currently there are 6 kinds of dice texture, realistic physics and sound, a total score display and a "shake to roll" feature.

We are planning a series of updates which will introduce a number of additional features, such as backgrounds, shadows and a dice tray for playing poker dice/yahtzee etc

If you have a Windows Phone 7, why not give it a try :)

Monday 8 February 2010

SpatiaLite data provider now in SharpMap trunk

It was nice to see that sussing out how to use those spatial indexes in SpatiaLite was not in vein. A post on bill dollins site prompted me to take a look at the recent changes to the SharpMap trunk, which now includes bills excellent SpatiaLite data provider complete with spatial index support.

Thursday 21 January 2010

WPF Header/Multi-Column ListBox Control



The WPF ListBox control can be used to create a wide variety of user interface elements thanks to templates and styling. However, recently it left me out in the cold...for a while at least.

We had an existing control, based on a heavily styled ListBox, that contained a bunch or items that could be considered as rows in a table. It all worked fine until some bright spark had the idea of adding column headers.

Now, at this point you may be thinking that ListBox is not the ideal candidate here. ListView, with its GridView layout, is the control to use if you want to layout column based data with column headers. Right?

Well, not always. GridView is fine if you want to apply styles to individual cells or columns. The problem with our particular style was that each conceptual row could be expanded in various ways, making the GridView unsuitable. There were two options as far as i could see:

  1. Write a custom View (based on ViewBase) for ListView
  2. Add a header to the ListBox control

We went with option two. The pain began.

The basic problem is that the header needs to be part of the ListBox so that when the ListBox is scrolled horizontally, the header scrolls too. However, when you scroll it Vertically you want the header to remain visible and not scroll with the rest of the list.

I started by looking at the style of the ListView control which does something similar to what i wanted to achieve. This gave me a big clue. The ListView uses a custom ScrollViewer that has a child scrollviewer nested inside. This child ScrollViewer contains the header; The scrollbars on the child ScrollViewer are always hidden. Magic.

I also had a look at this very helpful article: Building a Multi-Column ListBox in Avalon

This came close to what i wanted to achieve, but there were a number of problems:

  • It was written before the first release of WPF and would no longer compile
  • Having updated it so that it did compile, there were several problems with the scrolling resulting in unsatisfactory placement of the vertical scrollbar (not consistent with ListView) and errors causing the content to slide past the header at the extreme right.

Nevertheless, it gave me a lot of ideas, In-fact the example is based on a lot of the same XAML.

Next, I need to work out how to make the header scroll horizontally when the list box is scrolled horizontally. Unfortunately there is no easy way to do this. Ideally you would bind the HorizontalOffset of the header ScrollViewer to that of the main one. However, this is not possible thanks to its read only nature. To overcome this I used an attached behaviour to achieve the same goal by making use of the ScrollToHorizontalOffset method.

The resulting XAML for the ScrollViewer looks like this:

 <Style x:Key="HeaderedScrollViewer" TargetType="{x:Type ScrollViewer}"> 
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DockPanel Margin="{TemplateBinding Padding}">
<ScrollViewer DockPanel.Dock="Top"
local:SetHorizontalOffset.Offset="{Binding
RelativeSource={RelativeSource TemplatedParent},
Path=HorizontalOffset}"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
Focusable="false"
Content="{StaticResource ListHeader}">
</ScrollViewer>
<ScrollContentPresenter Name="PART_ScrollContentPresenter"
KeyboardNavigation.DirectionalNavigation="Local"/>
</DockPanel>
<ScrollBar Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Grid.Row="1"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Value="{TemplateBinding HorizontalOffset}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
<ScrollBar Name="PART_VerticalScrollBar"
Grid.Column="1"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Value="{TemplateBinding VerticalOffset}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

And it is applied to the ListBox with this style:

 <Style x:Key="{x:Type ListBox}" TargetType="{x:Type ListBox}"> 
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ScrollViewer Style="{StaticResource HeaderedScrollViewer}"
Grid.IsSharedSizeScope="True">
<StackPanel IsItemsHost="true"/>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

You can download the full source here: MultiColumnList.zip