Building a Complex Shape
|Figure 6: Although this looks like text printed on the desktop background, this is a shaped form. The button in the "D" gives it away.|
Now that you understand the basics, it's time to build a form with a more complex shape. This is accomplished by adding many smaller line segments together when building the GraphicsPath. The GraphicsPath
object provides a number of methods to do this, including methods to add straight lines (AddLine()
), curves (AddArc()
,...), and much, much more. There is even a method to add strings of a certain font and size (AddString()
), which results in a vector representation of the string sent to the method. Figure 6
shows a form shaped based on the outline of a string. It looks a bit like some text printing on the desktop background, but it is actually a shaped form! Listing 3
shows the code required to build this form.
So far (aside from Listing 3
), you've only seen the AddEllipse()
method, which created a self contained path that formed a full circle. In other words, it formed a "closed" path where the start-point and the end-point are the same. When you create paths out of many smaller shapes, it's important that you ultimately close that shape. Luckily, the GraphicsPath
object has methods (CloseAllFigures()
) to close shapes automatically.
|Figure 7: Windows Media Player with a classic skin.|
At this point I want to return your attention to the Media Player example. I want to show you how you can create a form like Windows Media Player using .NET Windows Forms. You probably know that Windows Media Player supports a feature called skins
that allow the user to change the shape of the application window entirely (among other things). I picked one of the classic shapes shown in Figure 7
What is interesting about this skin is not only its shape, but that it has panels that you can expand and collapse. Figure 8
shows Media Player with all panels expanded.
|Figure 8: Media Player with expanded panels.|
In order to build a similar form you need to create a fairly complex shape. As you can see in Figure 7
and Figure 8
, the form uses a large number of arcs connected by straight lines. One of the nice things about the GraphicsPath
object is that it is smart enough to connect individual segments using straight lines in case the starting point of the next segment is not the same as the end point of the previous one. Therefore, you can describe the shape by sticking (almost exclusively) to adding arcs to the path. The CreateShape()
method (Listing 4
) shows how the path is created.
By default the form is shown with its panels collapsed. Two fields on the form (bRightPanelVisible
) define whether either one of those panels is visible. If they are visible, the path is created differently to accommodate the new shape of the form. This allows you to call the CreateShape()
method at any time to generate a new shape, and then use the AssignShapePath()
method to change the shape of the form. In fact, the code uses this approach whenever the user clicks in certain areas of the form. The idea is to trap mouse clicks that occur whenever the user clicks the mouse where the handles of the panels are. When this happens, you toggle the fields to show the clicked panel in the opposite state, recreate the shape, and assign it to the form.
Note: Windows Media Player slides panels out of the main form. In our example, the panels just jump out. You could create the slide effect by changing the shape gradually. This would actually be relatively straightforward to implement. However, I decided not to show the technique here in order to keep the example to a reasonable size.
|Figure 9: Our very own form exposing behavior similar to Windows Media Player.|
OK, you've got a fancy shape but it still isn't very exciting because the window appears as an oddly shaped gray "blob" on the screen. What you need to make this look good are background images. In this example I'm going to add four different images. One with all panels collapsed, one with all of them expanded, and one each for each panel expanded on its own. The easiest way to create background images is to run the form with its "naked" shape, then take a screen shot of that form, and then use a graphics program to create the image that exactly matches the shape of the form. It is very important that the image matches exactlyotherwise there will be white pixels around the edges of your form, which makes it look unprofessional.
Most graphics programs allow you to apply textures and effects such as 3D borders. Following the procedures I described above, I created the background images used in the forms shown in Figure 9 and Figure 10. The images are pre-loaded when the form starts, and are ready to be assigned to the BackgroundImage member of the form. You do this in the SetBGImage() method that fires whenever the user clicks on a panel.
Voila! Our first complex shaped form is complete!
|Figure 10: Our complex shaped form with both panels expanded.|
Creating shaped forms is a rather straightforward task from a programmer's point of view. Things really only get tricky when the vector-based description of the form's outline gets complex. You will need a graphic artist to produce the background images for your forms unless you have a good amount of artistic talent yourself. This is especially true if you create forms that can change their shape dynamically, maybe even in an animated fashion.
Now that you know how to build shaped forms, you'll notice that standard windows controls look horribly out of place when you put them on a shaped form. Imagine the Windows Media Player with a rectangular "Play" button! Plan for the time to create more art for your buttons and other controls.
Please contact me if you have questions. Also, let me know what uses you come up with for shaped forms!