I'm sure you already heard our screen designer product, Klik! DesignerLib.Net. It's based on the same base classes of the Windows Forms designer you use everyday on Visual Studio. Having the same common base, it brings you lots of advanced features you find in Visual Studio as well it's limitations, well actually a limitation of using windows to draw items into the screen...
The most advanced screen designers as of today are graphical tools such as Adobe Photoshop, where you can create layers and have them blend over each other if you play with their transparency level. In one of the custom projects where we used Klik! DesignerLib.Net, we had to have such a capability. Without going into it's details, it's was used in a part of an enterprise digital signage product where you could create content screens to be played by the players.
The player was already created with WPF and as you already know, WPF does not use controls as we know as controls
. To be more clear, in traditional windows form, every element (textbox, combo, etc.) you add into it is actually a window again where each has it's own handle. In WPF, every control you create are actually simple drawings which are drawn into the same form background. So in WPF, a centralized drawing mechanism is responsible for all those drawings into the same canvas and effects such as transparency/translucency can be supported easily, where in regular window world each window is separate on drawing it's inner elements thus there is no multi layer transparency support out of the box.
A little bit magic and some trick to get it right...
Windows Forms controls does have a style flag called SupportsTransparentBackground and with it you can achieve a partial transparency, good for a start. But what it does is a simple hack of drawing it's parent background into it's background to emulate the transparency and there is no consideration of other controls in between itself and the background. What we did is to only add this consideration and also limit to a level of controls, as otherwise with a crowded amount of controls in the screen, the application would become very unresponsive due to the amount of paint calls.
Without going any further, the code we used to do that is like that and is used in the base usercontrol for all other controls :
protected override void OnPaintBackground(PaintEventArgs e)
{
//Call our layered paint method instead of the regular one
PaintLayeredBackground(e);
}
//Paints the Layers
private void PaintLayeredBackground(PaintEventArgs e)
{
e.Graphics.SetClip(e.ClipRectangle);
//This is the call for painting the parent background
base.OnPaintBackground(e);
int StartIndex = Parent.Controls.Count-1;
int EndIndex = Parent.Controls.IndexOf(this);
//3 is our draw level
if (StartIndex - EndIndex > 3)
{
StartIndex = EndIndex + 3;
}
//Now draw 3 level of other controls which their bounds intersect with our control
for (int i = StartIndex; i > EndIndex; i--)
{
if (Bounds.IntersectsWith(Parent.Controls[i].Bounds))
{
e.Graphics.TranslateTransform(Parent.Controls[i].Left - Left, Parent.Controls[i].Top - Top);
InvokePaint(Parent.Controls[i], e);
e.Graphics.ResetTransform();
}
}
e.Graphics.ResetClip();
}
And the result is looking something like this :
Not bad ha?
We set a limitation of 3 level drawing which was good enough for us both on performance and visual wise. To simple summarize, what the code does is simply drawing the parent background like .Net Windows Forms transparency does and than drawing the foreground elements of in between controls. Thes in between controls are also choosen from the ones which their bounds intersect with the control we are actually drawng. Than, at last the control draws it's own elements on top of all these merged drawings.
We hope that you'll find it usefull and hope to see you at the next post...