I have a tab control which I want to customize. To be more specific, I want to change the color of the tab page header, as well as the color of that white line around the tab page (check first picture).
I thought of using a custom renderer to do this (similar to recoloring a menu strip, for example), but I'm not sure how to do this. I've also read that setting the DrawMode to OwnerDrawFixed may do this, but using this option makes the the tab control look as if my program was made in the '90s (check second picture).
What I really want to do is to keep the tabs simple and flat and change their color. Check the way tabs are in Visual Studio as an example (check third picture).
Any ideas?
Edit: Another picture of the tab page so that it's more clear what this "white line" is.
When you use OwnerDrawFixed it means you will supply the drawing code. If you did not hook up and use the DrawItem event, nothing gets drawn. This will look much the same as yours at design time, because the event is not firing. For design time painting, you'd have to subclass the control and use OnDrawItem.
// colors to use
private Color[] TColors = {Color.Salmon, Color.White, Color.LightBlue};
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
// get ref to this page
TabPage tp = ((TabControl)sender).TabPages[e.Index];
using (Brush br = new SolidBrush(TColors[e.Index]))
{
Rectangle rect = e.Bounds;
e.Graphics.FillRectangle(br, e.Bounds);
rect.Offset(1, 1);
TextRenderer.DrawText(e.Graphics, tp.Text,
tp.Font, rect, tp.ForeColor);
// draw the border
rect = e.Bounds;
rect.Offset(0, 1);
rect.Inflate(0, -1);
// ControlDark looks right for the border
using (Pen p = new Pen(SystemColors.ControlDark))
{
e.Graphics.DrawRectangle(p, rect);
}
if (e.State == DrawItemState.Selected) e.DrawFocusRectangle();
}
}
basic result:
The tab thumb looks a bit cramped to me and not as tall as the default. So, I added a TFontSize to draw the text at a different size than the Font.
Set the TabControl.Font to 10 (which seems to be plenty), so that Windows draws a slightly larger thumb/header. If you still draw the text at the default 8.25, there is more room:
private float TFontSize = 8.25F; // font drawing size
...
using (Font f = new Font(tp.Font.FontFamily,TFontSize))
{
// shift for a gutter/padding
rect.Offset(1, 1);
TextRenderer.DrawText(e.Graphics, tp.Text,
f, rect, tp.ForeColor);
}
One thing you will loose this way is the VisualStyles effect, but they would seem to clash with colored tabs anyway.
Related
I have a problem with a ToolStripStatusLabel which occurs when the BorderSides is set to All and I set a Background Color different to the owning StatusStrip Background Color: The ToolStripStatusLabels Backgroundcolor bleeds outside the border - which looks pretty ugly. I tried to set the BorderStyle property to other settings than Flat without success.
In the screenshot added below, you see the issue - the example in teal is with BorderStyle = Adjust to get the border drawn outside the rectangle. But unfortunately the border dissappears completely.
What I would like to get is no bleeding at all like in this hand-drawn example.
Is this possible to do by a setting or by inheriting or overriding a specific method of the ToolStripStatusLabel? I'm open to programmatical solutions, but I don't know where to start from, so any hints would be welcome.
Implemented Solution by combining x4rf41 and TaWs answers below
Since I made use of multiple answers which led me on the right track, I added the final solution to the question.
I extended the ToolStripStatusLabel class and overrode the OnPaint method. This gave me the possibility to make use of the classes properties and draw it as it would draw itself normally but without the bleeding.
public partial class ToolStripStatusLabelWithoutColorBleeding : ToolStripStatusLabel
{
/// <summary>
/// Bugfix to prevent bleeding of background colors outside the borders.
/// </summary>
/// <param name="e"></param>
protected override void OnPaint(PaintEventArgs e)
{
Rectangle borderRectangle = new Rectangle(0, 0, Width - 1, Height - 1);
// Background
e.Graphics.FillRectangle(new SolidBrush(BackColor), borderRectangle);
// Border (if required)
if (BorderSides != ToolStripStatusLabelBorderSides.None)
ControlPaint.DrawBorder3D(e.Graphics, borderRectangle, BorderStyle, (Border3DSide)BorderSides);
// Draw Text if you need it
e.Graphics.DrawString(Text, Font, new SolidBrush(ForeColor), 0,0);
}
}
I don't think that your problem can be solved by setting the labels properties. You have to do some custom drawing.
I don't know exactly what you are trying to do with your labels but the easiest way for custom drawing is to use the paint event of the label:
private void toolStripStatusLabel1_Paint(object sender, PaintEventArgs e)
{
// Use the sender, so that you can use the same event handler for every label
ToolStripStatusLabel label = (ToolStripStatusLabel)sender;
// Background
e.Graphics.FillRectangle(new SolidBrush(label.BackColor), e.ClipRectangle);
// Border
e.Graphics.DrawRectangle(
new Pen(label.ForeColor), // use any Color here for the border
new Rectangle(e.ClipRectangle.Location,new Size(e.ClipRectangle.Width-1,e.ClipRectangle.Height-1))
);
// Draw Text if you need it
e.Graphics.DrawString(label.Text, label.Font, new SolidBrush(label.ForeColor), e.ClipRectangle.Location);
}
this will give you your hand-drawn example if you set the BackColor of the label to magenta and the ForeColor to the right gray.
You can also extend the ToolStripStatusLabel class and override the onPaint method. The code would be pretty much the same, but you have more options in the custom class, like adding a BorderColor property or something like that.
I played around a little using ControlPaint.DrawBorder3D and found that it too has the BackColor showing as a bottom and right line.
So, similar to xfr41's answer, I tried to do owner-drawing. My idea was to use the system's routines, but to enlarge the drawing rectangle over the clipping area; this way the wrong stripes are lost altogether..
private void toolStripStatusLabel1_Paint(object sender, PaintEventArgs e)
{
Rectangle r = e.ClipRectangle;
Rectangle r2 = new Rectangle(r.X, r.Y, r.Width + 1, r.Height + 1);
ControlPaint.DrawBorder3D(e.Graphics, r2 , Border3DStyle.SunkenInner);
}
I have a listview that uses custom cell colors, but when I set a background image in the listview, the custom cell colors will not appear anymore. I tried to remove the background image temporarily (when assembling the list) and restore it after applying cell colors. This results in no custom colors but shows the background. I would like to combine these 2 listview properties if possible.
My code for setting/removing background image:
list.BackgroundImage = Properties.Resources.bgalpha;
list.BackgroundImage = null;
A part of my code for setting custom cell colors:
for (int i = 0; i < kavels.Count(); i++ )
{
if (list.Items[i].SubItems[1].Text != "0")
{
list.Items[i].UseItemStyleForSubItems = false;
list.Items[i].SubItems[1].BackColor = Color.LightGreen;
}
}
Here are two screenshots:
List view with background: http://i.imgur.com/aHUXAVh.png
List view without background: http://i.imgur.com/sO83wTP.png
I also tried making a PictureBox with a transparent background along with a png image with transparency on top of the ListView, but that also didn't work obviously.
You have two options:
You could overlay a Panel or a PictureBox with a semi-transprent Image. For this to work you would have to make it sit inside the ListView, so that it is the Parent of the overlay.
But that will make the Listview non-clickable. - Another problem with this is that it will slightly color the text, so it won't look quite right.
Or you can set the ListView to OwnerDraw = true and add code to do the drawing yourself.
Here is an example, non-scrolled and scrolled:
Note that the original BackgroundImage shines through the emtpy space to the right.
If you owner-draw a ListView in Details mode you need to code events to draw subitems and headers; note the class level variable to hold the itemHeight; this assumes they all have the same Height .. The other one is need for horizontal scrolling.
int itemHeight = 0; // we need this number!
int itemLeft = 0; // we need this number, too
private void listView1_DrawColumnHeader(object sender,
DrawListViewColumnHeaderEventArgs e)
{
Rectangle R0 = listView1.GetItemRect(0);
itemHeight = R0.Height; // we need this number!
itemLeft = R0.Left; // we need this number too
e.DrawBackground();
e.DrawText();
}
private void listView1_DrawSubItem(object sender,
DrawListViewSubItemEventArgs e)
{
Rectangle rrr = listView1.GetItemRect(e.ItemIndex);
Rectangle rect = e.Bounds;
Rectangle rect0 = new Rectangle(rect.X - itemLeft , itemHeight * e.ItemIndex,
rect.Width, rect.Height);
Image img = listView1.BackgroundImage;
e.Graphics.DrawImage(img, rect, rect0, GraphicsUnit.Pixel);
using (SolidBrush brush = new SolidBrush(e.SubItem.BackColor))
e.Graphics.FillRectangle(brush, rect);
e.DrawText();
}
Here is the code to set the colors in the ListViewItem lvi for the example:
lvi.UseItemStyleForSubItems = false;
lvi.BackColor = Color.FromArgb(66, Color.LightBlue);
lvi.SubItems[1].BackColor = Color.FromArgb(77, Color.LightGreen);
lvi.SubItems[2].BackColor = Color.FromArgb(88, Color.LightPink);
Note that the code assumes your background is one large image and no tiling is involved! Also the code works only if you don't have groups!
ObjectListView -- an open source wrapper around a standard .NET ListView -- provides ImageOverlays and true background images too. They both work with colour cells.
If you take a look at the attached image, is there a way to get the drawing logic for this hover effect from the system renderer of the standard WinForms toolstrip ?
http://imageshack.us/photo/my-images/10/toolstriphovereffect.jpg/
EDIT: Anyway, I've manually implemented this with images, but if anyone comes here with a solution, please post.
Maybe this code helps. It draws red circle with black border around toolstripbutton when mouse is over it.
Set your toolstrip properties:
//Set render mode to professional
myToolStrip.RenderMode = ToolStripRenderMode.Professional;
//Assign new instance of your custom renderer
myToolStrip.Renderer = new MyCustomRenderer();
Custom renderer class:
public class MyCustomRenderer : ToolStripProfessionalRenderer
{
protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e)
{
if (!e.Item.Selected)
base.OnRenderButtonBackground(e);
else
{
Rectangle rectangle = new Rectangle(0, 0, e.Item.Size.Width - 1, e.Item.Size.Height - 1);
//Draw red circle
e.Graphics.FillEllipse(Brushes.Red, rectangle);
//Draw black border
e.Graphics.DrawEllipse(Pens.Black, rectangle);
}
}
}
I'm customizing the appearance of a WinForms ToolTip control by responding to the Draw event. I just want some of the ToolTip's corners to be rounded. I've got everything working such that the first time the ToolTip is displayed, everything looks perfect. On subsequent displays, however, the unfilled areas of my rounded rectangle continue to have what was in the background the first time the ToolTip was displayed.
Screen shot of problem (I don't have rights to put inline apparently):
http://tinypic.com/r/30xa3w9/3
In the picture, you can see the left-over artifacts in the upper-left corner where I would like it to just be transparent (showing the gray background), like this:
tinypic.com/r/mvn8eo/3 (nor rights to add more than one link)
Here is the drawing code:
private void ToolTip_Draw(object sender, DrawToolTipEventArgs args)
{
args.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
var rect = new RectangleF(0, 0, args.Bounds.Width, args.Bounds.Height);
using (var backBrush = new LinearGradientBrush(rect, Color.Silver, this.BackColor, 90))
{
using (var path = GetRoundedRectangle(rect, 10, 4, 4, 1))
{
args.Graphics.FillPath(backBrush, path);
args.DrawText();
}
}
}
The GetRoundedRectangle function (not included) just calculates the appropriate GraphicsPath for the rounded geometry that I want.
I tried adding a call to args.DrawBackground after setting the BackColor to Color.Transparent, but that just filled in the area with the dark gray of the form's background rather than actually being transparent, which I think is the typical "simulated" transparency of WinForms.
As a side note, an non-customized ToolTip with IsBalloon set to true is non-rectangular with correct transparency.
Can anyone suggest a fix for this problem?
Control.Region is what you are looking for. You need to tell the window manager the shape of the tooltip, so background is properly redrawn.
Here is a solution, though imperfect. It uses Graphics.CopyFromScreen to copy the area under the tooltip into the background. Of course, getting the location of the tooltip isn't simple -- hence the reflection and PInvoke call to GetWindowRect.
A remaining glitch is that the background might be wrong while the the tooltip fades out. For example, if you have a button that is colored when the mouse is over it, the tooltip will still have that colored background when you move the mouse off while it fades away. Setting ToolTip.UseFading to false seems to change the frequency of background paints such that it is worse than the fading problem. If the user has disabled eye candy at the OS level, that might also trigger the same paint glitches as having UseFading set to false.
private void ToolTip_Draw2(object sender, DrawToolTipEventArgs args)
{
var graphics = args.Graphics;
var bounds = args.Bounds;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
var windowRect = GetWindowRect();
graphics.CopyFromScreen(windowRect.Left, windowRect.Top, 0, 0, new Size(bounds.Width, bounds.Height));
using (var backBrush = new LinearGradientBrush(bounds, C.Color_LogitechGray2, this.BackColor, 90))
{
using (var path = GetRoundedRectangle(bounds, 10, 4, 4, 1))
{
args.Graphics.FillPath(backBrush, path);
args.DrawText();
}
}
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
private Rectangle GetWindowRect()
{
RECT rect = new RECT();
var window = typeof(ToolTip).GetField("window", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this) as NativeWindow;
GetWindowRect(window.Handle, ref rect);
return rect;
}
Does anybody know how to change the border color of a datagridview in a windows form?
You can't, it is drawn with the color that the user selected in her preferred theme, selected in Control Panel's Display applet. Overriding the user preference is risky, but you can do so by drawing it yourself. Set the DGV's BorderStyle property to None and draw a border yourself in the form's OnPaintBackground() method. For example:
protected override void OnPaintBackground(PaintEventArgs e) {
base.OnPaintBackground(e);
Rectangle rc = new Rectangle(dataGridView1.Left - 1, dataGridView1.Top - 1,
dataGridView1.Size.Width + 1, dataGridView1.Size.Height + 1);
e.Graphics.DrawRectangle(Pens.Fuchsia, rc);
}