|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionWhen I read James T. Johnson's article on the Background and ResourcesThe reader should be familiar with C# programming and Using the codeThe Zip file includes the Solution called WindowsStyle. You must do the following to get the solution up and running:
DetailThe [ProvideProperty("CssClass", typeof(System.Windows.Forms.Control))]
[ProvideProperty("Stylesheet", typeof(System.Windows.Forms.Form))]
public class Style : System.ComponentModel.Component, IExtenderProvider
{
private Hashtable properties = new Hashtable();
The private class Properties
{
public string CssClass;
public string Stylesheet;
public Properties()
{
CssClass = string.Empty;
Stylesheet = string.Empty;
}
}
This class is a wrapper for all the methods we want to provide to all the controls. It holds a The only thing you have to define when implementing the public bool CanExtend(object extendee)
{
return extendee is System.Windows.Forms.Control;
}
So this has the side effect of giving every control that is on the same form with our Next, in order to actually provide properties, our [Description("Set this property to apply a class of Style to this Control")]
[Category("Style")]
public string GetCssClass(System.Windows.Forms.Control c)
{
return EnsurePropertiesExists(c).CssClass;
}
/// <summary>
/// Set the CssClass property. When this is done, automatically read the
/// Form's Stylesheet document and change this control according to the
/// CssClass it wants from that Stylesheet
/// </SUMMARY>
/// <PARAM name="c">The Control changing its CssClass property</PARAM>
/// <PARAM name="value">the new CssClass value</PARAM>
public void SetCssClass(System.Windows.Forms.Control c, string value)
{
// set the Control's CssClass property
EnsurePropertiesExists(c).CssClass = value;
// don't load the class if it's the empty string
if( value.Length < 1 )
{
return;
}
// depending on the type of control, change its style
switch( c.GetType().FullName )
{
case "System.Windows.Forms.Button":
CssButton(c);
break;
case "System.Windows.Forms.TextBox":
CssTextBox(c);
break;
default:
break;
}
}
I'll talk about the The private void CssButton(object sender)
{
System.Windows.Forms.Button b = (System.Windows.Forms.Button)sender;
Hashtable style = GetStyle(b);
if( style == null ) return;
if( style["Width"] != null )
{
b.Width = int.Parse((style["Width"]).ToString());
}
if( style["Height"] != null )
{
b.Height = int.Parse((style["Height"]).ToString());
}
if( style["ForeColor"] != null )
{
b.ForeColor = System.Drawing.Color.FromName(style["ForeColor"].ToString());
}
if( style["BackColor"] != null )
{
b.BackColor = System.Drawing.Color.FromName(style["BackColor"].ToString());
}
if( style["FlatStyle"] != null )
{
switch( style["FlatStyle"].ToString() )
{
default:
case "Standard":
b.FlatStyle = FlatStyle.Standard;
break;
case "Popup":
b.FlatStyle = FlatStyle.Popup;
break;
case "Flat":
b.FlatStyle = FlatStyle.Flat;
break;
case "System":
b.FlatStyle = FlatStyle.System;
break;
}
}
}
That Now as promised, let's look at the code that loads the stylesheet itself and returns the properties that a particular control seeks under a particular public Hashtable GetStyle( System.Windows.Forms.Control c )
{
System.Windows.Forms.Control parentForm = c.Parent;
while( parentForm != null && !(parentForm is System.Windows.Forms.Form) )
{
parentForm = parentForm.Parent;
}
if( parentForm == null ) return null;
string stylesheet = EnsurePropertiesExists(parentForm).Stylesheet;
if( stylesheet.Length < 1 || !File.Exists(stylesheet) ) return null;
XmlDocument x = new XmlDocument();
try
{
x.Load(stylesheet);
}
catch( IOException ex )
{
System.Diagnostics.Debug.Write("Error opening" +
" stylesheet document for "+c.Name+": "+ex.ToString());
return null;
}
string cssClass = EnsurePropertiesExists(c).CssClass;
XmlNodeList nodes = x.SelectNodes(string.Format("/stylesheet" +
"/class[@name='{0}']/*",cssClass));
if( nodes.Count < 1 )
{
EnsurePropertiesExists(c).CssClass = string.Empty;
throw new Exception(string.Format(
@"Stylesheet: {0}
CssClass: {1}
This style class does not exist or
does not have any properties",stylesheet,cssClass));
}
Hashtable style = new Hashtable();
foreach( XmlNode node in nodes )
{
style[node.Name] = node.InnerText.TrimEnd('\n','\r','\t',' ');
}
return style;
}
This works in a few steps. First it finds the The public void SetStylesheet(System.Windows.Forms.Form f, string value)
{
...
f.Load += new EventHandler(CssFormLoad);
}
private void CssFormLoad(object sender, EventArgs e)
{
foreach( Control c in ((Form)sender).Controls )
{
// only apply style if the Control specified a CssClass
if( EnsurePropertiesExists(c).CssClass.Length < 1 ) continue;
switch( c.GetType().FullName )
{
case "System.Windows.Forms.Button":
CssButton(c);
break;
case "System.Windows.Forms.TextBox":
CssTextBox(c);
break;
default:
break;
}
}
}
This replicates everything explained above but at runtime. Again, a better data structure certainly exists to take the place of the straightforward In closing, let's look at the format I picked for the stylesheet. Nothing fancy, stylesheet.xml file looks like this. I used it for the example illustrated at the top of this article. The image on the left is before I set any of the <stylesheet>
<class name="RedButton">
<WIDTH>40</WIDTH>
<HEIGHT>40</HEIGHT>
<FLATSTYLE>Popup</FLATSTYLE>
<BACKCOLOR>Red</BACKCOLOR>
</CLASS>
<class name="WideButton">
<WIDTH>200</WIDTH>
</CLASS>
<class name="PasswordTextBox">
<PASSWORDCHAR>#</PASSWORDCHAR>
</CLASS>
<class name="MultilineTextBox">
<MULTILINE>true</MULTILINE>
<WIDTH>200</WIDTH>
<HEIGHT>40</HEIGHT>
</CLASS>
<class name="SharedStyle">
<FORECOLOR>White</FORECOLOR>
<BACKCOLOR>Blue</BACKCOLOR>
<FLATSTYLE>Flat</FLATSTYLE>
<BORDERSTYLE>None</BORDERSTYLE>
</CLASS>
</STYLESHEET>
That's it. If any of that was confusing at all, please don't hesitate to drop me a line. This is my first try at this and I appreciate your patience for any unclear explanation. Points of InterestThis was an eye opening project for me. With a more thorough implementation, you can save many man hours spent aligning, resizing, coloring and standardizing all sorts of controls on Windows Forms. With careful data structure organization, this can be extended to allow for layout manipulation and many other uses. Currently, I think XML may be a better format for the stylesheet itself. On the other hand, one could take advantage of Firefox's open source CSS engine to parse out a typical .css file for the stylesheet. HistoryFirst submission. A basic proof of concept for applying style to Windows Forms from XML formatted stylesheets.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||