@yashachan haha... probably in response to me not understanding the math behind some things we do :). I can explain the code, though!
Tweeted Mar 9, 2010 at 5:30:34 PM
Adding Custom WPF Controls to a XAML Document

Posted Dec 3, 2006 7:19:05 PM

It's often useful to either extend WPF controls or create custom controls to supplement to functionality provided to you by the WPF classes. In this brief tutorial, we'll refactor the code from the first part of the Drawing a RubberBand in WPF tutorial and see how we can add use a custom class in an XAML document.

Refactoring

We've already built a simple window class that contains a System.Windows.Controls.Canvas instance, and registered several mouse handlers with it to draw the rubber band. Instead of instantiating a base Canvas instance though and telling it how to behave (i.e., by registing external handlers), we'll extend the Canvas class, and move the handlers inside it so that'll it'll know how to behave.

The first step is to create a new User Control (WPF) type to our project. This will create an XAML document and associated code-behind file for a class that extends UserControl. Simply changing references to UserControl to Canvas will result in a class that extends Canvas, like so:

RubberBandingCanvas.xaml:
<Canvas x:Class="Rubber_Band_Tutorial.RubberBandingCanvas"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	Background="White">

</Canvas>

RubberBandingCanvas.xaml.cs:
public partial class RubberBandingCanvas : Canvas
{
    public RubberBandingCanvas()
    {
        InitializeComponent();
    }
}

