Introduction

Cascading Style Sheet (CSS) is a familiar name to all web developers. Using CSS, you can make a consistent look and feel for all the web pages, and more importantly, it provides a centralized control location which enables you to set or modify the look and feel (color, font, font size, ...) of all the web pages at once.

There is no CSS functionality defined for WinForms. There are commercial components that you can buy and integrate to your application, or you can use Windows Forms XML Parser and make the user interface by XML. But the easiest way is using inheritance.

Using visual inheritance will provide the CSS functionality for the WinForms but there are some considerations involved. In this article, you will see how inheritance works, and how you can use App.config file as a CSS.

Using the code

The following steps will show you how to make a reusable Label control:

Add a "Windows Control Library" project to the solution (call it: WinCSS).

Inside WinCSS project, right click on UserControl1.cs and rename it ErrorLabel.cs.

Inside WinCSS project, right click on ErrorLabel.cs, click on View Code, then search and replace "UserControl1" with "ErrorLabel".

Use inheritance and derive your control from one of the pre existing ones. In this case, I changed:

publicclass ErrorLabel :System.Windows.Forms.UserControl

to:

publicclass ErrorLabel :System.Windows.Forms.Label

Save the changes.

Inside WinCSS project, double click on ErrorLabel.cs. The IDE will show you a page with this message:

To add components to your class, drag them from Server Explorer or Toolbox ...

Right click on the page and choose Properties.

Use the Properties window and set any property you need. In this example, I set the Font to: Verdana, size 10, bold, and set the ForeColor to "Red".

Save all and build WinCSS project.

Inside MyWinApp project, double click on Form1.cs.

Inside the IDE toolbox, open one of the tabs, for example, "My User Controls", then right click and choose "Add/Remove Items...".

Use Browse button and point to the C:\...\WinformCSS\WinCSS\bin\Debug folder, and select WinCSS.dll. It adds all the available controls in the WinCSS.dll to the ".NET Framework Components" tab. Click OK. Now "ErrorLabel" is available in "My User Controls" tab, and you can use it in any WinForm.

Drag and drop "ErrorLabel" to Form1. Now, you have an inherited Label, its properties (Font and ForeColor) have been preset.

But wait, we are not finished yet.

To test this, open ErrorLabel.cs, use the "Properties Window", and change the color of the ErrorLabel from Red to Blue, and rebuild the solution.

If you go back to Form1, you see that the color of the label is still red. What happened?

As you can see, the ForeColor property has been set to Red, which means that it is overriding the predefined value. In other words, although the new color of ErrorLabel is Blue, Form1 shows the local value for the errorLabel1.

Solution

We need to override the control properties inside WinCSS project and make them read only. To do this:

Now, if you build the WinCSS project and go back to the Form1, you’ll see that the color is Blue.

Enhancement

Till now, in order to change the style, you needed to change the code and recompile it again. But if you want to expose this functionality and be able to change the style without changing the code and rebuild the application, you need these extra steps:

In ErrorLabel.cs file, use ConfigurationSettings.AppSettings[""] to read the values in start time and pass them to the overridden properties.

Note: In design time, you should change the settings inside the App.config, but in production, you need to change MyWinApp.exe.config file.

Summary

You can use this approach to make a library of controls such as Button, Label, RadioButton, and …, set their properties by overriding their base class properties, and make them read only. Then you can add the controls to your VS.NET toolbox and use them in your WinForm.

Any time you change the settings in your custom controls (or in the configuration file), the style of the controls in you application(s) will be updated automagically.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Comments and Discussions

In addition to changing the properties of the ErrorLabel, you can also set the defaultvalue for the property. If you do this, the designer will not override it and save it in the generated code, you would be able to change all the ErrorLabels from red to blue in one step, but still allow for custom color ErrorLabels.

Here's how.

