A universal way to customize the appearance of a WinForms application (using the example of FAQ.Net)

Introduction



After posting the first article about my FAQ.Net application (Windows note program) , the first users appeared who would like to further develop their note program.



To increase the interest of users, an idea arose to increase its attractiveness by adjusting the color and font.



Requirements for customizing the appearance of the application



  1. store and load properties of individual components in an XML file
  2. the user should have a simple tool for customizing the appearance
  3. the list of custom components should expand very simply (in one line of code)
  4. when changing the value of a property, the user should immediately see the changes on the form
  5. there should be a cancel button that will allow you to revert all changes back


Work result



Appearance settings window:







Before and after appearance settings:







To solve the problem it is necessary



  1. use the PropertyGridEx component to display and change component properties
  2. create a CustomDesignControl class for the convenience of storing properties in XML
  3. create a MainForm form, on which we will change the appearance
  4. create an AppSettingsForm form, on which we will configure the appearance of MainForm
  5. create the SettingsXml class for ease of working with XML


The function to undo changes in appearance



To implement the abolition of changes in appearance (clause 5 of the requirements), I decided to find a universal way to clone the properties of the components that will be created in memory and replace the displayed components.



Cloning objects:



public static class ControlExtensions { public static T Clone<T>(T controlToClone) where T : Control { PropertyInfo[] controlProperties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); T instance = Activator.CreateInstance<T>(); RichTextBox rtb = new RichTextBox(); foreach (PropertyInfo propInfo in controlProperties) { if (propInfo.Name == "Parent") continue; if (propInfo.CanWrite) { if (propInfo.Name != "WindowTarget") { propInfo.SetValue(instance, propInfo.GetValue(controlToClone, null), null); } } } return instance; }
      
      





I copied the code, removed unnecessary properties that do not need to be saved, leaving the colors and font.

After checking the operation of the code, the disadvantages of this method were immediately revealed:



  1. casting any control to the Control class is used, which means that half of the properties can be lost (for example, the DataGridView component is inherited from the Control class, but has its own additional set of properties)
  2. when cloning a component, some properties (for example: the font color inside the DataGridView cell) are not cloned, but create links to the cloned component (perhaps this is because the property is used with its internal properties and needs to be handled somehow differently).


He began to experiment on the second point, in order to achieve the cloning of the property.

To do this, create an object of the required type with the new operator.



 DataGridView dgvClone = new DataGridView();
      
      





To no avail, the component was unable to revert the changes back.



I tried then to create the property itself with the new operator and set the value to it using the SetValue method:



 propInfo.SetValue(_controls[i], propInfo.GetValue(_controlsBeforeChange[i], null), null);
      
      





And this method did not give a result. When you change the value of a property in one component, it is automatically updated in the cloned component.



I didnโ€™t look for other methods anymore and made the change return by simply restarting the application with the ability to save the working document or cancel the restart. This allowed to significantly save the application memory, since it is not necessary to create clones of objects.



Example application with customization of appearance



Just in case, Iโ€™ve prepared a ready-made code where everyone can check and possibly modify the function of canceling changes in appearance without restarting the application and tell us about the problem in the comments.

It is necessary to ensure that when you click on the "Autotest" button, the DataGridView component has a gray background and the font of the cells is small.







Download Windows application FAQ.Net (free):



(x64) yadi.sk/d/sq3g4NOD3Zt7ZY

(x86) yadi.sk/d/NKXeyUWH3Zt8PQ



Vkontakte community

Source Code FAQ.Net



Output



I hope that in many WinForms applications, thanks to this article, the end user will have the opportunity to customize the interface to their taste and color.



All Articles