As you can see, the XML root element in our XAML is a Canvas type, which matches the base-class type specified in our partial class definition in the code-behind. Also notice the addition of the Background="White" attribute to the canvas in the XAML... recall from part 1 that a canvas with no background does not respond to mouse event handlers (still haven't figured that one out, btw).

The next thing to do is move our mouse handlers out of the Window class, and into our new Canvas-derived RubberBandingCanvas. I'll also update the methods to be static, which'll help minimize the application footprint if I were to allow use of multiple canvases.

RubberBandingCanvas.xaml.cs:
public partial class RubberBandingCanvas : Canvas
{
    public RubberBandingCanvas()
    {
        InitializeComponent();

        this.MouseLeftButtonDown += OnLeftDown;
        this.MouseLeftButtonUp += OnLeftUp;
        this.MouseMove += OnMouseMove;
    }

    private Point mouseLeftDownPoint;
    private Shape rubberBand = null;

    protected static void OnLeftDown(object sender, MouseEventArgs args)
    {
        RubberBandingCanvas canvas = sender as RubberBandingCanvas;
        if (!canvas.IsMouseCaptured)
        {
            canvas.mouseLeftDownPoint = args.GetPosition(canvas);
            canvas.CaptureMouse();
        }
    }

    protected static void OnLeftUp(object sender, MouseEventArgs args)
    {
        RubberBandingCanvas canvas = sender as RubberBandingCanvas;
        Shape rubberBand = canvas.rubberBand;

        if (canvas.IsMouseCaptured && rubberBand != null)
        {
            canvas.Children.Remove(rubberBand);
            rubberBand = null;

            canvas.ReleaseMouseCapture();
        }
    }

    protected static void OnMouseMove(object sender, MouseEventArgs args)
    {
        RubberBandingCanvas canvas = sender as RubberBandingCanvas;
        if (canvas.IsMouseCaptured)
        {
            Point currentPoint = args.GetPosition(canvas);
            Point mouseLeftDownPoint = canvas.mouseLeftDownPoint;

            if (canvas.rubberBand == null)
            {
                canvas.rubberBand = new Rectangle();
                canvas.rubberBand.Stroke = new SolidColorBrush(Colors.LightGray);
                canvas.Children.Add(canvas.rubberBand);
            }

            double width = Math.Abs(mouseLeftDownPoint.X - currentPoint.X);
            double height = Math.Abs(mouseLeftDownPoint.Y - currentPoint.Y);
            double left = Math.Min(mouseLeftDownPoint.X, currentPoint.X);
            double top = Math.Min(mouseLeftDownPoint.Y, currentPoint.Y);

            canvas.rubberBand.Width = width;
            canvas.rubberBand.Height = height;
            Canvas.SetLeft(canvas.rubberBand, left);
            Canvas.SetTop(canvas.rubberBand, top);
        }
    }
}

The code is almost identical to the handlers we wrote last time, with the exception that member variables are resolved against the canvas object that raised the event (i.e., the sender paramter of the handler method).

Adding a Custom Class to a XAML

The only thing left to do is to update my main window's XAML to use the new RubberBandingCanvas class instead of the WPF's base Canvas class.

This is a simple two step process... the first is to add an XML Namespace declaration that will map my CLR-Namespace to an XML namespace, effectively giving me access to my class names as valid XML element names.

Window1.xaml:
<Window x:Class="TimFanelli.WPF.Tutorials.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Rubber_Band_Tutorial;assembly="
    Title="Rubber_Band_Tutorial" Height="300" Width="300"
    >
	<DockPanel LastChildFill="True">
		<ToolBarTray DockPanel.Dock="Top" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
			Removed for brevity...
		</ToolBarTray>

		<local:RubberBandingCanvas/>
	</DockPanel>
</Window>

The first bold line give me access to public members of the Rubber_Band_Tutorial clr-namespace via the local xml namespace. Then to add a RubberBandingCanvas to the windows dock-panel, I simply add it the same way I would any other element.

Conclusion

Most of this post was concerned with setting up the derived class we used, however the real goal was to add my custom class to the Window1 XAML document, which you can see if a very simple task. This method will work for not only any custom or derived controls you write, but indeed any CLR object with a default constructor.

It's also worth noting, since we didn't use the feature explicitly, any public CLR property (or dependency property) you expose in your objects can be set using standard XML syntax as well, with the property name used as an xml-attribute name on the element in question.

Drawing a RubberBand in WPF

Posted Nov 24, 2006 5:54:55 PM

Overview

Rubber-banding is a very simple and familiar concept in most graphical applications where the outline of a shape to be drawn is painted to the screen, following the mouse cursor so the end user can visualize exactly where the shape will be placed.

This tutorial will guide you through implementing a very simple WPF window containing a canvas, with rubber-banding rectangles. Adding additional shapes is then a trivial matter, which I'll address in a follow up to this post.

Main Window

The first thing we'll do is set up a simple window structure with a toolbar containing buttons for the various shapes, and a canvas on which we'll paint our rubberband. (For now, we're only addressing rectangles, and will ignore the toolbar all together... but I'm putting in place for the follow up post) The XAML to describe this structure looks like this:

<Window x:Class="Rubber_Band_Tutorial.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Rubber_Band_Tutorial" Height="300" Width="300"
    >
	<DockPanel LastChildFill="True">
		<ToolBarTray DockPanel.Dock="Top" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
			<ToolBarTray.Resources>
				<Style TargetType="{x:Type Button}">
					<Setter Property="Margin" Value="1,0,1,0"/>
				</Style>
			</ToolBarTray.Resources>

			<ToolBar Padding="2">
				<GroupBox Padding="2,5,2,0" Margin="0,0,3,0">
					<ToolBarPanel Orientation="Horizontal">
						<Button x:Name="shapeButtonSelect">
							Select
						</Button>
						<Button x:Name="shapeButtonRectangle">
							<Rectangle Margin="2" Width="13" Height="9" Stroke="Black"/>
						</Button>
						<Button x:Name="shapeButtonSquare">
							<Rectangle Margin="2" Width="13" Height="13" Stroke="Black"/>
						</Button>
						<Button x:Name="shapeButtonEllipse">
							<Ellipse Margin="2" Width="13" Height="9" Stroke="Black"/>
						</Button>
						<Button x:Name="shapeButtonCircle">
							<Ellipse Margin="2" Width="13" Height="13" Stroke="Black"/>
						</Button>
						<Button x:Name="shapeButtonLine">
							<Line Margin="2" X1="0" Y1="0" X2="13" Y2="13" Stroke="Black"/>
						</Button>
					</ToolBarPanel>
				</GroupBox>
			</ToolBar>
		</ToolBarTray>

		<Canvas Background="White" x:Name="canvas"/>
	</DockPanel>
</Window>

IMPORTANT: For some strange reason, if you do not set the background of your canvas, the event handlers we register with it in the following section do not work. It's almost as if the canvas disables it's mouse events in the absence of an explicit Background color... this makes no sense to me, if someone knows why this would be, please post it in the comments.

Functionality Overview

Once we have our main window structure, we'll need to attach several mouse handlers to paint the rubberband. We'll need to retain the point at which the left mouse button is depressed, track the mouse cursor as it moves, and finally remove the rubber band from the canvas when the mouse button is released. Therefore, we'll implement handlers for the canvas' MouseLeftButtonDown, MouseMove, and MouseLeftButtonUp events.

To get setup, we'll create some event handlers and register them with our canvas object in the main window's code-behind file.

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Shapes;

namespace TimFanelli.WPF.Tutorials
{
    public partial class Window1 : System.Windows.Window
    {
        public Window1()
        {
            InitializeComponent();

            canvas.MouseLeftButtonDown += OnLeftDown;
            canvas.MouseLeftButtonUp += OnLeftUp;
            canvas.MouseMove += OnMouseMove;
        }

        protected void OnLeftDown(object sender, MouseEventArgs args)
        {
        }

        protected void OnLeftUp(object sender, MouseEventArgs args)
        {
        }

        protected void OnMouseMove(object sender, MouseEventArgs args)
        {
        }
    }
}

Mouse Left Button Down

The canvas object raises its MouseLeftButtonDown event when the user presses the left mouse button over it. In this handler, we'll want to store the point at which this event occurred, as it will be used as one corner of the rectangle to which our shape is bound... so we'll add a private Point member to our window class, and implement the OnLeftUp function as follows:

        private Point mouseLeftDownPoint;
        protected void OnLeftDown(object sender, MouseEventArgs args)
        {
            if (!canvas.IsMouseCaptured)
            {
                mouseLeftDownPoint = args.GetPosition(canvas);
                canvas.CaptureMouse();
            }
        }

This is the simplest of the handlers we'll write... however it does several very important things... first and foremost, it obtains the point of the event on the canvas. The call to args.GetPosition() takes the UI element we'd like our Point in relation to - so by passing in the canvas, we obtain the X/Y coordinates in relation to (0,0) being the top-left corner of the canvas itself (as opposed to the containing dock panel, or window, or even the screen as a whole). Second, and equally as important, it causes the canvas to capture the mouse. This is done so that any mouse events that occur will be directed to our canvas, regardless of the mouse cursor's position on the screen. Notice that this is all done only if the canvas does not currently have mouse capture - we perform this check simply as a precaution, if somehow MouseLeftButtonDown happens twice, we don't want to perform these steps twice. We'll release mouse capture in OnLeftUp.

Mouse Move

The Mouse Move handler is where we'll actually create, draw and update our rubberband shape.

One very powerful feature of WPF is that when a UI element is added to a content container, you can affect it's appearance simply by modifying that element's properties... there's no need to redraw the element or post paint events to an event-queue, all this is done transparently for you. So we'll add a private Shape member to the window class and implement our OnMouseMove handler:

        private Shape rubberBand = null;
        protected void OnMouseMove(object sender, MouseEventArgs args)
        {
            if (canvas.IsMouseCaptured)
            {
                Point currentPoint = args.GetPosition(canvas);

                if (rubberBand == null)
                {
                    rubberBand = new Rectangle();
                    rubberBand.Stroke = new SolidColorBrush(Colors.LightGray);
                    canvas.Children.Add(rubberBand);
                }

                double width = Math.Abs(mouseLeftDownPoint.X - currentPoint.X);
                double height = Math.Abs(mouseLeftDownPoint.Y - currentPoint.Y);
                double left = Math.Min(mouseLeftDownPoint.X, currentPoint.X);
                double top = Math.Min(mouseLeftDownPoint.Y, currentPoint.Y);

                rubberBand.Width = width;
                rubberBand.Height = height;
                Canvas.SetLeft(rubberBand, left);
                Canvas.SetTop(rubberBand, top);
            }
        }

Notice we only handle mouse move if the canvas has mouse capture. This is effectively our way of asking "did the user press the left mouse button down" - and since we'll release mouse capture in OnLeftUp, it's more specifically "does the user have the left button held down?"

To place the shape properly, we calculate it's width and height, as well as the position for it's top-left corner.

The width and height calculations are the absoluate value of the difference between the left-down and current point's x and y coordinates. Remember that since (0,0) is the top left corner of the canvas, if the user drags a shape out to the right, then the difference between left-down's X and current point's X would be negative. Similarly, the top left corner is placed at the minimum X and minimum Y coordinates of the rectangle defined by the two mouse points.

Notice that the shape instance is only aware of it's width and height. The placement on the canvas is done using two static methods of the Canvas class, SetLeft and SetTop.

Mouse Left Button Up

The final step is to remove the rubberband when we release the left mouse button. This is also relatively simple:

        protected void OnLeftUp(object sender, MouseEventArgs args)
        {
            if (canvas.IsMouseCaptured && rubberBand != null)
            {
                canvas.Children.Remove(rubberBand);
                rubberBand = null;

                canvas.ReleaseMouseCapture();
            }
        }

All this handler does is remove the rubberband shape from the canvas, null out the rubber band so it'll properly be recreated later, and release the mouse capture. Effectively, we're just putting the world back the way we found it.

If this were a more full-fledged application, your mouse left button up would also have code that placed the a new shape instance on your canvas in the same position as the rubberband.

In my next post, we'll refactor this slightly and then add support for rubberbanding elipses and lines as well. The third part of this tutorial will address circles and squares.

Trackback for Blosxonomy

Posted Nov 13, 2005 12:40:58 PM

I ported the pyBlosxom trackback plugin this morning, so I now have trackback support re-enabled! Very exciting.

Now that Blosxonomy has comments and trackback support, I'm going to release version 0.5 at Blosxonomy.com

Blosxonomy Comments

Posted Nov 12, 2005 7:18:32 PM

I implemented a simple comments plugin tonight for Blosxonomy - loosely based on the pyBlosxom comment plugin. pyBlosxom's comment plugin is a little more involved than I was shooting for, as one of Blosxonomy's goals is simplicity. Mine simply checks the post data for a comment, verifies all the information is there, and writes out a comment file with a simple format:

Comment Author
Author's email
Comment body

The plugin requires the current code base, which I'm going to post over at Blosxonomy sometime tomorrow (I still need to do some sanity checking on it). In the meantime though, feel free to start leaving comments!

Powered by Me

Posted Nov 10, 2005 5:35:40 PM

I recently decided it'd be fun to learn the Ruby language, and that a worthy undertaking to do so would be to port pyBlosxom - which I've been using for a little over a year now. The result is something I'm quite proud of, and am still actively working on -- Blosxonomy.

"What a name!", you say? Blosxonomy is a Blosxom-like blogging system implemented with Folksonomy in mind. Basically I've written it from the ground up to incorporate the work I've been doing in pyBlosxom to support tags, tag clouds, and external relations (specifically to Amazon products).

So now I'm proud to say that timfanelli dot com is:

Powered by me.

If you're interested, I have a guide at Blosxonomy.com that covers installing Blosxonomy in parallel with pyBlosxom, so you can try it out with out modifying your existing pyBlosxom site. In fact, timfanelli dot com still has pyBlosxom running, I use it for the Portico gallery plugin, which I haven't gotten to port yet.

ATOM 1.0 Renderer for pyBlosxom

Posted Oct 31, 2005 10:46:00 PM

I wrote up an ATOM 1.0 renderer for pyBlosxom tonight based on Blake Winton / Will Guaraldi's RSS2Renderer plugin.

AtomRenderer renders a valid ATOM 1.0 feed without the need for ATOM flavour templates. You can access mine at http://www.timfanelli.com/atom. You can validate my output here.

You can download AtomRenderer here

Update: (Nov 1) Just got an email from Will Guaraldi letting me know that pyBlosxom 1.3 will have a native ATOM flavour for generating feeds -- so this plugin is really only useful for pyBlosxom 1.2 and earlier :).

