|
|||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionAfter that strange title, I'd better kick off with an explanation of what this article is all about. One of my favourite 'pet' topics is data visualisation and how graphically displaying data you can often see trends, patterns & anomalies and often improve the 'user experience (UX)'. This article is going to be in three main parts; firstly I'll discuss what Treemaps are, how they can help you interpret data and how they are built. But briefly, they provide a graphical representation of information enabling users to compare items by size and changing attributes, often used to view hierarchical data. Secondly, digressing slightly off topic, I will discuss why I hope & think Longhorn and XAML will bring about important changes to desktop computing. Then back to treemaps to show how to create the Treemap application in C# and XAML using the alpha builds of Microsoft Longhorn (codename for the next release of Microsoft Windows) and Whidbey (which will be released as Visual Studio 2005 next year) from the October 2003 Microsoft Professional Developers Conference (PDC). Because I appreciate most people won't have the PDC bits installed, I have also put together a little video demo for people to download in the style of the Longhorn Concept Videos. Hopefully Microsoft and Carter Maslan will appreciate me wanting to evangelise and not be upset by the similar style. Note, the video is in no way produced nor endorsed by Microsoft and the codenames used in this article belong to their respective owners. Finally, I'll be taking a look at some other visualisation/interactive techniques I've seen lately that might be quite interesting to you; including commercial packages and ideas for viewing information. BackgroundI came across the graphical technique called Treemaps about four years ago on the SmartMoney website. The technique is used there as a way of comparing financial stock information and is called 'Map of the Markets'. It impressed me with how easily it allowed users' to compare the size and the change of different attributes for multiple sectors and companies.
Wanting to understand how they calculated and laid out the different areas, I researched deeper and found academic papers on Human Computer Interaction (HCI), in particular Treemaps. There I came across the work done by Ben Shneiderman of the University of Maryland. From there specifically the paper "Squarified Treemaps", by Mark Bruls, Kees Huizing, and Jarke J. van Wijk of the Eindhoven University of Technology, Dept. of Mathematics and Computer Science, which is the layout technique I'll be using here. There are many examples of people using treemaps, including Microsoft Research on Netscan to analyse Usenet posting, showing which groups have the most posts and the change from last month. You can download their Treemap.NET SDK. The HiveGroup have a product using treemaps for business intelligence data mining. Squarified TreemapsThese are a type of treemap, but the layout algorithm aims to draw the data with the "best" aspect ratio, ie. trying to keep the area's shape "square" by aiming for a low aspect ratio approaching the value 1. So let's start with a simple example, imagine we have five different companies. We have data on millions £ of sales and the % change from last year. We could show the data as a table:
Or graphically as a chart:
This allows us to see easily that Company B (the largest sales) had a negative percentage change on sales from last year. Company F is about half the size of Company E and the smallest Sales was Company G but Company H had highest change % from last year. Drawing that information as a Squarified Treemap gives us a diagram like this:
To explain what we are looking at, we have drawn all the companies in a grid with the size determined by the Sales figure; the colour gives the % change. Hopefully you will agree that it is easy to get a good idea of the companies from a quick glance, and compare the size of sales. Also the bright green colour on Company H makes it stand out as a positive % change, and that Company C, D, J and G are similar in sales size.
The technique isn't really that impressive with a small data set, but with more companies you can glance and pick out anomalies really quickly. If we look at a couple of screenshots from the Smartmoney Sector Map we can see negative changes in "Technology Hardware & Equipment" and "Communications Technology" below very easily.
Drilling further into the Software sector on Smartmoney, we can see the dominance (in market capitalisation) of Microsoft.
Did you really know Microsoft was that much bigger (in market capitalisation) than Oracle? How the layout works?Now we have seen how the technique shows data, how do we decide on the actual layout of the data? In the paper "Squarified Treemaps" as listed above, it discusses using a layout technique to try and obtain the best aspect ratio; as mentioned before, that means trying to layout the elements to get the ratio to approach the value 1. I'm going to use their example again, say we have an area of 6 x 4 and we have seven items of data with sizes of 6, 6, 4, 3, 2, 2 and 1 respectively. We could split the area vertically:
Or horizontally:
Neither really looks very good, the idea put forward in the Squarified Treemap paper is to try placing the items down differently aiming for the optimum aspect ratio.
Step 1 : Start by deciding that because the area we want to fill is wider than the height we'll draw the first element vertically. bool DrawVertically (single width, single height) {
return width > height;
}
We calculate the aspect ratio as: single aspect = Math.Max( width / height, height / width);
Step 2 : Continue drawing vertically as we didn't have a previous aspect ratio to better. Now we want to draw the first two elements of size 6 and 6.
Step 3 : Again continue drawing vertically as the aspect ratio improved, ie. getting closer to 1. Drawing the first three elements in the list of size 6, 6 and 4.
Step 4 : Because the aspect ratio was worse (higher than value from step 2 and moving away from the value 1), we backtrack to step 2 and lock that layout in place. Now we are trying to layout the remaining elements into the area left, a 3 by 4 space. As we are starting again, check if we are going to draw vertically, the answer is no because the area is taller than the width. Let's try to place the element of size 4 again.
Step 5 : Continue on, drawing horizontally, elements of size 4 and 3.
Step 6 : Continue on drawing horizontally, elements of size of 4, 3 and 2.
Step 7 : Because the aspect ratio was worse (higher than value from step 5 and moving away from the value 1), we backtrack to step 5 and lock that layout in place. Now we are trying to layout the remaining elements into the area left, a 3 by 1.67 space. As we are starting again, check if we are going to draw vertically, the answer is yes this time, because the area is wider than the height. Let's try to place the element of size 2 again.
Step 8 : Continue on drawing vertically, elements of size 2 and 2.
Step 9 : Because the aspect ratio was worse (higher than value from step 7 and moving away from the value 1), we backtrack to step 7 and lock that layout in place. Now we are trying to layout the remaining elements into the area left, a 1.8 by 1.67 space. As we are starting again, check if we are going to draw vertically, the answer is yes again, because the area is wider than the height. Let's try to place the element of size 2 again.
Step 10 : Continue drawing vertically, elements of size 2 and 1.
Obviously this example is made easier to understand because the total size of the elements (6+6+4+3+2+2+1 = 24) which is also the total area (6 by 4 = 24). But a different number and total size of elements could have been placed it would have just involved scaling the sizes appropriately. I hope people have been able to follow that explanation and you can see how recursively we try different layouts, with the end result a much more "square" and better laid out set of elements descending in size. We'll come back to the actual code and colouring of the change parameter a bit later in this article.
Please feel free to skip this next discussion about Longhorn if you are only interested in the squarified treemap code. Why could Longhorn bring huge changes?The next release of Microsoft Windows after Windows XP is codenamed Longhorn. A lot of information has been released by Microsoft already, including talking about three main pillars, namely Avalon, WinFS, Indigo and the overall fundamentals; collectively known as WinFX. I'll let you read the Microsoft Developer Network (MSDN) articles on the Longhorn Developer Center to learn more. The book Introducing "Longhorn" for Developers by Brent Rector is a good roadmap guide and is all available to read on-line now as well.
Looking at the graphics subsystem, in particular "Avalon" which is described as "providing the foundation for building applications and high fidelity experiences in Longhorn, blending together application UI, documents, and media content, while exploiting the full power of your computer ". Built on top of this is "Aero" is "the new Windows user experience. Aero consists of guidelines, recommendations, and user experience values that help your applications get the most out of the Microsoft Windows ". You don't need to fully understand these terms but it will probably make things a bit clearer. I'm only really going to be concerned with Avalon and touch on Aero in this article, because they are at the heart of the Longhorn UX. A new way of way of defining UI layout in Longhorn is XAML (pronounced 'Zamal'). This new markup language allows you to declaratively to specify a hierarchy of objects with a set of properties and logic. Normally this will be a user interface (UI) for an application and this is what I'm going to use for the Treemap application discussed below. Why Avalon could be so importantOk enough, what are these new user interfaces going to be like. Well David Massy, wrote a blog entry that I'd like to quote from:
As this screenshot from the Winsupersite showcase shows, imagine Control Panel like this where the elements slide around like the timeline in Picasa mentioned below. Ok maybe not in a corporate environment but it would wow home users and make them feel like it really is the 21st century. As long as there is a balance between functionality and aesthetics I think it will be a step forward.
You only have to look at some of the consumer UIs for the XBox or Windows XP Media Center Edition, in particular the intro video, to see that it really can impress and be functional.
Take a look at Picasa and the timeline view. It shows your pictures on a scrolling timescales with smooth animation.
Many people really like the Apple OS X interface and I would agree that the machines look really nice and the effects do impress passers by. You can try something similar on Windows XP via AquaDock. But for me, I wanted the taskbar back pretty quickly; read more on OS X vs Windows XP.
At Microsoft Research, they are looking at improving the UI interface continually, the recent article, "Wave Goodbye to the Ordinary User Interface" talks about being "inspired by how computers work in movies. In movies, you never have to login, you never have to type a file name, you never have to remember the URL, or know what a browser is. There's none of these artificial distinctions. Things just happen." "In GWindows we use two webcams above the monitor. The machine senses when there is something in the space in front of the webcams. It puts up a little hand on the computer display. You can grab onto a window, move it, and let it go. Or you can say 'scroll' and it maps the up and down scrolling motion. So you can combine speech and gesture". This sort of stuff has been in the research labs for years, will it ever hit the mainstream? Will we start to see displays and interactions like in the film Minority Report? User eXperience (UX)As we mentioned, adding a gradient and a fade effect doesn't make a great application, and I think a number of people are concerned that by allowing developers to easily embed video, graphics, rotated animating text and buttons the UI could become an absolute mess. No offence meant to Chris Anderson and Don Box's Lap around Longhorn example! But this is possible today, just think back to the <marquee> <blink> tags in HTML pages and a hundred and one different fonts in a Word document. It has to be used in a useful and appropriate way. Just as with Java UI you have the "Java Look and Feel Design Guidelines" and on Windows the "Official Guidelines for User Interface Developers and Designer" the fundamentals of "User-Centered Design Principles".
The aim being a new set of books code-named "The Windows User Experience Cookbooks" which will include guidelines and examples of good Aero user experience design. These books will show you how to: Design products that look and act like a new Longhorn application.
The link to sampler above compiled for PDC 2003 contains preliminary versions of chapters, discussing the: It describes different groups of applications, called "Application Archetypes", showing how the UI can share some consistently across similar groups. For example an on-line travel website, could combine the web-like presentation with rich listview-type controls.
Where it talks about the Windows XP icon style of fun, colour, and energy — and, as there are now 32-bit colour versions of the icons, smooth edges. With each icon being rendered in a vector program and then manipulated in Adobe Photoshop to create a beautiful image. These guidelines are geared towards designers. So they recommend working with a good graphic designer, especially one with experience in using vector or 3D programs.
So what is XAML?As we have mentioned it can be seen as a declarative markup language for Windows, what makes it special is:
This will enable interoperation between UI authoring tools and developer tools and help:
In XAML: <Button Width="100px"
Background="LightBlue">OK
</Button>
In C#: Button b1 = new Button();
b1.Content = "OK"
b1.Background = new SolidColorBrush(Colors.LightBlue);
b1.Width = new Length(100);
Would give:
There are other markup languages for describing UI and even contests comparing them. One I think will be interesting to watch is Macromedia Flex but it seems like the pricing for the server may be too high. For a more general introduction to XAML, read the MSDN Article, "A First Look at Writing and Deploying Apps in the Next Generation of Windows". From the Bitmap world to scalable VectorsWith Avalon most of the UI for the Windows Shell will be drawn as vector graphics. This means they can be scaled without ending up looking blocky, as would happen if done with bitmapped images; much in the same way Adobe Type Manager/TrueType made fonts scalable in Windows. Paul Thurrott gives a good example in his review where he shows the differences below. Bitmap image scaled:
This isn't really a vector image but an icon from OS X, but I hope it does give the idea of how a better scaled image should work at different resolutions:
Chris Sells, in his article "A Journey of a Thousand Miles" talks about vector based images for the card images in the new Solitaire game he is writing as part of learning Longhorn. Again, Introducing the New Avalon Graphics Model by Ian Griffiths talks more about the XAML and vector paths. So are Winforms a dead technology?If XAML is the future, should people invest time learning about Winforms today? Well really the question is, given that Longhorn is looking like 2006 at the earliest can you afford not to release an application for anyone to use till then? The answer is probably no, but as David Massy from Microsoft points out in Why wait for Avalon?:
A similar posting by Robert Scoble, WinForms is dead misconceptions are misplaced, had a lot of people discussing the situation.
Back to the Coding Part!Pre-requisites - Installing the PDC bitsI'm afraid that most people won't have the PDC bits, but if you or your company has an MSDN Universal subscription you can download the alpha 4051 Longhorn build. Also guessing if people went to the PDC then they will have installed and played around with the bits over the last few months. But maybe like me, before writing this article, you have a full-time job to do and only get to try things out at home as and when you can so you haven't had much exposure. It is all good reading articles and having an idea of how technologies work, but you really don't begin to learn until you really try and write an application; much like learning to drive after passing your driving test or how I felt moving from COM world to .NET a few years ago. You can see some screenshots of the Longhorn PDC 4051 build at Winsupersite. ApproachMy approach was to work on the C# implementation of the Treemap calculation code separately, get that working and then look at how to combine and generate the XAML code. By taking small steps, I could more easily debug and change coding direction as I discovered more, especially which XAML layout panels to use where. I didn't have a spare PC to install and test the alpha bits, so instead I used VMware Workstation 4.0.5 to create a virtual PC using the 30 day evaluation version available. I'm sure it would work fine under Microsoft Virtual PC 2004 if you have that instead. Specification wise you should really give the virtual session 512Mb of RAM and 8Gb of disk space if you intend to install all the development tools, software development kit (SDK) and documentation. Make sure you follow the release notes, because the order of installation on Longhorn is very important. Also download and install the XAML IntelliSense Patch for PDC Visual Studio .NET "Whidbey" to improve the IntelliSense editing of XAML files. Designing the layout XAML codeWorking out how to draw the rectangles and layout in XAML was a process of trial and error. I had a reasonable idea of the different types of layout shapes and panels, but exactly which elements would work I wasn't sure, so had to just play around with them.
<Rectangle Fill="red" StrokeThickness="2" Stroke="darkgray"
Height="100" Width="100" />
For absolute positioning, could change to: <Rectangle Canvas.Top="200" Canvas.Left="200" Fill="red" />
Looking good so far, next the mouseover animation – which I wanted the gray border to change to white. So reading Create a Rollover Effect Using Events and Animating Property Triggers I started in that direction. Which would mean I could declaratively define the change centrally in a style, in MyApp.xaml: <Style def:Name="treemapRect">
<Rectangle StrokeThickness="2" Stroke="darkgray" />
<Style.VisualTriggers>
<PropertyTrigger Property="IsMouseOver" Value="True">
<Set PropertyPath="Stroke" Value="white" />
<Set PropertyPath="Cursor" Value="Hand" />
</PropertyTrigger>
</Style.VisualTriggers>
</Style>
Note, need to be careful editing XAML styles in Visual Studio, as it often incorrectly closes tags where it shouldn't, just keep an eye out if you are typing away. The way to assign a style to an object is: <Rectangle Style="{treemapRect}">
Which seemed a bit strange to me, there are a lot of similarities to HTML/CSS but then a lot of differences too. We'll see how this pans out in the future. Unfortunately, this didn't work as expected, although it might be my misunderstanding. The Stroke colour change was ignored, even though the Cursor changed. However setting the value in actual C# code from the MouseEnter and MouseLeave events did update the border colour. <Rectangle MouseEnter="HighlightRect" MouseLeave="HighlightRect">
private void HighlightRect (object sender,
MSAvalon.Windows.Input.MouseEventArgs e)
{
MSAvalon.Windows.Shapes.Rectangle r =
(MSAvalon.Windows.Shapes.Rectangle)sender;
MSAvalon.Windows.Media.SolidColorBrush brush =
(MSAvalon.Windows.Media.SolidColorBrush) r.Stroke;
if ( brush.Color == Colors.White )
r.Stroke = new SolidColorBrush(Colors.DarkGray);
else
r.Stroke = new SolidColorBrush(Colors.White);
}
I did try to add the MouseEnter and MouseLeave events to the style, but got the error "Events not support in style declarations". I didn't look any further into this, but guessing will be possible to add CSS behaviour type code maybe using delegates? Note, because the XAML and C# code are partial classes, you no longer have to declare markup objects in C# as you currently do with ASP.NET. This is true for ASP.NET in the Whidbey release; a big improvement over ASP.NET 1.x.
Because I wanted text inside the rectangle, I thought I'd try this: <Rectangle Fill="red" Width="100" Height="100">
<Text>Hello there</Text>
</Rectangle>
But this didn't work, the text wasn't visible and the old Windows idea of z-order didn't seem to the right place to look. So I tried: <Rectangle Fill="red" Stroke="darkgray" StrokeThickness="2"
Cursor="Hand" Width="100" Height="100"
Canvas.Left="0" Canvas.Top="0" MouseEnter="HighlightRect"
MouseLeave="HighlightRect" />
<Text Width="100" Height="100" Canvas.Left="0"
Canvas.Top="0">Hello there</Text>
Which meant coding the Left and Top positions twice, but it did draw the text on top of the Rectangle. Unfortunately now the MouseEnter event didn't fire as the Text control was covering the Rectangle!
Note the change from Stroke and StrokeThickness to BorderBrush and BorderThickness, also why Fill and Background on different controls? Also ID not following the .NET framework guidelines of calling it Id? Sure these things will pan out in time before it is released properly. <Border Canvas.Left="100" Canvas.Top="0" Width="100" Height="100"
BorderBrush="darkgray" BorderThickness="2" Background="red" Cursor="Hand">
<Text Foreground="white" TextWrap="Wrap" TextTrimming="CharacterEllipsis"
VerticalAlignment="Center" HorizontalAlignment="Center"
Width="98%" Height="98%" MouseEnter="HightLightText"
MouseLeave="HighLightText">My text inside the Border</Text>
</Border>
Looking good, think we have the basis for drawing the Treemap rectangles. Because we will be creating them in code I'll need to translate the XAML to C#. If you debug the application and have a look at the obj/Debug/Windows1.g.cs which is the generated C# from the XAML markup you can see what is actually compiled. It is yuck, but it does give an idea of what is actually called.
Armed with that, the C# code would be: private void WindowsLoaded (object sender, EventArgs e)
{
MSAvalon.Windows.Controls.Border b = new MSAvalon.Windows.Controls.Border();
MSAvalon.Windows.Controls.Text t = new MSAvalon.Windows.Controls.Text();
// Could use overload on Length to assume pixels
Canvas.SetLeft (b, new Length(100, UnitType.Pixel));
Canvas.SetTop (b, new Length(100, UnitType.Pixel));
b.Width = new Length(100, UnitType.Pixel);
b.Height = new Length(100, UnitType.Pixel);
b.BorderBrush = new SolidColorBrush(Colors.DarkGray);
b.BorderThickness = new Thickness(new Length(2, UnitType.Pixel));
b.Background = new SolidColorBrush(Colors.Red);
b.Cursor = MSAvalon.Windows.Input.Cursor.Hand();
t.Foreground = new SolidColorBrush(Colors.White);
t.TextWrap = TextWrap.Wrap;
t.TextTrimming = MSAvalon.Windows.TextTrimming.CharacterEllipsis;
t.VerticalAlignment = MSAvalon.Windows.Media.VerticalAlignment.Center;
t.HorizontalAlignment = MSAvalon.Windows.HorizontalAlignment.Center;
t.Width = new Length(98, UnitType.Percent);
t.Height = new Length(98, UnitType.Percent);
((MSAvalon.Windows.Serialization.IAddChild)(t)).AddText("My text
inside the Border");
t.MouseEnter += new MSAvalon.Windows.Input.MouseEventHandler
(this.Highlight);
t.MouseLeave += new MSAvalon.Windows.Input.MouseEventHandler
(this.Highlight);
((MSAvalon.Windows.Serialization.IAddChild)(b)).AddChild(t);
oCanvas.Children.Add(b);
}
Give me declarative markup any day of the week, and why is it VerticalAlignment in MSAvalon.Windows.Media but HorizontalAlignment in MSAvalon.Windows namespace? Also I would have liked to look at using some of the advanced databinding control features in XAML. When writing the code and playing with XAML I did keep asking myself, is it "better" because it's new and fun or is it really an improvement? Guess one way to look at it is how difficult would a automatically reflowing document panel with a floating rounded gradient filled vector scalable transparent rectangle be to draw under GDI+ code? I'll leave you reading Charles Petzold books for a while then…
<Canvas ID="oInfoCanvas" Width="160" Height="58" Opacity="0">
<Rectangle ID="oInfoRect" RadiusX="4" RadiusY="4" Height="100%"
Width="100%" Stroke="#1978B1" StrokeThickness="2"
Fill="VerticalGradient #FFC6EBFF #DDFFFFFF" />
<TextPanel Canvas.Left="10" Canvas.Top="6" FontSize="10"
FontWeight="Bold"ID="oInfoName"></TextPanel>
<TextPanel Canvas.Left="10" Canvas.Top="24" FontSize="10"
ID="oInfoSize"></TextPanel>
<TextPanel Canvas.Left="10" Canvas.Top="36" FontSize="10"
ID="oInfoChange"></TextPanel>
</Canvas>
Squarified Treemap Calculation CodeThe actual code for calculating the sizes and colour of each element isn't really to complex. It is just like following the steps in the example above. I used DataSets for passing the actual data to draw to the Treemap class and for the return calculated values for the items, ie. size, position and colour. public class Treemap
{
private int width;
private int height;
private double changeLimit = 4; // Should really calculate this
// automatically from the given dataset
...
public Treemap()
{
// Constructor
returnDs = new DataSet();
DataTable dt = new DataTable();
dt.Columns.Add("id", typeof(string));
dt.Columns.Add("name", typeof(string));
dt.Columns.Add("size", typeof(Single));
dt.Columns.Add("change", typeof(Single));
dt.Columns.Add("x", typeof(Single));
dt.Columns.Add("y", typeof(Single));
...
returnDs.Tables.Add(dt);
}
public DataSet GetData()
{
// Remove temporary calculation columns
returnDs.Tables[0].Columns.Remove("sizeTmp");
returnDs.Tables[0].Columns.Remove("changeTmp");
returnDs.Tables[0].Columns.Remove("widthTmp");
returnDs.Tables[0].Columns.Remove("heightTmp");
return returnDs;
}
The CalcMap function loops around the data and then calculates the scaling and change colours for each item. public void CalcMap()
{
CopyValues(); // Copy values from original DataSet to return DataSet
tmp_dWidth = width;
tmp_dHeight = height;
double valueScale = 0;
double changeTotal = 0;
double dataTotal = 0;
DataTable dt = returnDs.Tables[0];
for (int n=0; n < dt.Rows.Count; n++)
{
dataTotal += (Single)dt.Rows[n]["sizeTmp"];
}
// Scale and set colour change value
valueScale = ((width * height) / dataTotal) / 100;
for (int n=0; n < dt.Rows.Count; n++)
{
changeTotal += (Single)dt.Rows[n]["change"];
dt.Rows[n]["sizeTmp"] = valueScale * (Single)dt.Rows[n]["sizeTmp"];
dt.Rows[n]["changeTmp"] = (255 / changeLimit) *
(Single)dt.Rows[n]["changeTmp"];
// Reset if we are over colour range
if ((Single)dt.Rows[n]["changeTmp"] > 255)
dt.Rows[n]["changeTmp"] = 255;
else if ((Single)dt.Rows[n]["changeTmp"] < -255)
dt.Rows[n]["changeTmp"] = -255;
// Set the green or red change colour value
if ((Single)dt.Rows[n]["changeTmp"] >= 0)
{
dt.Rows[n]["r"] = 0;
dt.Rows[n]["g"] = (int)(Single)dt.Rows[n]["changeTmp"];
dt.Rows[n]["b"] = 0;
dt.Rows[n]["htmlColour"] = ColorTranslator.ToHtml(
Color.FromArgb(0,
(int)(Single)dt.Rows[n]["changeTmp"], 0));
}
else
{
dt.Rows[n]["r"] = (int)Math.Abs((Single)dt.Rows[n]["changeTmp"]);
dt.Rows[n]["g"] = 0;
dt.Rows[n]["b"] = 0;
dt.Rows[n]["htmlColour"] = ColorTranslator.ToHtml(
Color.FromArgb(
(int)Math.Abs((Single)dt.Rows[n]["changeTmp"]),
0, 0));
}
}
This next piece of code should but recursive but I use a simple goto statement as a bit of a hack instead. Decide if we are going to draw vertically or horizontally based on whether the area to draw has a width greater than the height. Then try and draw the current elements defined by start to end range. The Try function attempts to place the items and returns the aspect ratio for the last placed item. If this aspect ratio is worse than the previous ratio, then we Lock the previous layout positions, set the new remaining area to layout in and start over again. int start = 0;
int end = 0;
bool vert;
Single aspectCurr = 999;
Single aspectLast;
vert = DrawVert(tmp_dWidth, tmp_dHeight);
startover:
while (end != dt.Rows.Count)
{
aspectLast = Try(start, end, vert);
// Is aspect ratio worse than previous ratio?
if ((aspectLast > aspectCurr) || (aspectLast < 1))
{
Single currX = 0;
Single currY = 0;
// Lock the previous items in place
for (int n = start; n < end; n++)
{
dt.Rows[n]["x"] = offsetX + currX;
dt.Rows[n]["y"] = offsetY + currY;
if (vert)
currY += (Single)dt.Rows[n]["height"];
else
currX += (Single)dt.Rows[n]["width"];
}
if (vert)
offsetX += (Single)dt.Rows[start]["width"];
else
offsetY += (Single)dt.Rows[start]["height"];
tmp_dWidth = width - offsetX;
tmp_dHeight = height - offsetY;
vert = DrawVert(tmp_dWidth, tmp_dHeight);
start = end;
end = start;
aspectCurr = 999;
goto startover;
}
else
{
// Store newly calculate sizes
for (int n = start; n <= end; n++)
{
dt.Rows[n]["width"] = dt.Rows[n]["widthTmp"];
dt.Rows[n]["height"] = dt.Rows[n]["HeightTmp"];
}
aspectCurr = aspectLast;
}
// try to draw another item
end++;
}
// Set each item in the positions in the remaining area
Single currX1 = 0;
Single currY1 = 0;
for (int n = start; n < end; n++)
{
dt.Rows[n]["x"] = offsetX + currX1;
dt.Rows[n]["y"] = offsetY + currY1;
if (vert)
currY1 += (Single)dt.Rows[n]["height"];
else
currX1 += (Single)dt.Rows[n]["width"];
}
}The Try function tests setting the total item sizes in the available space and return the aspect ratio of the last item we place. private Single Try(int start, int end, bool vert)
{
Single total = 0;
Single aspect = 0;
Single localWidth;
Single localHeight;
for (int n = start; n <= end; n++)
{
total += (Single)returnDs.Tables[0].Rows[n]["sizeTmp"];
}
// Scale as needed for the width or height
if (vert)
{
localWidth = total / tmp_dHeight * 100;
localHeight = tmp_dHeight;
}
else
{
localHeight = total / tmp_dWidth * 100;
localWidth = tmp_dWidth;
}
for (int n=start; n <= end; n++)
{
if (vert)
{
returnDs.Tables[0].Rows[n]["widthTmp"] = localWidth;
returnDs.Tables[0].Rows[n]["heightTmp"] = (Single)(localHeight *
((Single)returnDs.Tables[0].Rows[n]["sizeTmp"] / total));
}
else
{
returnDs.Tables[0].Rows[n]["widthTmp"] = (Single)(localWidth *
((Single)returnDs.Tables[0].Rows[n]["sizeTmp"] / total));
returnDs.Tables[0].Rows[n]["heightTmp"] = localHeight;
}
aspect = Math.Max((Single)returnDs.Tables[0].Rows[n]["heightTmp"] /
(Single)returnDs.Tables[0].Rows[n]["widthTmp"],
(Single)returnDs.Tables[0].Rows[n]["widthTmp"] /
(Single)returnDs.Tables[0].Rows[n]["heightTmp"]);
}
return aspect;
}
private bool DrawVert(Single width, Single height)
{
return width > height;
}
Using the CodeTo actually use the Treemap.cs C# code isn't too bad. Create an instance of the class, set the width and height, assign a DataSet with columns id, name, size and change to the DataSource member. Call CalcMap to calculate the layout and then GetData to retrieve a DataSet with the original columns and additional x, y, width, height, htmlcolour, r, g, and b columns. Then iterate around the return DataSet drawing items using the x, y, width and height values. Treemap.Treemap map = new Treemap.Treemap();
map.Width = (int)oMainCanvas.Width.Value;
map.Height = (int)oMainCanvas.Height.Value;
map.DataSource = LoadData();
map.CalcMap();
ds = map.GetData();
for (int n = 0; n < ds.Tables[0].Rows.Count; n++)
{
MSAvalon.Windows.Controls.Border b = new MSAvalon.Windows.Controls.Border();
Canvas.SetLeft(b, new Length(
Double.Parse(ds.Tables[0].Rows[n]["x"].ToString()),
Must apologise for the quality and naming of the code, I really should have reworked and refactored it a lot but once a had it working I didn't feel much like altering in the VMware session as it wasn't the greatest of setups to debug. Remember this is only meant as a demo, so don't judge me on the design please! Also wished I hadn't used DataSets as Data Transfer Objects between the view and controller, I thought I'd try it for a change but after all the casting and debugging nightmares (even with data visualisers) I'll stick using proper classes. Sample data and XPathThe sample industries, sectors and company information I use in the demo application I download as .csv information from the Yahoo Finance industry browser website. I then wrote a quick little Excel VBA macro to export that data as a string which happened to be XML; the schema I just made up. <?xml version="1.0" encoding="utf-8" ?>
<Industries Date="8-May-2004" Source="Yahoo Finance"
Size="Market Cap (Billions)" Change="1 Day Price Change (%)">
<Industry Id="I1" Name="Services" Size="4393.54" Change="-1.864">
<Sector Id="S1" Name="Communications Services" Size="1640.2" Change="-1.8" />
<Sector Id="S2" Name="Broadcasting & Cable TV" Size="497" Change="-1.22"/>
<Sector Id="S3" Name="Retail (Department & Discount)" Size="357.4"
Change="-1.35" />
<Sector Id="S4" Name="Real Estate Operations" Size="242.3" Change="-3.4" />
<Sector Id="S5" Name="Printing & Publishing" Size="216.1" Change="-0.82"/>
<Sector Id="S6" Name="Retail (Specialty)" Size="208.8" Change="-2.36" />
<Sector Id="S7" Name="Business Services" Size="195.8" Change="-0.4" />
<Sector Id="S8" Name="Retail (Grocery)" Size="162.8" Change="-1.42" />
<Sector Id="S9" Name="Retail (Home Improvement)" Size="120.4" Change="-3.59"/>
...
This means I can use XPathNavigator to select which set I want to display using simple XPath expressions such as: /*/* to get all the industries.
/*/*[@Id='I1']/* to get all the sectors for the I1 industry
/*/*[@Id='I3']/*[@Id='S1']/* to get all the companies in a sector, industry
And load the data with this: XPathDocument doc = new XPathDocument(@"c:\YahooData.xml");
// Create an XPathNavigator
XPathNavigator nav = doc.CreateNavigator();
// Create a node interator to select nodes and move through them (read-only)
XPathNodeIterator Iterator = nav.Select(currXPath);
while (Iterator.MoveNext())
{
dr = dt.NewRow();
dr["id"] = Iterator.Current.GetAttribute("Id", nav.NamespaceURI);
dr["name"] = Iterator.Current.GetAttribute("Name", nav.NamespaceURI);
dr["size"] = Iterator.Current.GetAttribute("Size", nav.NamespaceURI);
dr["change"] = Iterator.Current.GetAttribute("Change", nav.NamespaceURI);
dt.Rows.Add(dr);
textOverview.TextRange.Text = Iterator.Current.Name + " Overview";
}
You could easily replace my XML file with one of your own if you wanted. Designing the Layout of the ApplicationPlease don't take my demo application as a good example of UI design and what to expect from Longhorn. It was my first attempt at using XAML for forms design and it was quite difficult starting off with no prior knowledge. It will be interesting to see what the final design-time tools are like for editing XAML. Ideally they will be a cross between Visual Studio & Macromedia Dreamweaver, Fireworks and Flash with a bit of discreet 3ds max or Alias Maya for the 3D support. All of these tools you can get evaluation versions of to play with, and indeed I did to help with this article. Given the nature of XAML as a well-formed XML file, it should be easily 'tool-able' and hopefully might support wizards to help optimise in-line markup out to centralised styles. Squarified Treemaps Concept VideoAs mentioned at the start, I wanted to allow people who don't have the Longhorn PDC build installed to see the application working. I followed the style of the Microsoft Longhorn Concept Videos, which are a must watch if people haven't downloaded them before.
So recorded myself clicking through the application as an .avi file and then used Windows Movie Maker 2 to add some effects and transitions before producing it as a .wmv file; You'll need Microsoft Windows Media Player 9 to play the clip.
I also mocked up how a zooming effect could work if I used some of the XAML animations and transitions using Flash, so a little bit of cheating there. Download video demo - 3762 Kb Hope you enjoy watching it! Example ApplicationYou can start the application XAMLTreemaps.exe from the bin\debug directory and making sure the file YahooData.xml is in C:\. Once loaded click the 'Load Data' button on the right-hand side of the window.
Then notice as you move the cursor over the different areas you gets the additional metadata on the element in the floating window. Click on required Industry to drill into the data down to the sector level.
I've only added data for the 'Software and Programming' sector, so if you click on that it will show you the companies under that sector. Otherwise it will zoom back out to the top level again.
I should have used the navigation features of the XAML window style but it was enough work to get to this stage. Also I took the data on what was a pretty bad day for the financial markets as most of the sectors/companies price changes were negative. But it is should still give you a good example of how at a glance you can tell switch sectors and stocks are performing positively or negatively. If you resize the window you have to click Load Data again as I didn't find a way to tie-up the size changed event to the panel.
Visualisation & Interactive TechniquesNow, as promised, a quick look and mention of a few visualisation and interactive techniques I've seen of late. Note, I'm not associated with any of these products or companies but list them because I think they are interesting. At the desktop level, Microsoft Research has some screenshots and a video clip of something they called the Task Gallery. A research test project from 1999, they used it so learn how different metaphors, interaction techniques and underlying technological infrastructures work for application redirection. We won't see this coming in a future version of Windows but elements of the multiple desktops and categorisation are strong concepts. | ||||||||||||||||||||||||||||||||||||||||