Click here to Skip to main content
15,946,529 members
Articles / Programming Languages / C#

CP Vanity

Rate me:
Please Sign up or sign in to vote.
4.93/5 (80 votes)
8 Jun 2011CPOL8 min read 208.9K   1.8K   67   141
A viewer for CodeProject's recently introduced reputation information; futile, but fun


This article presents a viewer for CodeProject's recently introduced reputation information. Completely futile, but fun to create, and fun to have available.

Overall Functionality

The first view presents information on a single account; it shows the user name (HTML rendered in a little WebBrowser), the reputation graph, and a list of articles (real ones and Tip&Trick ones).

CPVanity One Account

The second view presents a list of "highest achievers", i.e. the people that show up on the Who's Who pages when querying by reputation, by article count or by message count. Values for all reputation categories are listed here.

CPVanity Highest Achievers

The third view offers counter values for the same members.

CPVanity Highest Achievers

Some Functional Details

Some effort has been spent to offer functionality together with user comfort:

  • The User ID, when changed, gets stored in the registry; a single registry key is used under CurrentUser\Software\CPVanity\MemberID, so the utility will start by showing the last account it has presented in a previous session; unless it gets launched while holding the control key (no account shown), or a userID is passed on the command line (Yes, you could make desktop shortcuts for a number of accounts).
  • To honor CodeProject conventions, hitting CTRL/F5 will refresh the current view.
  • The "Highest Achievers" view applies the CodeProject coloring scheme (white, bronze, silver, gold, and platinum).
  • The champion for each reputation category gets a blinking red background, and the one account (if any) gets a blinking green background when present amongst the highest achievers. This blinking effect is conditional on a checkbox and gets persisted in another registry key CurrentUser\Software\CPVanity\Blinking.
  • The "Highest Achievers" page offers hyperlinks to the profile page of all the listed accounts; when clicked with control key down, CP Vanity loads the "One Account" view with the selected account. Since version 2.3 the "shift" button (as well as the "shift" key!) scroll the DataGridView horizontally and reveal several new values, the total counts for articles, tips, blogs, messages, questions, answers, and comments.


There are a lot of limitations to CP Vanity, some by design, some by the principles that had to be used in order to collect the necessary information. Here is a list:

  1. The window width is fixed at 1024; that is the width the DataGridViews need to display all their information; since I don't like horizontal scrolling, I decided to settle for a fixed width that should be no problem except for the smallest displays.
  2. CP Vanity shows a snapshot; it does not provide a live image or an automatic refresh.
  3. The information is gathered by fetching a couple of CodeProject web pages and scraping their HTML content; there is an ever increasing risk that something will change (the URLs used, the page content, the HTML tags used, whatever) to such an extent that CP Vanity no longer functions as intended. If so, I may or may not provide an update.
  4. The Highest Achievers list is aggregated from the Who's Who pages, which basically offers the top members ordered by total reputation, by article count, or by message count. For some of the reputation categories, many points could be earned without creating lots of articles or messages (e.g. Organiser points are earned by voting), so it is conceivable, but not very likely, that some highly ranked people will not show up on the "Highest Achievers" page.

Some Technical Details

All the CodeProject specific things are isolated in the CPSite class, which offers methods to get URL strings, to fetch web page content, and to extract the required information. If anything changes at CodeProject, CPSite is where the changes may have to be reflected.

All web page fetching and scraping is performed by a BackgroundWorker, and of course the GUI updating is handled in the ProgressChanged and RunWorkerCompleted event handlers.

The TabControl Tabs

The configurability of the WinForms TabControl is very limited; all the tabs are rendered identically, using the TabControl's Font, ForeColor and BackColor values. So a different look-and-feel (multiple colors, hovering effects, ...) are not supported at all. This often is the subject of a question in the programming forums.