Explicit Relations in Folksonomy

Posted Oct 31, 2005 7:29:15 PM

I added a feature to folksonomy tonight that lets you explicitly define relationships between stories using Folksonomy. To do this, just add a comma separated list of $absolute_path/$filename for the entries you want to be related to. For instance, I'm forcing this entry to be related to my Folksonomy Update post, by adding the following to its metadata section:

#related item/folksonomy_update.txt

You'll notice that it is the first entry in my related stories section -- explicit relations are evaluated before any others are inferred, and therefore take precedence.

This feature is available in Folksonomy 1.4

Related Amazon Items pyBlosxom Plugin

Posted Oct 30, 2005 3:23:00 PM

I wrote a quick plugin this morning to generate links to related books (or other items) at Amazon.com using Amazon's Web Services (AWS), based on your entry's tags.

The plugin queries AWS's ItemSearch for each tag in an entry, and then sorts out the results in decreasing order of how many tags a product was returned in. Links are then generated using Amazon's product images for the top N results, where N is some configurable number of products. All the links are generated using your own Amazon Associates ID as well.

You can download it here

From the plugin documentation:

Uses your Amazon Web Services account to query products related to 
your Folksonomy Tags. This plugin does not require Folksonomy, 
however if you are using Folksonomy, please get the latest version 
of it, as there were some potential conlicts over the 'tags' 
metadata entry.

