WinForms system-renderer Toolstrip item hover effect on button controls - c#

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);
}
}
}

Related

Howto fix Backgroundcolor bleeding in bordered ToolStripStatusLabel

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);
}

Recoloring TabControl

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.

Highlight the Rectangular Area while Dragging it

I am creating a image viewer sort of application. I am on Windows and using .Net
In my app, I am trying to highlight a Particular area while dragging.
I have created a Rectangle.
Rectangle areaRect = new Rectangle(100,100, 300, 300);
Point ptOld = new Point(0, 0);
Pen rectPen = new Pen(Brushes.White, 3);
protected override void OnPaint(PaintEventArgs e)
{
Graphics dcPaint = e.Graphics;
dcPaint.DrawRectangle(rectPen, areaRect);
}
Now I am dragging this rectangular area along with my mouse movements.
protected override void OnMouseMove(MouseEventArgs e)
{
Point ptNew = new Point(e.X, e.Y);
int dx = ptNew.X - ptOld.X;
int dy = ptNew.Y - ptOld.Y;
areaRect.Offset(dx, dy);
MoveRect(ptNew);
ptOld = ptNew;
}
Here I am trying to move this rect along with my mouse
void MoveRect(Point point)
{
Graphics grfxClient = CreateGraphics();
Rectangle tempRectangle = new Rectangle(areaRect.Left, areaRect.Top, areaRect.Width, areaRect.Height);
grfxClient.DrawRectangle(rectPen, tempRectangle);
this.Invalidate();
grfxClient.Dispose();
}
My Code till this point is working fine.
Now I would like to darken the INVERSE drag Area (The area which is outside the drag region), I mean the area which is within this Rectangle should gets highlighted while dragging.
Any idea how to proceed.
Thanks.
-Pankaj
I suppose you can do it by creating a Region object that covers the outside of the rectangle and fill it with a semi-transparent SolidBrush to make it look darkened.
You also don't have to create a graphics and draw in OnMouseMove event, but just shift the rectangle and invalidate the surface of the control you are drawing on.
The code I used looks more or less like this:
Rectangle areaRect = new Rectangle(100,100, 300, 300);
Point ptOld = new Point(0, 0);
Pen rectPen = new Pen(Brushes.White, 3);
//A new field with a semi-transparent brush to paint the outside of the rectangle
Brush dimmingBrush = new SolidBrush(Color.FromArgb(128, 0, 0, 0));
protected override void OnPaint(PaintEventArgs e)
{
Region outsideRegion = new System.Drawing.Region(e.ClipRectangle);
outsideRegion.Exclude(areaRect);
Graphics dcPaint = e.Graphics;
dcPaint.FillRegion(dimmingBrush, outsideRegion);
dcPaint.DrawRectangle(rectPen, areaRect);
}
protected override void OnMouseMove(MouseEventArgs e)
{
Point ptNew = new Point(e.X, e.Y);
int dx = ptNew.X - ptOld.X;
int dy = ptNew.Y - ptOld.Y;
areaRect.Offset(dx, dy);
ptOld = ptNew;
this.Invalidate();
}
The method named MoveRect is not needed.
It now seems to work as you wanted it to.
Suggestions
I also have some suggestions. You don't have to use them, maybe they will be helpful for you.
You haven't written what kind of control you are using to draw on (or overriding Form methods and painting directly on it), but I suggest you to use a PictureBox control, create a custom control derived from it and override its events. This should make the painting process smooth and prevent flickering. To do it this way:
Create a new user control by selecting Add and User Control... and name a new control i.e. MyPictureBox
change the parent class of the control, so it should now contain the line:
public partial class MyPictureBox : PictureBox
open file MyPictureBox.Designer.cs and comment out these lines:
//this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
//this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
copy the code I posted in this answer and add line base.OnPaint(e); and the beginning of OnPaint method
compile the project
now you should be able to open designer of your main form, drag MyPictureBox control from the toolbox and use it without additional code needed
You also might consider changing the behaviour of the highlighted area, so mouse cursor was in the center of it. I suppose it would be more intuitive to the user.
If you have any issues with the code, just write it in the comments and I'll try to help :).

Clear background of transparent user control

I'm working on ImageButton, in which I paint every state(i've got several images for each state) of this button (like mouseOver, mouseDown etc.).
I've made control transparent using this code:
public ImageButton()
{
InitializeComponent();
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
}
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x20;
return parms;
}
}
But there's a problem, after few switches of state, corners becoming sharp and ugly, to solve this problem I need to clear background, but if my control is transparent then it's impossible.
I've tried this solution: Clearing the graphics of a transparent panel C#
but it's slow and makes control flickering.
Do you have any ideas how to clear this background and keep transparency of control?
Ok, I've solved this problem.
I've worked around it by setting control as not transparent and I draw canvas which is under my control, as background of my ImageButton.
Solution (in Paint event):
//gets position of button and transforms it to point on whole screen
//(because in next step we'll get screenshot of whole window [with borders etc])
Point btnpos = this.Parent.PointToScreen(new Point(Location.X, Location.Y));
//now our point will be relative to the edges of form
//[including borders, which we'll have on our bitmap]
if (this.Parent is Form)
{
btnpos.X -= this.Parent.Left;
btnpos.Y -= this.Parent.Top;
}
else
{
btnpos.X = this.Left;
btnpos.Y = this.Top;
}
//gets screenshot of whole form
Bitmap b = new Bitmap(this.Parent.Width, this.Parent.Height);
this.Parent.DrawToBitmap(b, new Rectangle(new Point(0, 0), this.Parent.Size));
//draws background (which simulates transparency)
e.Graphics.DrawImage(b,
new Rectangle(new Point(0, 0), this.Size),
new Rectangle(btnpos, this.Size),
GraphicsUnit.Pixel);
//do whatever you want to draw your stuff
PS. It doesn't work in designtime.

Transparency around non-rectangular (owner draw) ToolTip control?

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;
}

Categories

Resources