Reputation: 593
I have a custom control, a Hover button, that simply shows borders whenever a mouse moves over it or got focus.
Everything is fine but I am having problem handling a situation where I click the button and it shows another form (ShowDialog) or in case of (MessageBox), and then return back to the parent form of the control.
This is the control code:
namespace CustomControlTutorial
{
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
public partial class HoverButton : Button
{
byte eventPaintMode = 0;
//Color borderClr = Color.Transparent;
public HoverButton()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
//base.OnPaint(pe);
// render background
Color clr = this.Parent.BackColor;
Brush b = new SolidBrush(clr);
pe.Graphics.FillRectangle(b, ClientRectangle);
// Draw borders depending on event case
Point p00 = new Point(0, 0);
Point p01 = new Point(0, ClientSize.Height - 1);
Point p10 = new Point(ClientSize.Width - 1, 0);
Point p11 = new Point(ClientSize.Width - 1, ClientSize.Height - 1);
Pen pen1;
Pen pen2;
switch (eventPaintMode)
{
case 1:
{
// draw borders (using pen)
pen1 = new Pen(new SolidBrush(Color.DimGray), 1);
pen2 = new Pen(new SolidBrush(Color.White), 1);
pe.Graphics.DrawLine(pen1, p10, p11);
pe.Graphics.DrawLine(pen2, p00, p10);
pe.Graphics.DrawLine(pen1, p01, p11);
pe.Graphics.DrawLine(pen2, p00, p01);
break;
}
case 2:
{
pen2 = new Pen(new SolidBrush(Color.DimGray), 1);
pen1 = new Pen(new SolidBrush(Color.White), 1);
pe.Graphics.DrawLine(pen1, p10, p11);
pe.Graphics.DrawLine(pen2, p00, p10);
pe.Graphics.DrawLine(pen1, p01, p11);
pe.Graphics.DrawLine(pen2, p00, p01);
break;
}
case 3:
{
// draw borders (using pen)
pen1 = new Pen(new SolidBrush(Color.DimGray), 1);
pen2 = new Pen(new SolidBrush(Color.White), 1);
pe.Graphics.DrawLine(pen1, p10, p11);
pe.Graphics.DrawLine(pen2, p00, p10);
pe.Graphics.DrawLine(pen1, p01, p11);
pe.Graphics.DrawLine(pen2, p00, p01);
// draw focus lines
pen1 = new Pen(new SolidBrush(Color.Black), 1);
pen1.DashCap = DashCap.Round;
pen1.DashPattern = new float[] { 1, 1, 1, 1 };
p00 = new Point(5, 5);
p01 = new Point(5, ClientSize.Height - 6);
p10 = new Point(ClientSize.Width - 6, 5);
p11 = new Point(ClientSize.Width - 6, ClientSize.Height - 6);
pe.Graphics.DrawLine(pen1, p10, p11);
pe.Graphics.DrawLine(pen1, p00, p10);
pe.Graphics.DrawLine(pen1, p01, p11);
pe.Graphics.DrawLine(pen1, p00, p01);
pe.Graphics.SmoothingMode = SmoothingMode.None;
break;
}
default:
break;
}
// render text
String drawString = this.Text;
SizeF size = pe.Graphics.MeasureString(drawString, this.Font);
float pointX = ((float)ClientSize.Width - size.Width) / 2;
float pointY = ((float)ClientSize.Height - size.Height) / 2;
pe.Graphics.DrawString(Text, Font, new SolidBrush(ForeColor), (int)pointX, (int)pointY);
b.Dispose();
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
eventPaintMode = 1;
Invalidate();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
eventPaintMode = 0;
Invalidate();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Left)
{
eventPaintMode = 2;
Invalidate();
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (e.Button == MouseButtons.Left)
{
eventPaintMode = 1;
Invalidate();
}
}
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
eventPaintMode = 3;
Invalidate();
}
protected override void OnLostFocus(EventArgs e)
{
base.OnLostFocus(e);
eventPaintMode = 0;
Invalidate();
}
}
}
And this is where I show a test message box:
private void hoverButton1_Click(object sender, EventArgs e)
{
MessageBox.Show("Test");
}
I can't find an event like, OnParentLostFocus or something like that.
Upvotes: 1
Views: 347
Reputation: 4660
The problem is that when I set the button to show a message, the button's parent (Form) can't be accessed until the message is closed. So there is no connection between the 2 forms that I can use to make any changes to the button..
This is how it works. The parent From and its children won't receive the windows paint messages to redraw themselves as long as the modal dialog is shown.
Your code creates:
As you can see, the button draws two different effects on tab stops. A hover and focus effects. While it draws the hover effect and ignores the focus one on the MouseEnter
even if the control is currently has the focus. Another thing is, the button doesn't show or draw the normal/default state when the modal dialog is closed.
IMHO, the two things shouldn't combine, interact with the mouse events to draw the hover and down effects, and draw the focus rectangle whenever the control gains the focus and the mouse effects are not applied. Just like how the default System.Windows.Forms.Button
works.
For example:
// Your namespace...
public enum MouseState : int
{
None, Over, Down
}
[DesignerCategory("Code")]
public class HoverButton : Button
{
private MouseState state = MouseState.None;
public HoverButton() : base()
{
SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.ResizeRedraw, true);
UpdateStyles();
}
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
var r = ClientRectangle;
g.Clear(Parent.BackColor);
var p00 = new Point(0, 0);
var p01 = new Point(0, r.Height - 1);
var p10 = new Point(r.Width - 1, 0);
var p11 = new Point(r.Width - 1, r.Height - 1);
switch (state)
{
case MouseState.Over:
if (r.Contains(PointToClient(MousePosition)))
{
g.DrawLine(Pens.DimGray, p10, p11);
g.DrawLine(Pens.White, p00, p10);
g.DrawLine(Pens.DimGray, p01, p11);
g.DrawLine(Pens.White, p00, p01);
}
break;
case MouseState.Down:
g.DrawLine(Pens.White, p10, p11);
g.DrawLine(Pens.DimGray, p00, p10);
g.DrawLine(Pens.White, p01, p11);
g.DrawLine(Pens.DimGray, p00, p01);
break;
default:
break;
}
TextRenderer.DrawText(g, Text, Font, r, ForeColor,
TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);
if (Focused && MouseButtons == MouseButtons.None &&
!r.Contains(PointToClient(MousePosition)))
{
r.Inflate(-2, -2);
ControlPaint.DrawFocusRectangle(g, r);
}
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
state = MouseState.Over;
Invalidate();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
state = MouseState.None;
Invalidate();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Left)
{
state = MouseState.Down;
Invalidate();
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
state = MouseState.Over;
Invalidate();
}
protected override void OnEnter(EventArgs e)
{
base.OnEnter(e);
state = MouseState.None;
Invalidate();
}
protected override void OnLeave(EventArgs e)
{
base.OnLeave(e);
state = MouseState.None;
Invalidate();
}
}
Which creates:
Side Notes
e.Graphics.Clear(..)
method to clear the drawing canvas with the desired color.e.Graphics.DrawString(..)
is better one to draw over images.Graphics.DrawString(..)
method) to dictate that along with the other text layout information.Upvotes: 1