As I wanted a menu bar rather than the typical tabs, my dilemma was to either use a TabControl and somehow fake a tab strip, or use superimposed Panels rather than TabPages and control their visibility myself. The one advantage of the TabControl is that one can design the TabPages in site, within the Form itself; no separate Forms or UserControls are needed. So I decided to go for the former, and take on the fight with TabControl. This is what I came up with:

  • Inside Visual Designer, I added a Panel and a TabControl to the Form. The Right and Width properties match, the Panel.Height is sufficient to hold the new tab strip and to hide the unwanted tabs (once the Panel and TabControl get moved over one another); however the TabControl's position is under the Panel, so the TabControl initially is a bit lower, allowing both of them to be designed with ease.
  • At run-time (i.e. in the Form constructor, right after calling InitializeComponents), the TabControl's Top gets reduced and its Height gets increased by the height of the Panel, effectively sliding the native tabs under the artificial tab strip. Et voila, the ugly tabs are gone, and the look-and-feel is what was hoped for. In reality, the tab strip isn't as tall as the old tabs, however a second panel above them hides whatever protrudes.
CPVanity Designer Detail CPVanity RunTime Detail

The left image shows the Designer view: the new tab strip (green panel) will be used to hide the original (gray) tabs, which have Appearance=FlatButtons. The right image shows the run-time view, the original tabs are hidden by sliding the TabControl up and under the other panel(s).

DataGridView Problems

I've had some trouble getting the DataGridView controls to correctly render empty cells! Everything works fine when background coloring is not required; however, when a cell changes from non-empty with background color into empty (always without background), then the cell does not get erased.

The one workaround that solved (most of) the unwanted mispaints, consisted of three special measures:

  1. When the TabControl switches to another page, the newly visible DataGridView is made invisible for a while with a simple Thread.Sleep(100). That is sufficient to get a clean background before the control gets rendered as required.
  2. When the application returns from the background, the same repaint is forced.
  3. When the DataGridView gets sorted, the background coloring is suspended for a while; i.e. a boolean flag controlling background colors is set false, and a timer is launched to set it true again some 100 milliseconds later. Without this trick, toggling the sort order in some columns creates a mirrored image, where the cells from the top and those from the bottom look identical (whereas one set of them should be empty).

The above did not solve the problems about repainting (or lack thereof) that should occur when the DataGridView gets scrolled, so I kept looking for a real solution. And I finally discovered what seems to be the real problem: empty cells only work properly when no background color gets set (not even White or Transparent), so the CellFormatting event handler has to check the reputation points before setting the BackColor, and skip all of it for a zero value. I fixed the code accordingly in version V1.3 and removed the code related to the earlier workarounds. All is well now.

Regex Performance

While I'm no Regex expert, I wanted to come up with patterns that would be somewhat tolerant to changes in the HTML code used. Initially that resulted in some complex patterns containing multiple wildcards, some greedy, some lazy. The net result was scraping tended to use several seconds per page.

In a second iteration, I simplified the Regex patterns, used fewer wildcards, and fewer groups; and I used some simple string searches too. As an example, the user names and the reputation numbers are extracted with regex patterns, whereas the colors are determined by searching separately for the color names. The speed up was spectacular, scraping time now is irrelevant.


To support situations where the PC is located behind a firewall, the configuration file got adapted to this:

<?xml version="1.0"?>

  <defaultProxy enabled="true" useDefaultCredentials="true" />
<startup><supportedRuntime version="v2.0.50727"/></startup></configuration>

When a proxy server uses password-based authentication, a NetworkCredential must be issued; look into file CPSite.cs and adapt method CreateWebRequest() as required.


CP Vanity offers two pages of highly-condensed information at your finger tips, most of it presented in DataGridViews that can be sorted in many ways. One remarkable outcome is that the seven reputation categories currently have six different champions.