Obtains a REST response from the following URL:

http://webservices.amazon.com/onca/xml?Service=AWSECommerceService
	&AWSAccessKeyId=config['aws_access_key']
	&AssociateTag=config['aws_associates_id']
	&SearchIndex=config['aws_product_type']
	&Operation=ItemSearch
	&Keywords=
	&Availability=Available
	&Merchant=Amazon
	[&Publisher=config['aws_favorite_pub']]

Specify your AWS Access Key and the type of product to 
search for in your config.py, like so (defaults are as 
shown here):

	# Your AWS Access key
	py['aws_access_key']=		

	# Your Amazon Associates ID
	py['aws_associates_id']=		

	# The type of product to search on.
	py['aws_product_type']=Books			
	
	# Restricts selections to a particular publisher
	py['aws_favorite_pub']=  

	# The number of books to show on a page.
	py['story_product_count']=4			

	# The size of the amazon img to retrieve
	py['aws_image_size']="MediumImage""		

aws_image_size can be one of:
	SmallImage
	MediumImage
	LargeImage

I like to use MediumImage and then scale it down using CSS. The 
SmallImage returns a very low-res picture from Amazon, and is 
useful if you don't have a lot of bandwidth.

aws_product_type and aws_favorite_pub can be overridden on a 
per-story basis by defining metadata with the same key name in 
your story. So for instance, if your config.py specifies an 
aws_product_type of "Books", but you just posted about DVDs, 
you could override this setting for your story by doing:

	My Post on DVDs
	#tags batman,superman,mighty mouse
	#aws_product_type DVD
	<p>DVDs are awesome.</p>

