Delta Engine Blog

AI, Robotics, multiplatform game development and Strict programming language

Most distracting addin ever for Visual Studio: Changing background images

Download link for CR_RandomBackgroundChanger now at CodePlex.

I used several tools over the years to change my background image automatically every few minutes. I even collected over 50k desktop images over the years ^^ While this is great and always fun to watch at changing backgrounds every few minutes, most of the time I do not even notice because all my screens are completely covered by windows.

As a programmer I have Visual Studio open most of the time, especially at work, where distractions like changing background images would have the biggest impact. Luckily Visual Studio has a boring white (or black for me) background and will not distract us that much. It would be terrible if Visual Studio would have changing background images that even make it harder to look at code and could be distracting having a background image changing every 5 minutes. Let's explore that!

First of all: How to even change or set a background image directly in the Visual Studio code window?
There is a great collection of gadgets from the SlickEdit guys, called Free SlickEdit Gadgets, which are completely free to use and include a feature to set a background image in the code editor. Searching for this on a search engine like Google isn't that easy, it is easier to find if you search for "Visual Studio Dancing Banana" :)

After you have installed the SlickEdit Gadgets (which currently only work on VS2005/VS2008, not on VS2010 yet), you should see a SlickEdit menu item. From there you can select Editor Gadgets, which just opens the Visual Studio Options dialog. There you can go to the Editor graphic tab and select a background image to be displayed in the editor. I use the following settings:
  • Display an image: Checked
  • Image textbox: The selected image
  • Tile Radiobutton checked (use bottom left if you just want a small transparent image there, IMO not distracting enough!)
  • Lock the image location checked
  • Transparency checked and set to 75% (which means 25% visibility, 75% of your background color remains).
Now your code window might look like the following:


While this is kinda fun, it is not distracting enough yet. We need changing background images and preferably a way to quickly change the editor background image with a click at anytime in case it doesn't fit. First of all we need to find out where SlickEdit saves this background image. After searching for some SlickEdit setting file and not able to find any, I checked the registry and found this key: HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\SlickEdit\EditorGadgets\ImageFilename This is where the image name is saved. Just for fun I changed it to "C:\Users\Public\Pictures\Sample Pictures\Penguins.jpg", but obviously nothing happend because there was no reason for Visual Studio to reload and apply any new settings from the Options dialog. So how to we force that to happen?

Well, after trying to find some solution for that for a while, I gave up. There is no event I can trigger to update this and there is simply no functionality in the SlickEdit Gadgets to get this update working.

Since I would need to write something to set a new background image every few minutes anyway and this won't be easy or even impossible with my little Wallpaper Changer app I decided to write a VS addin myself. I started a new CodeRush plugin (which also runs on the free DXCore framework) and called it CR_RandomBackgroundChanger (a little bit too long for CodePlex, so there it is just called VSBackgroundChanger). Next I used the EditorPaint event and wrote the following code to display an image in the code editor:

 private void BackgroundChangerPlugIn_EditorPaint(EditorPaintEventArgs ea)
 {
     //Log.Write("BackgroundChangerPlugIn_EditorPaint");
     //tst: ea.DrawLine(1, 1, 50, Color.Red);
     if (currentBackgroundImage == null)
         currentBackgroundImage = Bitmap.FromFile(
             @"C:\Users\Public\Pictures\Sample Pictures\Desert.jpg");
     ea.Graphics.DrawImage(backgroundImage, 0, 0);
 } // BackgroundChangerPlugIn_EditorPaint(ea)
 
This produced terrible results. The image got displayed for a fraction of a second and then was replaced by the default background color. Using other events from the StandardPlugIn class in the CodeRush framework like EditorPaintBackground, EditorPaintForeground, EditorPaintLanguageElement, EditorvalidateClipRegion, etc. also did not help much, the background image was usually not displayed and even if it was flickering terribly and disappearing all the time.

