Click image for full size screenshot
Table of Contents
I like to keep an eye on how my articles are doing, so I wrote a little application to keep track of them. I thought it might be of interest to other authors, so here it is...
This WPF application was written in C# 3.0 using Visual Studio 2008 and requires .NET 3.5.
Please do not abuse this tool by loading lots of members or fetching ratings too often. Chris has kindly allowed me to publish this, so please do not make him regret it by overloading the servers.
If you want to jump right in, this is the section to read. It's not rocket science.
From the User menu, choose Add User. This brings up a dialog box where you can enter a CodeProject member id. This is the number shown on the Who's Who page for the member. Repeat as required.
Press the Get Ratings button.
Save updated data to file.
This is a short description of the metrics recorded by the app, both those calculated by CodeProject and a couple I have added.
This is calculated by CodeProject. I believe each vote is weighted depending on the "Status" of the voter, but I'm not sure about the details. As you know, it is a number between 1 and 5 inclusive.
This is also calculated by CodeProject, but the formula is known. It is:
popularity = rating * log10( number of votes )
This is a new summary metric for each author over all his/her articles. It is basically the average of each article's rating, but weighted by the number of votes for the article. I should point out, therefore, that articles with zero votes do not contribute to the X-Rating metric.
xrating = [ sum over all articles of ( votes * rating ) ] / [ total number of votes ]
This is also a new summary metric for each author. It is based on the formula for each article's popularity.
xpopularity = xrating * log10( total number of votes )
To make obvious any changes since you last downloaded, the cells in the grid turn yellow for changed metrics. So you can easily see when someone votes for one of your articles and drill into the grid to find the details.
The "Get Ratings" button works on the currently selected user in the combo box. If "All Users" is selected, it downloads and parses a page for every loaded user. If a particular user is selected, it only gets info for that user.
If you collect your ratings every day or so, you will soon build up a history of statistics showing how your articles are being received over time.
There are five views. The "All Authors" view lists all the summary ratings for all the authors in a file. Then there are two pairs of views. The first pair are the "Totals" and "History" views. These show the summary ratings for a particular author over time and the ratings on a particular date. The second pair are "Articles" and "Details". These show the current ratings for a particular author and the collected ratings for a particular article.
You can double click on a data row in the grid, or press the button, to change views. The view toggles between each view in each pair. You can press ctrl-A to select "All Authors" which shows the authors view again.
You can save the data to a file as either a *.xml or *.bob file. The difference is that the *.bob file compresses the XML by using a GZipStream
. This compression reduces the file size by a factor of about ten.
There is no "do you want to save your changes" dialog, so don't forget to save your file after updating.
Apart from adding a user, the only edit you can make is to delete a row. Just select a row and press the backspace
or delete
key. This functions in all views to remove either an author, an article, or a single detail. Of course you always have the choice of reloading your data from file, thus discarding all unsaved changes.
All the data is available on the article summary page for each member. The app fetches this page and parses it using RegEx
's. This means it is quite fragile and will stop working if Chris changes so much as a space. However, he doesn't do this very often. I have only tweaked the RegEx
's a few times during the two years I have been using this.
The RegEx
's are in the CodeProject.cs file, if you want to take a look.
When you save to a file, all collected data is persisted. Nothing is discarded. This means that by taking a snapshot every day or so, you can build up a history of ratings. This in turn enables the application to highlight new votes on articles.
The summary details are calculated whenever they are required from the article stats, so they always reflect your current data. There is some LINQ fun and games going on in Data.cs, if you're interested.
Xceed have written an excellent grid control for WPF and offer an "Express Edition" for free. At the time of writing, they have just released version 2.0 here [^]. They are another benevolent Canadian company, by the way.
There are plenty of articles about WPF, so I won't dwell on it here. However, there are a couple of things worth mentioning.
The little circles on the buttons come from a neat data template:
<DataTemplate x:Key="ButtonTemplate">
<StackPanel Orientation="Horizontal">
<StackPanel.Width>
<Binding Path="Width" RelativeSource="{RelativeSource AncestorType=Button}" />
</StackPanel.Width>
<Ellipse Width="10" Height="10" Margin="0,0,10,0">
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0.3, 0.3">
<GradientStop Offset="0.2"
Color="{StaticResource FloralWhiteColour}" />
<GradientStop Offset="1" Color="{StaticResource OrangeColour}" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<ContentPresenter Content="{TemplateBinding Content}" />
</StackPanel>
</DataTemplate>
Double-clicking a row in the grid swaps views. The obvious way I tried was this:
grid.MouseDoubleClick += ( s, a ) => SwapViews();
Unfortunately this also responds to double clicks in areas like the scroll bars.
So I decided to hook the event at the DataRow
level. Luckily, the EventManager static
class that you use to register a routed event, also has a method called RegisterClassHandler
. This allows you to hook an event on all instances of a class:
EventManager.RegisterClassHandler(
typeof( DataRow ),
Control.MouseDoubleClickEvent,
new RoutedEventHandler( ( s, a ) => SwapViews() ) );
If you want to know when a dependency property changes, you can use the DependencyPropertyDescriptor
class:
DependencyPropertyDescriptor.FromProperty(
DataGridControl.SelectedItemProperty,
typeof( DataGridControl ) )
.AddValueChanged( grid, grid_SelectedItemChanged );
Well, that's all folks. If you like this app, please vote. I'll be watching!
2008 March 06: |
First published |
2009 April 23: |
Bug fix - code updated |