When deciding whether to "Serialize" (ie generate and save code for) a property, the Form Editor does two things. First it checks the property for a DefaultValue attribute, which tells it the default value of the control. If it's value is different that the default, it saves code for the changed property. This only works for simple properties, not for colors and fonts. So there is a second way to set a default property. If it can't find DefaultValue, the Editor calls ShouldSerialize[PROPERTYNAME] on your control if it exists. If it returns true, it saves the property. False and it does not.

And then if we want to change our ErrorLabels to blue, we change the property grid item as described in the tutorial, and the two instances here. Recompile and our errorlabels should all be blue! AND we retain the flexibility of being able to change the color of one or two.

If it doesn't work, make sure the ForeColor property on your ErrorLabels doesn't have a bold value (which means it's serialized) which could have happened if you added ErrorLabels before it had the ShouldSerialize and Reset routines. If so, press delete on that value and it should reset back to the default color.

For an added challenge, figure out how this would work for the Font property.

Hi, I am developing a windows control library. I am adding windows forms to the control and hosting in a IE page. To test the control i have added a windows application to the solution. The problem i have is that the AppSettings reader does not read the key-value pair from the config file that i have added to the windows control library.

I have no problem when i add a config file to windows application. I am able to read the key-value pair. Do you have any suggestion as to how to make the config file work with the control library. Appreciate your reply. Thanks.

Hi,When you are using Windows Controls inside a html or aspx file, like an applet, the hosting application for the object is iExplore.exe, so the trick is to rename the config file to "iexplore.exe.config" and put the file in the root directory of your website. Make sure that the config file should have this structure:

Perfect. It worked great after i put that config file in the root folder. But what if i have more than one control library. Should i have all the Key-value pair for all the projects in the same iexplore.exe.config file.

I checked if it will work by having the config file in the same directory as the hosting webpage, but it did not work.

If you need to define different config files for each of your components, or group the keys for each section of your application, you should use a more complex logic:you need to pass the path of the config file to an start-up static method and read the sections from it:

//this method calls the Create function automatically and you can use the//read functions to retrieve the values:public static void OnApplicationStart(String myAppPath){appRoot = myAppPath;System.Configuration.ConfigurationSettings.GetConfig"AppX");System.Configuration.ConfigurationSettings.GetConfig("AppY");

}

/// <summary>/// Called from OnApplicationStart to initialize settings from/// the Web.Config file(s). /// <remarks>/// The app domain will restart if settings change, so there is /// no reason to read these values more than once. This funtion/// uses the NameValueSectionHandler base class to generate a /// hashtablefrom the XML, which is then used to store the current/// settings. Because all settings are read here, we do not actually /// store the generated hashtable object for later retrieval by/// Context.GetConfig. The application should use the accessor/// functions directly./// </remarks>/// <param name="parent">An object created by processing a section /// with this name in a Config.Web file in a parent directory./// </param>/// <param name="configContext">The config's context.</param>/// <param name="section">The section to be read.</param>/// <retvalue>/// <para>/// A ConfigOutput object: which we leave empty because all settings/// are stored at this point./// </para>/// <para>/// null: if there was an error./// </para>/// </retvalue>/// </summary>public Object Create(Object parent, object configContext, XmlNode section){

/// <summary>/// String version of ReadSetting./// <remarks>/// Reads a setting from a hashtable and converts it to the correct/// type. One of these functions is provided for each type/// expected in the hash table. These are public so that other/// classes don't have to duplicate them to read settings from/// a hash table./// </remarks>/// <param name="settings">The Hashtable to read from.</param>/// <param name="key">A key for the value in the Hashtable.</param>/// <param name="defaultValue">The default value if the item is not found.</param>/// <retvalue>/// <para>value: from the hash table</para>/// <para> /// default: if the item is not in the table or cannot be case to the expected type./// </para>/// </retvalue>/// </summary>public static String ReadSetting(NameValueCollection settings, String key, String defaultValue){ try { Object setting = settings[key];