|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionWhen you create an application which contains a tabbed view and define a manifest file (as explained on this MSDN page), you would expect the tabbed view to display with the active theme. But it does not! Why? I searched the web, hoping to find a solution. I only found people who observed exactly the same problem as I did; and there was no solution readily available to C# programmers. Trying to set the Wrapping uxThemeIn order to draw the themed background, you have to call
All these functions degrade gracefully if there is no In my first version of this article, I implemented the wrapper functions in a managed C++ assembly, but the resulting DLL was way too large (more than 160 KB). By implementing the wrapper as an unmanaged DLL, with just very little C# glue in a separate assembly, I reduced the combined DLL size to about 35 KB, which is much more reasonable. Here is the implementation of the wrapper used to get the color of a text element using bool __stdcall Wrapper_GetTextColor (const wchar_t* name,
const wchar_t* part_name,
const wchar_t* state_name,
int* r, int* g, int* b)
{
bool ok = false;
if (xpStyle.IsAppThemed ())
{
HTHEME theme = xpStyle.OpenThemeData (NULL, name);
if (theme != NULL)
{
try
{
int part;
int state;
if (FindVisualStyle (name, part_name, state_name, part, state))
{
COLORREF color;
int prop = TMT_TEXTCOLOR;
if (S_OK == xpStyle.GetThemeColor (theme, part, state, prop, &color))
{
*r = GetRValue (color);
*g = GetGValue (color);
*b = GetBValue (color);
ok = true;
}
}
}
catch (...)
{
// Swallow any exceptions - just in case, so we don't crash the caller
// if something gets really wrong.
}
xpStyle.CloseThemeData (theme);
}
}
return ok;
}
Deriving TabPageI derived My first naive attempt was to just offset the background to match the origin of the clipping region. This worked fine as long as the view did not get partially obscured, and then partially repainted : in this case, the clipping origin no longer matched the origin of the label object and the background was, once more, painted with the wrong offset. I finally came up with a rather complex piece of code which walks trough all children, trying to identify which one's background is being erased and using the appropriate offset : public class TabPage : System.Windows.Forms.TabPage
{
public TabPage()
{
}
public bool UseTheme
{
get { return OPaC.uxTheme.Wrapper.IsAppThemed (); }
}
protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
{
if (this.UseTheme)
{
int ox = (int) e.Graphics.VisibleClipBounds.Left;
int oy = (int) e.Graphics.VisibleClipBounds.Top;
int dx = (int) e.Graphics.VisibleClipBounds.Width;
int dy = (int) e.Graphics.VisibleClipBounds.Height;
if ((ox != 0) || (oy != 0) || (dx != this.Width) || (dy != this.Height))
{
this.PaintChildrenBackground (e.Graphics, this,
new System.Drawing.Rectangle (ox, oy, dx, dy), 0, 0);
}
else
{
this.ThemedPaintBackground (e.Graphics, 0, 0,
this.Width, this.Height, 0, 0);
}
}
else
{
base.OnPaintBackground (e);
}
}
private bool PaintChildrenBackground(System.Drawing.Graphics graphics,
System.Windows.Forms.Control control,
System.Drawing.Rectangle rect,
int ofx, int ofy)
{
foreach (System.Windows.Forms.Control child in control.Controls)
{
System.Drawing.Rectangle find
= new System.Drawing.Rectangle (child.Location, child.Size);
if (find.Contains (rect))
{
System.Drawing.Rectangle child_rect = rect;
child_rect.Offset (- child.Left, - child.Top);
if (this.PaintChildrenBackground (graphics, child, child_rect,
ofx + child.Left, ofy + child.Top))
{
return true;
}
this.ThemedPaintBackground (graphics, child.Left, child.Top,
child.Width, child.Height, ofx, ofy);
return true;
}
}
return false;
}
private void ThemedPaintBackground(System.Drawing.Graphics graphics,
int ox, int oy, int dx, int dy, int ofx, int ofy)
{
System.IntPtr hdc = graphics.GetHdc ();
OPaC.uxTheme.Wrapper.DrawBackground ("TAB", "BODY", null, hdc, -ofx, -ofy,
this.Width, this.Height, ox, oy, dx, dy);
graphics.ReleaseHdc (hdc);
}
}
More trouble... GroupBox and CheckBoxI soon discovered that the After I started using my own If you are interested by the gory details, have a look at the sources... Using OPaC.Themed.Forms.TabPageTo use the code provided in this article, you must compile the unmanaged DLL ( Create your user interface and then replace To make sure your controls display as desired when used in a
Keep the default values for the Final noteI tried out every possible solution I could dream of in order to get the controls to paint properly in a tab control. Only one solution addressed all possible problems, and it is far from elegant. If only Microsoft made the sources of A helpful reader hinted me to use function HistoryThe source files were last updated on 24 July 2003.
|
||||||||||||||||||||||