Valid aws_product_type values are 
	[ "Books","Music","DVD","Toys","Video Games","Software",
	  "Software Video Games","Electronics","Tools",
	  "Sporting Goods","Art Supplies","Kitchen","Gourmet Food",
	  "Apparel","PC Hardware","VHS" ]

aws_product_type defautls to "Books"

Populates a varaible $relatedproducts for use in your story 
template, that will contain HTML like:

	<div id="relatedproducts">
		<a class="productlink" href="product url">
			<img class="productimg" src="product img"/>
		</a>
		<a class="productlink" href="product url">
			<img class="productimg" src="product img"/>
		</a>
	</div>

If you do not want to generate related products for a particular 
entry, add a "noproducts" metadata entry:

	My Entry with No Products
	#noproducts
Folksonomy Update

Posted Oct 29, 2005 1:49:00 PM

Folksonomy 1.2 is avaialble for download

Folksonomy is a pyBlosxom plugin that infers relationships between your blog entries based on tags; providing links to related stories, links to related tags, a tag cloud, and the ability to search for entries by tag.

What's New: As of Folksonomy 1.2, there are no longer any dependencies on other plugins. Folksonomy is now a complete tagging solution for pyBlosxom. If you're already using Tags or Tag Cloud, simple replace those plugins with Folksonomy, and you'll still have all the same functionality you had before!

Folksonomy Quick Guide:

Configuration

Add the following entries to your config.py:

py['pretext'] = "<div class="tags">Tags:""
py['posttext'] = "</div>"
py['tagsep'] = ", "
#py['tag_url'] = "http://mysite/tags"  # defaults to $base_url/tags
#py['tag_url_display'] = "http://mysite/tags" # defaults to tag_url
#py['ignore_tags'] = []

pretext, posttext and tagsep will be used to render the $tags variable for your story template. tag_url is the base url to search for tagged entries. Links to tags will be to the url: $tag_url/. tag_url_display is useful if you use mod_rewrite (or an equivellant) to hide the actual URL of your tag_url. For instance, my tag_url is http://www.timfanelli.com/blog/tags/, but I use mod_rewrite to make it http://www.timfanelli.com/tags/ instead. tag_url_display effectively overrides my tag_url when generating links (tag_url is still needed for searching though, so don't change it!). Finally, ignore_tags will cause Folksonomy to ignore those specified tags when infering relationships and creating the tag cloud, but you'll still be able to search for entries with those tags.

Install Folksonomy

This parts easy, just copy folksonomy.py to your plugins directory, and enable it in your py['load_plugins'] if necessary. If you already have Tags or Tag Cloud installed, you may uninstall those plugins now.