Thanks to:

  • Sacha Barber, whose Really Vain Web Spider article provided a lot of inspiration, and from whom I borrowed an icon
  • Pete O'Hanlon and Don Kackman, for providing the firewall/proxy suggestions
  • All early users for reporting their findings and offering suggestions and comments


  • Version 1.0 (22-Mar-2010): Original version
  • Version 1.1 (23-Mar-2010): Regional settings bug fixed; tooltips added to DataGridViews; article titles listed; minor improvements
  • Version 1.2 (23-Mar-2010): Duplicate titles bug fixed
  • Version 1.3 (28-Mar-2010): DataGridView background fixed; Bronze bug fixed; minor improvements
  • Version 1.4 (29-Mar-2010): Total reputation points and color calculated when not present at their original position
  • Version 1.5 (06-Apr-2010): Linklabel bug fixed; blink checkbox added; MVP column added
  • Version 1.7 (03-Aug-2010): accepting the latest CP styles; individual member type icons added
  • Version 1.8 (09-Oct-2010): added bookmarks column to articles table; several minor GUI improvements
  • Version 2.0 (06-Dec-2010): added support for Who's Who queried by Reputation, and cbFetchJob ComboBox; added PlatinumCount column; several minor GUI improvements
  • Version 2.1 (25-Dec-2010): updated to cope with the latest web site changes
  • Version 2.2 (27-Mar-2011): added downloads column to articles table
  • Version 2.3 (24-May-2011): added several counters to the top achievers tab, see "shift" button; updated and added some screen shots; added an executable-only download; fixed a scraping bug (the "One Account" tab failed to deal with the thousand separator in article votes, bookmarks, and downloads)
  • Version 2.4 (09-Jun-2011): added rep graph zooming


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Written By
Software Developer (Senior)
Belgium Belgium
I am an engineer with a background in electronics, software and mathematics.

I develop technical software, both for embedded systems and for desktop equipment. This includes operating systems, communication software, local networks, image processing, machine control, automation, etc.