So CodeRush was not very helpful in this particular instance, but no reason to give up. Actually I gave up that day after trying out so many things for many frustrating hours. The next day I tried something different: Use the NativeWindow class to intercept any event that is send to the Visual Studio code editor window (called View in CodeRush btw). Basically you derive from NativeWindow and then call the AssignHandle method to set the window handle (I just do it in the constructor). From then on you will get all WndProc events and you can decide whether to pass it on to the original WndProc or do something yourself.

public class ActiveViewBackgroundHelper : NativeWindow
{
    #region Constructor
    /// <summary>
    /// Create active view background helper class, each document view
    /// will get an instance of this class to handle the background drawing!
    /// </summary>
    public ActiveViewBackgroundHelper(IntPtr windowHandle)
    {
        base.AssignHandle(windowHandle);
    } // ActiveViewBackgroundHelper(windowHandle)
    #endregion

    #region WndProc
    /// <summary>
    /// WndProc, we are only interested at the WM_PAINT event!
    /// If a paint happens and we got a valid  we will call DrawBackgroundImage
    /// </summary>
    /// <param name="m"></param>
    protected override void WndProc(ref Message m)
    {
        // Your own code goes here
        base.WndProc(ref m);
    } // WndProc()
    #endregion
} // class ActiveViewBackgroundHelper

Now I was able to intercept all WM_ERASEBKGND and WM_PAINT messages. WM_ERASEBKGND turned out to be non-relevant because all the rendering happens in WM_PAINT. Otherr events like WM_NCPAINT and all the code rush events happened between a single WM_PAINT event anyway. So I had to dig deeper into WM_PAINT. I could not render a background image at the very start of WM_PAINT and if I aborted rendered right there, it would be displayed in the editor. But there was no text anymore making VS just useless. I had to figure out how to render text on top of my image by copying all the VS editor content to a bitmap and then later drawing it on top of my background image. To make all the flickering go away by drawing into a targetBitmap and then displaying that at once to the window (everything else flickers). There is also a lot of other optimizations and tricks in there. We will get back to the code in a little bit.

After working on this for the better part of the last weekend, I finally got it working and put some extra features in this CR_RandomBackgroundChanger addin, which you can see in this nice option screen for it. Note that I have a black background in Visual Studio, if you have a white one the preview image would be white with black text and it would have 25% of the background image mixed in.



You can download the addin and the source code at http://vsbackgroundchanger.codeplex.com/

I'm trying out CodePlex for this addin. I will probably improve it a little in the future and upgrade it to VS2010. I will also put up other projects on CodePlex like CR_Commenter and maybe some of my XNA games. I also plan to work a little bit more on my language and then put it on CodePlex too when I got a alpha version working with all basic features.