Tag Your Entries

This part could be, as they say in French, the suck. Especially if you have a lot of entries. Tagging a single entry is very easy, just add a line like the following:

#tags tag1,tag2,tag3

Below your entry title. I've created a little helper script that will tag all your entries based on their category just to get you started (BACK UP FIRST, I TAKE NO RESPONSIBILITY FOR LOST OR DAMAGED CONTENTS). My friend Matt used it successfully though.

Set Up Your Templates

Now that everything's tagged, we need to set up your templates to show all the cool stuff. Starting with your story template:

  • Add $tags to your story, mine is located just underneath the body. This will show the tags for your entry.
  • Add $relatedstories, mine is located at the very bottom of my story template, and I use CSS to make it look nice.
  • Add $relatedtags, I don't show this on my blog because I don't think it adds much value, but it's there if you want to use it.

Then your header or footer template for the tagcloud:

  • Add $tagcloud or $populartagcloud wherever you like, mine is in my side bar defined in my footer template.

$popular tag cloud is a rebalanced subset of your full $tagcloud -- use this if you have a lot of tags with very few entries. It'll help reduce clutter on your page. I'm currently working to enable a URL that will show the full tag cloud for use with this feature.

Voila!

Your blog should now be folksonomy enabled. Easy, right?

Leave Feedback

I'm very excited about how well Folksonomy has been received so far in the pyBlosxom community, and I fully intend to keep it under active development. Please leave your feedback as comments here, or feel free to email me.

Creating a Tag Cloud in pyBlosxom

Posted Oct 22, 2005 6:55:00 PM

I installed the tags plugin tonight, and tagged most of the entries on my site. I decided to replace categories all together, and as such, you'll notice a tag cloud on the left side of my site.

I wrote a simple pyBlosxom plugin to generate the tag cloud, to be used in conjunction with the tags plugin. You can download it here: tag cloud plugin.

From the plugin documentation:

tag_cloud.py

Creates a tagcloud to compliment the tags plugin. Simply copy to 
your plugins directory and enable in py['load_plugins'] if 
necessary. The tags plugin must be installed and properly 
configured.

Simply add a #tags tag1[,tag2[...]] to your entry metadata section.

You should then define the following classes:
	.smallestTag
	.smallTag
	.mediumTag
	.bigTag
	.biggestTag

as well as

	#tagcloud

in your CSS to fit your needs.

As of 1.0.1, tag_cloud supports the following config property:
	py[ 'ignore_tags' ] = [ ]

where 'ignore_tags' is a list of tags to ignore while generating 
the tag cloud. This way, if you have a very widely used tag, such 
as "general", which is overwhelming your tag and isn't necessary, 
you can choose to omit it. 

As of 1.0.2, tag_cloud obeys the ignore_directories propery in 
pyblosxom.

To use the cloud, simply add $tagcloud somewhere to your head or foot templates. Mine happens to be in the foot template, because that's where I keep my sidebar code.

Comments, bug reports, and fixes are welcome!

Word Counter

Posted Oct 19, 2005 9:13:00 PM

That's right... word counter. Jeanna's students will know what I'm talking about.

For those of you that don't, this word counter, for one whole semester, was my pride and joy. Jeanna used this toy example program to help demonstrate how even the simplest programs, if not properly thought out and designed, can cause you massive headaches in a world where software consumers can't make up their minds. Jeanna would change the definition of what constitutes a "word" weekly, and off we'd go to adjust our counters - keeping in mind the end goal of having the fastest and most correct counter.

I'm pleased to say that my counter -- fully object oriented and extremely well designed -- was the second fastest in the class; beat out only by a man who some know as a maniac, others as my then-nemesis (due to our massively conflicting approaches to this project). He wrote his counter as a state machine, driven by goto's, in C. While unbeatable in speed, the low-level implementation required a complete re-write with each iteration. Mine only required a single line change, perhaps two or three depending how drastic the definition change was. But at most, I spent minutes each week on this project after my initial implementation*.

This application was also the start of my years-long const correctness frenzy. Check out the source -- it's const correct to the extreme!

You can download it here: word counter

* not including the time spent doing optimizations.

Popular Tags

Recent Stories

${recent.title}

About

My name is Tim Fanelli, I am a software engineer in Northern NY. I spend most of my time working, and when I can, I try to post interesting things here.

Cigar Dossiers