How can I retreive system colors related to current Aero style? I especially need the colors used in the selection gradient. SystemColors structure does not contain required colors.
Alternatively: How can I use WinAPI to draw selection on the specific canvas (Graphics object)?
OK, so the answer to the initial question is: there is no way to determine the specific colors I asked for. It is evaluated by internal theming routines provided by the OS.
Fortunately, there is a way to ask the OS to draw a themed piece of control, so called part. .NET provides two classes for drawing UI:
System.Windows.Forms.VisualStyles for themed UI and
System.Windows.Forms.ControlPaint for non-themed "Windows Classic".
Selection I asked for may be drawn by the following code:
// Graphics g = ...
VisualStyleRenderer selectionRenderer = new VisualStyleRenderer(VisualStyleElement.TreeView.Item.Selected);
selectionRenderer.DrawBackground(g, someRectangle);
Unfortunately, neither TreeView.Item.Selected, nor ListView.Item.Selected is supported by the default window theme. However, one may switch theme to the Explorer using WinAPI via p/invoke:
// C++
SetWindowTheme(hwnd, L"Explorer", nullptr);
And then P/invoke his way through few UXTheme.h routines, which works perfectly.
The system-defined color of the background of the selected items which includes the selected text and selected menu items as well is located at System.Drawing.KnownColor.Highlight
You may then use the Color struct to get a color from a KnownColor
Example
System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.Highlight);
Thanks,
Happy Holidays! :)
In VS2008 I designed a form for a C# dll. The dll is a plugin for a somewhat older app (ca. 2005): let's call it "OldApp". In VS form designer, the text in Label controls on my form is nicely rendered: antialiased and properly kerned. But when I bring up this form within OldApp (where the C# dll runs as a plugin), the text in Label controls looks ugly. It's legible, but the kerning is poor: the letters are spaced further apart and at seemingly random offsets. Anything I can do to make the text labels from within OldApp look as good as they do in VS's form designer? I doubt the specific font matters, but it's Arial, 7.2 pt (VS2008 default). I tried playing with the two relevant lines in Program.cs (see below), to no effect.
Application.EnableVisualStyles(); // tried using it and commenting it out
Application.SetCompatibleTextRenderingDefault(true); // tried true and false
I found a similar problem on MSDN forums that mentions adding the following line after the EnableVisualStyles() method.
Application.DoEvents()
Seems to be a bug in older .NET versions...which version are you using?
After an investigation I have some findings, so I'll just answer my own question:
The bad news: the old-style text rendering used by OldApp is what's causing the problem. I verified it by toggling the UseCompatibleTextRendering property for the label control in VS. The font distortion I see is the same one I see in OldApp. Which means that the Application.SetCompatibleTextRenderingDefault(false) line in my code has no effect. OldApp will ignore it and do old-style rendering anyway.
As suggested by DeviantSeev using a bigger font helps a bit. It doesn't get rid of the bad kerning, it just makes it less noticeable. I increased the font from 7.2pt to 8pt only (not 12pt), because the dialog box becomes too big otherwise. The way to do this is in the form's Font property (not the control's). This way, you'll change all controls uniformly (if their Font property is set to default).
The font sizes in VS appear to be discrete rather than continuous, or maybe there's an int() rounding off involved. Increasing the font from 7.2pt to 7.4pt results in very little change, while at 7.5pt the font makes a sudden jump in size.
Forms have an AutoScaleMode property. If it's set to Font and the form is resizeable, the form will resize in VS in proportion to the change in font size. This way, in VS you can find an acceptable middle ground between a (legible) font size and a bloated dialog. However, be careful: the auto-scale operation can suddenly go awry, for example if you change the Font units from points to pixels, inches, etc. You may suddenly end up with microscopic controls or a form bigger than your screen and hitting undo won't fix it. You really don't want to re-design your form again, so save it before any font unit change and then again when you're happy with what you see.
I have seen many other samples out there that draw smooth text on glass. But I can't use them. I need every single label that gets added at runtime to be smooth. I can't just "draw" text onto the screen.
Is this at all possible, and are there and sources around?
Thank you
Take a long at this article http://msdn.microsoft.com/en-us/magazine/cc163435.aspx#S6
It's a bit long but it answers alot of your question and alore more in regards to glass.
but the relevant part for you directly is
One particular gotcha is that
rendering a GDI item in black uses the
bit pattern 0x00000000-which also
happens to be a completely transparent
black if you are using an alpha
channel. This means that if you draw
with a black GDI brush or pen you'll
get a transparent color, not a black
one. The biggest problem this presents
is when you try to use the default
text color in a control of a text
label that sits on the glass area.
Since the default text color is
usually black, the DWM will consider
this to be transparent and the text
will be written in the glass
incorrectly. An example can be seen in
Figure 10. The first line is written
with GDI+, the second is a text label
control using the default color. As
you can see, it's nearly illegible
because it's actually incorrectly
rendered text that shows up as gray,
not black.
Happily, there are a number of ways
around this problem. Using owner-draw
controls is one. Rendering to a bitmap
that has an alpha channel is another.
Fortunately, the easiest way to get
text on controls is to let the .NET
Framework 2.0 use GDI+ for you. This
is easily accomplished by setting the
UseCompatibleTextRendering property on
your controls. By default, this
property is set to false so that
controls written for previous versions
of the .NET Framework will render the
same. But if you set it to true, your
text will come out looking correct.
You can set the property globally with
the
Application.SetUseCompatibleTextRenderingDefault
method.
He also provides example code you can place in your Main()
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(true);
Application.Run(new GlassForm());
}
But I recommend reading the article, It'll clear up alot of what's going on with Aero/Glass
Cheers,
Phyx
I have a ComboBox that I have set DrawMode = DrawMode.OwnerDrawFixed. Then I handle the OnDrawItem event and everything works perfectly. However, it looks very different from a standard ComboBox because mine doesn't seem to be rendered using VisualStyles. Do I need to do something to specifically enable VisualStyle rendering for my owner drawn control? I have tried SetWindowTheme on my control, but I'm not sure what theme class to send. Any help would be much appreciated. Thanks!
The down side of owner-draw is that when you turn it on, the owner (you) has to draw everything. You are almost completely on your own.
If you want visual styles, then you have to call the VisualStyles APIs directly to do what you want. If you want to show selected, focussed, enabled/disabled states, then you have to write code to deal with them all.
This isn't a direct answer for your combo-box issues, but as an example of how to use VisualStyles, here is how I've used VisualStyles in an owner-drawn TreeView to draw the Plus/Minus icon:
// Draw Expand (plus/minus) icon if required
if (ShowPlusMinus && e.Node.Nodes.Count > 0)
{
// Use the VisualStyles renderer to use the proper OS-defined glyphs
Rectangle expandRect = new Rectangle(iconLeft-1, midY - 7, 16, 16);
VisualStyleElement element = (e.Node.IsExpanded) ? VisualStyleElement.TreeView.Glyph.Opened
: VisualStyleElement.TreeView.Glyph.Closed;
VisualStyleRenderer renderer = new VisualStyleRenderer(element);
renderer.DrawBackground(e.Graphics, expandRect);
}
I have a library of a few "custom controls". Essentially we have our own buttons, rounder corner panels, and a few groupboxes with some custom paint. Despite the "math" in the OnPaint methods, the controls are pretty standard. Most of the time, all we do is draw the rounded corners and add gradient to the background. We use GDI+ for all that.
These controls are ok (and very nice looking according to our customers), however and despite the DoubleBuffer, you can see some redrawing, especially when there are 20++ buttons (for example) on the same form. On form load you see the buttons drawing… which is annoying.
I'm pretty sure that our buttons are not the fastest thing on earth but my question is: if double buffer is "on", shouldn't all that redraw happen in background and the Windows subsystem should show the results "instantly" ?
On the other hand, if there's "complex" foreach loop that will create labels, add them to a panel (double buffered) and change their properties, if we suspendlayout of the panel before the loop and resume layout of the panel when the loop is over, shouldn't all these controls (labels and buttons) appear "almost instantly"? This doesn't happen like that, you can see the panel being filled.
Any idea why this is not happening? I know it's hard to evaluate without sample code but that's hard to replicate too. I could make a video with a camera, but trust me on this one, it's not fast :)
We've seen this problem too.
One way we've seen to "fix" it is to completely suspend drawing of the control until we're ready to go. To accomplish this, we send the WM_SETREDRAW message to the control:
// Note that WM_SetRedraw = 0XB
// Suspend drawing.
UnsafeSharedNativeMethods.SendMessage(handle, WindowMessages.WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero);
...
// Resume drawing.
UnsafeSharedNativeMethods.SendMessage(handle, WindowMessages.WM_SETREDRAW, new IntPtr(1), IntPtr.Zero);
One of the things you should look at is whether you have set BackColor=Transparent on any of the child controls of your panels. The BackColor=Transparent will significantly degrade rendering performance especially if parent panels are using gradients.
Windows Forms does not use real transparency, rather it is uses "fake" one. Each child control paint call generates paint call on parent so parent can paint its background over which the child control paints its content so it appears transparent.
So if you have 50 child controls that will generate additional 50 paint calls on parent control for background painting. And since gradients are generally slower you will see performance degradation.
Hope this helps.
I'll approach your problem from a performance angle.
foreach loop that will create labels,
add them to a panel (double buffered)
and change their properties
If that's the order things are done, there's room for improvement. First create all your labels, change their properties, and when they are all ready, add them to the panel: Panel.Controls.AddRange(Control[])
Most of the time, all we do is draw
the rounded corners and add gradient
to the background
Are you doing the same thing over and over again? How are your gradients generated? Writing an image can't be that slow. I once had to create a 1680x1050 gradient in-memory, and it was really fast, like, too fast for Stopwatch, so drawing a gradient can't be so hard.
My advice would be to try and cache some stuff. Open Paint, draw your corners and save to disk, or generate an image in-memory just once. Then load (and resize) as needed. Same for the gradient.
Even if different buttons have different colors, but the same motif, you can create a bitmap with Paint or whatever and at runtime load it and multiply the Color values by another Color.
EDIT:
if we suspendlayout of the panel before the
loop and resume layout of the panel when the loop is over
That's not what SuspendLayout and ResumeLayout are for. They suspend the layout logic, that is, the automatic positioning of the controls. Most relevant with FlowLayoutPanel and TableLayoutPanel.
As for doublebuffering, I'm not sure it applies to custom draw code (haven't tried). I guess you should implement your own.
Doublebuffering in a nutshell:
It's very simple, a couple lines of code. On the paint event, render to a bitmap instead of rendering to the Graphics object, and then draw that bitmap to the Graphics object.
In addition to the DoubleBuffered property, also try adding this to your control's constructor:
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint, true);
And if that ends up not being enough (which I'm gonna go out on a limb and say it isn't), consider having a look at my answer to this question and suspend/resume the redraw of the panel or Form. This would let your layout operations complete, then do all of the drawing once that's done.
You may want to look at the answer to my question, How do I suspend painting for a control and its children? for a better Suspend/Resume.
It sounds like what you are looking for is a "composited" display, where the entire application is drawn all at once, almost like one big bitmap. This is what happens with WPF applications, except the "chrome" surrounding the application (things like the title bar, resize handles and scrollbars).
Note that normally, unless you've messed with some of the window styles, each Windows Form control is responsible for painting itself. That is, every control gets a crack at the WM_ PAINT, WM_ NCPAINT, WM_ERASEBKGND, etc painting related messages and handles these message independently. What this means for you is that double buffering only applies to the single control you are dealing with. To get somewhat close to a clean, composited effect, you need to concern yourself not just with your custom controls that you are drawing, but also the container controls on which they are placed. For example, if you have a Form that contains a GroupBox which in turn contains a number of custom drawn buttons, each of these controls should have there DoubleBuffered property set to True. Note that this property is protected, so this means you either end up inheriting for the various controls (just to set the double buffering property) or you use reflection to set the protected property. Also, not all Windows Form controls respect the DoubleBuffered property, as internally some of them are just wrappers around the native "common" controls.
There is a way to set a composited flag if you are targeting Windows XP (and presumably later). There is the WS_ EX_ COMPOSITED window style. I have used it before to mix results. It doesn't work well with WPF/WinForm hybrid applications and also does not play well with the DataGridView control. If you go this route, be sure you do lots of testing on different machines because I've seen strange results. In the end, I abandoned used of this approach.
Maybe first draw on a control-only 'visible' (private) buffer and then render it:
In your control
BufferedGraphicsContext gfxManager;
BufferedGraphics gfxBuffer;
Graphics gfx;
A function to install graphics
private void InstallGFX(bool forceInstall)
{
if (forceInstall || gfxManager == null)
{
gfxManager = BufferedGraphicsManager.Current;
gfxBuffer = gfxManager.Allocate(this.CreateGraphics(), new Rectangle(0, 0, Width, Height));
gfx = gfxBuffer.Graphics;
}
}
In its paint method
protected override void OnPaint(PaintEventArgs e)
{
InstallGFX(false);
// .. use GFX to draw
gfxBuffer.Render(e.Graphics);
}
In its resize method
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
InstallGFX(true); // To reallocate drawing space of new size
}
The code above has been somewhat tested.
I had the same problem with a tablelayoutpanel when switching usercontrols that I wanted displayed.
I completely got rid of the flicker by creating a class that inherited the table, then enabled doublebuffering.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
namespace myNameSpace.Forms.UserControls
{
public class TableLayoutPanelNoFlicker : TableLayoutPanel
{
public TableLayoutPanelNoFlicker()
{
this.DoubleBuffered = true;
}
}
}
I've had a lot of similar issues in the past, and the way I resolved it was to use a third-party UI suite (that is, DevExpress) rather than the standard Microsoft controls.
I started out using the Microsoft standard controls, but I found that I was constantly debugging issues which were caused by their controls. The problem is made worse by the fact that Microsoft generally does not fix any of the issues which are identified and they do very little to provide suitable workarounds.
I switched to DevExpress, and I have nothing but good things to say. The product is solid, they provide great support and documentation and yes they actually listen to their customers. Any time I had a question or an issue, I got a friendly response within 24 hours. In a couple of cases, I did find a bug and in both instances, they implemented a fix for the next service release.
I have seen bad winforms flicker on forms where the controls referred to a missing font.
This is probably not common, but it's worth looking into if you've tried everything else.