In the option screen of CR_RandomBackgroundChanger you can specify 5 settings:
  • Enabled: Whether to use this addin at all. This allows you to quickly disable the addin and keeping all your previous settings once you need more distractions again.
  • Directory: All images in this directory and all images in all the sub directories will be used for displaying random background images in your Visual Studio code editor. Please note that searching for 100 000+ images can take a few seconds (I tested it with that many), but the subsequent VS startups and every background image change will be very fast because of all the caching involved. The disadvantage of all the caching is that if you add new images or even delete the whole images folder, you need to update this directory to get all those changes reflected by the addin by either deleting the cached ImageFilenames.txt in your %TEMP% directory or by just selecting the same directory again.
  • Transparency (default: 25% opacity): This is the most important setting because having colorful background images at 100% opacity can often make the already colorful editor text unreadable. If you know that your background images have mostly the same color as your code background you can use higher settings (for example I used some black only images with some stuff on the right in the beginning, while this was cool at first, it got boring after a while because I'm used to changing background images every few minutes). If you have photos or other images that do not really fit to having text on top of them use even lower opacity settings (e.g. 10%) to make Visual Studio text easier to read. You can mostly still see the background image if your code window is big enough and you have some empty lines in there :)
  • Change image every x seconds: Use this setting to allow the addin to change the background images every 5 minutes (or use whatever number of minutes you like). Note that using 0 minutes will result in constantly changing backgrounds while you are typing and scrolling, not really sure if this is useful, but was good for testing ^^
  • And finally the last settings is to use different background images for different document windows. If you have 20 documents opened in Visual Studio, every one of them will get a different background image (which can then change every 5 minutes too). This even makes it easier to figure out where you are, I probably need to improve the Tab-Rendering too to make it more useful like the ColorfulTabs addin for Firefox. And this is of course great fun, usually I get annoyed by having too many documents open so I often close them all after I get 50, but now I really like having many different background images open.
The option screen also features the Preview box, which lets you see your selected background images and tweak the transparency setting until it looks right for you. Once you close the Options screen all settings will be applied to your VS code editor. Please note that this addin only works whenever a WM_PAINT event happens. This is usually only in the active document you are working on. Most of the time the background image will stay even if you change to another tab on the other side (if you use 2 tabs at once, else you will never notice this anyway). Sometimes you see some black or white background lines drawn on top of your background image because just some text was updated, but this happens very rarely. I hope you enjoy the addin, I will probably use it for a long time and I will port it to VS2010 once CodeRush runs there or I'm using VS2010 (still waiting for a working TestDriven.net version). Hopefully implementing this addin with the MEF (Managed Extensibility Framework) is not hard.

Let's go back to the code. Last time I stopped talking about the WndProc method, which is most important for catching and handling the WM_PAINT event. From here we can call our DrawBackgroundImage method, which does all the magic. Please note that the source code for the addin is a lot more complex than the code presented here. This is mostly because of heavy optimizations and a lot of caching to make the code run as fast as possible (one of the most important things about this addin, it would be unusable if it makes VS slow).

#region WndProc
/// <summary>
/// WndProc, we are only interested at the WM_PAINT event!
/// If a paint happens and we got a valid  we will call DrawBackgroundImage
/// </summary>
/// <param name="m"></param>
protected override void WndProc(ref Message m)
{
    // Just make sure we mark the erase background message as handled.
    // Will not do anything anyway (all painting done in WM_PAINT).
    if (m.Msg == (int)WindowsMessages.WM_ERASEBKGND)
    {
        // Mark event is already handled
        m.Result = (IntPtr)1;
    } // if

    // Handle the paint event
    if (m.Msg == (int)WindowsMessages.WM_PAINT &&
        // Are we currently drawing our own background? Then make sure we do
        // not handle this and use the default message handling instead!
        drawingBackground == false &&
        // Make sure we got a view, else we don't have a windows handle!
        CodeRush.TextViews.Active != null &&
        // Check for invalid hwnd, then we can't paint backgroundImage!
        (int)CodeRush.TextViews.Active.Handle != 0)
    {
        TextView view = CodeRush.TextViews.Active;
        IntPtr activeWindowHandle = view.Handle;
        Rectangle rect = new Rectangle();
        Win32.GetUpdateRect(activeWindowHandle, ref rect, false);
        // Only proceed if we have a valid rect
        if (Win32.IsRectEmpty(ref rect) == false)
        {
            // Make sure we mark this flag so subsequent calls to WM_PAINT
            // will actually just paint the normal stuff, not just our
            // background rendering!
            drawingBackground = true;
            DrawBackgroundImage(activeWindowHandle, rect);
            drawingBackground = false;

            // Mark event is already handled
            m.Result = (IntPtr)1;

            // Do not call base.WndProc, we don't want to process it here!
            // Instead we invalidate inside DrawBackgroundImage and force
            // a new WM_PAINT event inside there, which will be executed
            // because drawingBackground is still true while in there.
            return;
        } // if (rect)
    } // if (WM_PAINT)

    base.WndProc(ref m);
} // WndProc()
#endregion
    
And finally the DrawBackgroundImage method, which copies the current editor text as an image, applies transparency to it. Then we draw our background image and the now transparent editor text on top into a helper targetBitmap, which is finally displayed on the screen at once (else we would get flickering issues). Please note that in the source code you can download there is a some testing code, e.g. for drawing bitmaps with transparency using ImageAttributes and SetColorMatrix, which works great, but is just too slow. Our approach is to pre-calculate the opacity in the backgroundImage and use that over and over again (3-4 times faster). There is still a performance penalty for all this painting, but any future optimizations will be hard. I recommend using 2 tabs, rendering is twice as fast (because only one side is updated when you type or scroll) and you should have a fast PC :)

