Bill Morefield My thoughts, discoveries, and occasional rambiings.

April 9, 2012

Skinning a Windows Form Application

Filed under: c#,development,windowsforms — Tags: , , , — Bill Morefield @ 9:30 am

The normal application window looks like a simple, basic rectangle.  For most uses it works quite well though providing a nice and familiar wrapper for a program.  It’s a bit boring though.  Sometimes though you create a program that doesn’t quite fit the normal square window.  Maybe you’re creating a custom display.  Whatever the reason, you are not limited to the standard window look.

I recently started updating and cleaning up a few applications I’ve written for my own use.  One of them works as a target to drag files onto and really cried out for something other than a square window.  I decided to create a nicer interface and in this post I’m going to show how to create a Windows form application with a custom look.

Step One: Tear Down the Default Form

When you skin a window, some of the things that Windows normally does for you now have to be done manually. Before we apply our custom look, we’ll start by getting rid of things we don’t want and then recreating those lost elements.  Let’s start with the form that you get when create a new Windows Form application in Visual Studio 2010. It looks like this when you run the application.

clip_image001

We want to get rid of the menu bar and those buttons in the top right window.  We can easily do this by changing the window type. In the form’s properties Change the FormBorderStyle attribute from the default Sizable to None.

clip_image002

If you run the program at this point you will get a plain grey window.  If you try to click on the window and move it, you will find it ignores your request. If you try to resize the window, that also will not work. You will also find nothing to click and exit the application.  By changing the border style to none, we also lost the built in support to move the window, close the application, and resize the window.

Step Two: Recreate Lost Functionality

We therefore need to add any of these features that we want the user to have. At the least we should give the user a way to close our program. For simplicity, I’m going to add a handler so that if someone right clicks on the window, the program exits.  In a real program, right clicking might bring up a context menu with an exit option.  The method it’s invoked doesn’t matter as this code will close your application.

        private void SkinnedWindow_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                Application.Exit();
            }
        }

If you now run the program, you will again see a simple gray square.  If you right click on the window, the application will nicely exit.

In most cases we will still want to allow the user to move our application.  I found a good way to allow moving a skinned windows is to start the move when the user presses the left mouse button while on the window. They can then move the window while they hold the left button down. When the user releases the left mouse button, the window will remain at that location.

To implement this, the application needs to handle the MouseDown, MouseMove, and MouseUp events for the form. In addition, we’ll need to keep track of when we’re moving or dragging the window and the form’s location when we start moving it. So we’ll add two variables to the form class store this information.

        private Point _offsetPoint;
        private bool _dragging;

When the user clicks the left mouse button on the window we simply want to note that we’re entering a state where the user is moving the window and store the initial location where the user clicked the mouse.

        private void SkinnedWindow_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                _offsetPoint = new Point(e.X, e.Y);
                _dragging = true;
            }
        }

In the MouseMove event we first check to see if we’re moving the window by checking the bool we set when the left button was clicked. If so then we’ll take a note of the current location of the mouse and convert that to a screen point.  We then set the form’s location to this new point which is the difference between this screen point and the original locaiton.

        private void SkinnedWindow_MouseMove(object sender, MouseEventArgs e)
        {
            if (_dragging)
            {
                Point mousePoint = new Point(e.X, e.Y);
                Point screenPoint = PointToScreen(mousePoint);
                Location = new Point(screenPoint.X - _offsetPoint.X, screenPoint.Y - _offsetPoint.Y);
            }
        }

When the user releases the left mouse button, we just need to note that we’re no longer moving the window by setting our flag to false.  Our form location is already where we want to leave the form.

        private void SkinnedWindow_MouseUp(object sender, MouseEventArgs e)
        {
            _dragging = false;
        }

If you run the program now, you’ll still see a gray window.  You can still right click and exit as before.  Now you can also move the form around. If you click inside the window and hold the mouse button down, you can drag the window around the screen. When you release the button the window will stay where it was.

In my application, I did not want the user to arbitrarily resize the window. In my application, I provided a context menu the user can select from a few predetermined sizes. When the user selects a size I set the size of the window using the Size property for the form. You could also do something similar to how we handled moving the window if you want to give the user more flexibility on resizing the window.

Step Three: The New Look

We’ve now implemented the basic functionality we’d lost so we can now focus on making the window look how we want. For this sample, I’m going to select an image from the Visual Studio image library.  I’m using the 007_PrintView_128x128_72.png image located in the library under Actions\png_format\WinVista. You can be any image, but for simplicity it should be a format that natively supports transparency for areas that are not part of the image.

clip_image004

First add the image to our project as a resource. To do this, go to the Project’s properties and select the Resources tab. Select Add Resource -> Add Existing File…. In the dialog that opens find and select the image that you wish to use for your application’s appearance. You will see a new Resources folder appear in the project and our graphic will appear under it. We can now reference this resource in our program when we want to draw the image.

In our case we want our entire window to appear as this image. Since the image I am using is 128 x 128 and I am not going to let the user resize the window, I will set the window on startup to this size. I can do this by going to the Properties of the form and setting the size to 128, 128 which the window the form to same size as the graphic that I’m using.

clip_image005

I will use this graphic as my window.  To give my form this custom look I implement the Paint event of the form.  In the Paint event, I first load the image. Here I’m doing it from the image resource.  You could also add the image as a file in the project and create the image from that file.

The code below will always resize the image to fill the window.  It changes a few settings to improve the quality of the resizing.  Since we above set our window size to 128 x 128, the same as the image, no resizing will be needed.  To see the effect of this, you can change the size of the window to a different value and the image will be scaled appropriately.

        private void SkinnedWindow_Paint(object sender, PaintEventArgs e)
        {
            // Get Image from resources
            using (Image folderImage = Resources.Network)
            {
                // Set to high quality and draw image onto background
                e.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                e.Graphics.DrawImage(folderImage, 0, 0, Width, Height);
            }
        }

We’re almost there.  If we run the application now our window appears.  We see our graphic, but they gray background from the form still shows.

clip_image006

Our last step is to get rid of the form background. We do this by setting the background color of the form to a know value. By default it is set to the Control value which can be changed by the user if they customize the appearance of Windows or use a Windows Theme. Since we want our form background to always be transparent we’ll change this to a known value. The form property we need to change is BackColor and here I’m changing the value to white.

clip_image007

Next we want to set the TransparencyKey property to match this new BackColor property. This property tells Windows to treat any part of the form that is this color as transparent and let whatever lies under it show through.

clip_image008

After this change when we run our application, we get a nice transparent window that looks just like our image. Below you can see the image placed on top of Visual Studio 2010. Notice that the transparent parts of the image are not considered part of the form. If we right click or left click and drag in the top right area just above the printer, it does not register as a click on our form and will bring up the application behind ours.

clip_image009

You can download a sample project with this code for VS 2010 .

Powered by WordPress