I have been using all kinds of microcontrollers and microprocessors (Intel 4004/8080/8051/80386/Pentium, Motorola 680x/680x0/ColdFire/PowerPC, Microchip PIC, Altera NIOS, and many more), lots of programming languages (all relevant assemblers, Fortran, Basic, C, Java, C#, and many more), and different operating systems (both proprietary and commercial).

For desktop applications and general development tools I have been using both UNIX systems and Mac/MacOS for many years, but I have switched to x86-based PCs with Windows, Visual Studio and the .NET Framework several years ago.

I specialize in:
- cross-platform development (making software that runs on diverse hardware/OS combinations)
- instruction set simulation
- improving software performance, i.e. making sure the software runs the job at hand in as short a time as possible on the given hardware. This entails algorithm selection, implementation design, accurate measurements, code optimisation, and sometimes implementing virtual machines, applying SIMD technology (such as MMX/SSE), and more.

Comments and Discussions

GeneralPretty good! Pin
Rajesh R Subramanian24-Mar-10 2:42
professionalRajesh R Subramanian24-Mar-10 2:42 
GeneralRe: Pretty good! Pin
Luc Pattyn24-Mar-10 2:51
sitebuilderLuc Pattyn24-Mar-10 2:51 
GeneralDates.... Pin
martin_hughes23-Mar-10 21:37
martin_hughes23-Mar-10 21:37 
GeneralRe: Dates.... Pin
Sandeep Mewara23-Mar-10 21:54
mveSandeep Mewara23-Mar-10 21:54 
GeneralRe: Dates.... Pin
Luc Pattyn24-Mar-10 2:50
sitebuilderLuc Pattyn24-Mar-10 2:50 
GeneralLaunch of version V1.2 Pin
Luc Pattyn23-Mar-10 13:38
sitebuilderLuc Pattyn23-Mar-10 13:38 
GeneralRe: Launch of version V1.2 Pin
Tom Deketelaere24-Mar-10 0:44
professionalTom Deketelaere24-Mar-10 0:44 
GeneralNice work + question + bug Pin
Kristian Sixhøj23-Mar-10 7:17
Kristian Sixhøj23-Mar-10 7:17 
Nice little tool you've written here Smile | :)

One question though: The 'Total reputation' row (on the Highest Achievers page) is blinking red all the way down, and there's no values in the cells - Why?

There's also some blinking (also red) cells in the other rows, what does this mean? These do contain values though.

And lastly, a bug report (I know this isn't anything 'extremely serious', but still Smile | :) ). When clicking the Name - uh.. what do you call it.. tab? - to sort by name, I get an ArgumentOutOfRangeException. It doesn't occur every time, but if you keep clicking the "tab?", it will eventually throw the exception:

System.ArgumentOutOfRangeException: Indekset lå uden for området. Det må ikke være negativt og skal være mindre end størrelsen på samlingen.

Parameternavn: index
   ved System.Collections.ArrayList.get_Item(Int32 index)
   ved System.Windows.Forms.DataGridViewRowCollection.SharedRow(Int32 rowIndex)
   ved System.Windows.Forms.DataGridViewRowCollection.get_Item(Int32 index)
   ved CPVanity.frmMain.dgvUsers_CellContentClick(Object sender, DataGridViewCellEventArgs e) i H:\LP_Projects\_DotNet\CPVanity\frmMain.cs:linje 423
   ved System.Windows.Forms.DataGridView.OnCellContentClick(DataGridViewCellEventArgs e)
   ved System.Windows.Forms.DataGridView.OnCommonCellContentClick(Int32 columnIndex, Int32 rowIndex, Boolean doubleClick)
   ved System.Windows.Forms.DataGridViewCell.OnMouseUpInternal(DataGridViewCellMouseEventArgs e)
   ved System.Windows.Forms.DataGridView.OnCellMouseUp(DataGridViewCellMouseEventArgs e)
   ved System.Windows.Forms.DataGridView.OnMouseUp(MouseEventArgs e)
   ved System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   ved System.Windows.Forms.Control.WndProc(Message& m)
   ved System.Windows.Forms.DataGridView.WndProc(Message& m)
   ved System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   ved System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   ved System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

I took a look at line 423, and it contains nothing but btnTopUsers.Enabled=true;, so how can this be causing an ArgumentOutOfRangeException?

Anyway, once again, nice work Smile | :) You got my 5.
Green Alien | [Alien] Kristian Sixhoej
"Invisible women out to kill ya - Oh, great!"

AnswerRe: Nice work + question + bug Pin
Luc Pattyn23-Mar-10 7:34
sitebuilderLuc Pattyn23-Mar-10 7:34 
GeneralRe: Nice work + question + bug Pin
Tom Deketelaere23-Mar-10 8:12
professionalTom Deketelaere23-Mar-10 8:12 
GeneralRe: Nice work + question + bug Pin
Kristian Sixhøj23-Mar-10 8:22
Kristian Sixhøj23-Mar-10 8:22 
GeneralRe: Nice work + question + bug Pin
Tom Deketelaere23-Mar-10 8:34
professionalTom Deketelaere23-Mar-10 8:34 
AnswerRe: Nice work + question + bug Pin
Luc Pattyn23-Mar-10 8:50
sitebuilderLuc Pattyn23-Mar-10 8:50 
GeneralRe: Nice work + question + bug Pin
Tom Deketelaere23-Mar-10 10:05
professionalTom Deketelaere23-Mar-10 10:05 
AnswerRe: Nice work + question + bug Pin
Luc Pattyn23-Mar-10 10:20
sitebuilderLuc Pattyn23-Mar-10 10:20 
AnswerRe: Nice work + question + bug Pin
Luc Pattyn23-Mar-10 13:42
sitebuilderLuc Pattyn23-Mar-10 13:42 
GeneralRe: Nice work + question + bug Pin
Kristian Sixhøj23-Mar-10 23:50
Kristian Sixhøj23-Mar-10 23:50 
GeneralNice, but 2 remarks and 1 question :) Pin
Tom Deketelaere23-Mar-10 6:14
professionalTom Deketelaere23-Mar-10 6:14 
AnswerRe: Nice, but 2 remarks and 1 question :) Pin
Luc Pattyn23-Mar-10 7:28
sitebuilderLuc Pattyn23-Mar-10 7:28 
GeneralRe: Nice, but 2 remarks and 1 question :) Pin
Tom Deketelaere23-Mar-10 8:10
professionalTom Deketelaere23-Mar-10 8:10 
AnswerRe: Nice, but 2 remarks and 1 question :) Pin
Luc Pattyn23-Mar-10 13:42
sitebuilderLuc Pattyn23-Mar-10 13:42 
GeneralNice one Pin
DaveyM6923-Mar-10 3:51
professionalDaveyM6923-Mar-10 3:51 
AnswerRe: Nice one Pin
Luc Pattyn23-Mar-10 5:01
sitebuilderLuc Pattyn23-Mar-10 5:01 
AnswerRe: Nice one Pin
Luc Pattyn23-Mar-10 13:42
sitebuilderLuc Pattyn23-Mar-10 13:42 
GeneralGreat job Luc Pin
Pete O'Hanlon23-Mar-10 3:08
mvePete O'Hanlon23-Mar-10 3:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.