#region DrawBackgroundImage
/// <summary>
/// This method draws the background and is called from WndProc whenever
/// it intercepts a WM_PAINT message. Again, some caching and confusing
/// optimized code is in here too, again for getting good performance.
/// </summary>
private void DrawBackgroundImage(IntPtr activeWindowHandle,
    Rectangle rect)
{
    // Create an image for storing the orginal editor screen.
    Bitmap sourceImage = new Bitmap(rect.Width, rect.Height);
    // Always create new graphics object, else we won't have current data
    Graphics sourceImageGraphics = Graphics.FromImage(sourceImage);

    // And grab current editor window content and copy it to it!
    IntPtr hdc = sourceImageGraphics.GetHdc();
    Win32.PrintWindow(activeWindowHandle, hdc, 1);
    sourceImageGraphics.ReleaseHdc(hdc);

    // Next find the color on the bottom right and use it as the
    // transparent color! Note: Antialasing will cause artifacts, make sure
    // the backgroundImage fits to the background color, e.g. by using a
    // lot of alpha transparency (<25% visibility)!
    backgroundColorPixel =
        sourceImage.GetPixel(rect.Width - 1, rect.Height - 1);
    sourceImage.MakeTransparent(backgroundColorPixel);

    // Create target image where we wanna paint to, this is important
    // because drawing directly to the VS window will produce flickering!
    Bitmap targetBitmap = new Bitmap(rect.Width, rect.Height);
    Graphics targetBitmapGraphics = Graphics.FromImage(targetBitmap);

    // Clear background with pixel color
    //not required, we draw solid image now (use this for transparent drawing):
    //targetBitmapGraphics.Clear(backgroundColorPixel);

    // Draw background image (tiled and transparent if specified)
    float transparency = Options.transparency / 100.0f;
    Image backgroundImage = GetBackgroundImage((int)activeWindowHandle,
        backgroundColorPixel, transparency);
    
    // Do the drawing as many times as we need to tile to fill everything!
    for (int y = 0; y < rect.Height; y+=backgroundImage.Height)
        for (int x = 0; x < rect.Width; x += backgroundImage.Width)
        {
            targetBitmapGraphics.DrawImage(backgroundImage, x, y);
        } // for for

    // Now draw source image on top (text and foreground stuff), else we
    // would only see our distracting background image and while that is
    // fun, we sometimes still need to be productive and see the editor
    // text ^^
    targetBitmapGraphics.DrawImage(sourceImage, 0, 0);

    // Finally draw on the VS window, but only do one single draw here
    // to make sure we do not have any flickering!
    Graphics graphics = Graphics.FromHwnd(activeWindowHandle);
    graphics.DrawImage(targetBitmap, 0, 0);

    // This is important: Validate the rect so all this can now be
    // displayed! All WM_PAINT calls during this method will
    // be handled normally (without our background painting), which is
    // used for normal updates because we mess everything up ^^
    Win32.ValidateRect(activeWindowHandle, ref rect);

    // Dispose everything we do not need anymore
    // Note: We always have to create new graphics and dispose them here,
    // else updating sourceImage, targetBitmap, etc. does not work!
    if (graphics != null)
        graphics.Dispose();
    if (sourceImageGraphics != null)
        sourceImageGraphics.Dispose();
    if (targetBitmapGraphics != null)
        targetBitmapGraphics.Dispose();
} // DrawBackgroundImage(activeWindowHandle, rect)
#endregion

For more information check out the source code, the important code for the addin is in ActiveViewBackgroundHelper.cs and the Options and PlugIn classes. The rest of the classes are just helpers for logging, string operations, random methods and Win32 helpers for some calls.

Download this addin at http://vsbackgroundchanger.codeplex.com/

I hope you will enjoy this addin. Here are 2 more screens from using this addin with different background colors and images:

White background theme (25% opacity):


Black background theme (25% opacity):