Control undo/redo move and resize - c#

I'm adding undo/redo functionality to my graphical editor, but I'm having some issues with undo/redo for move and resize.
The first thing is when I move my Control (Ellipse for example) and I click the undo button the Ellipse goes to another position than were it should go. When I press redo after the undo the Ellipse goes to the correct position.
Here is a link which will make it more clear what I mean: http://gyazo.com/d299fe5e52f08742e7fa2132f6ff9839
The second things which has a lot of agreements with the first thing is when I resize the Control (Ellipse for example) and I click undo the Ellipse has another size than it should have. After clicking redo again it has the right size again.
Here is a link which will make it more clear what I mean: http://gyazo.com/59ecdcba751ff3b8ffd053dd19cd9945
Maybe the problem is in the MoveCommand and ResizeCommand classes, my code is based on this code http://www.codeproject.com/Articles/33384/Multilevel-Undo-and-Redo-Implementation-in-Cshar. In the linked page they use WPF and Thickness is used for moving/resizing. In WinForms there's no Thickness but there's Margin/Padding which I also tried but with no luck. How to make the resizing / moving undo/redo work?
MoveCommand.cs
class MoveCommand : ICommand
{
Control _control;
int _changeOfTop;
int _changeOfLeft;
public MoveCommand(int top, int left, Control control)
{
_control = control;
_changeOfTop = top;
_changeOfLeft = left;
}
public void Execute()
{
_control.Top = _control.Top + _changeOfTop;
_control.Left = _control.Left + _changeOfLeft;
}
public void UnExecute()
{
_control.Top = _control.Top - _changeOfTop;
_control.Left = _control.Left - _changeOfLeft;
}
}
ResizeCommand.cs
class ResizeCommand : ICommand
{
private int _changeOfWidth;
private int _changeOfHeight;
private Control _control;
public ResizeCommand(int width, int height, Control control)
{
_changeOfWidth = width;
_changeOfHeight = height;
_control = control;
}
public void Execute()
{
_control.Height = _control.Height + _changeOfHeight;
_control.Width = _control.Width + _changeOfWidth;
}
public void UnExecute()
{
_control.Height = _control.Height - _changeOfHeight;
_control.Width = _control.Width - _changeOfWidth;
}
}
Ellipse.cs
class Ellipse : Control
{
private Point mDown { get; set; }
private Point conLoc { get; set; }
private bool userResizing = false;
private Form1 form =null;
public Ellipse(Form1 form1)
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
this.DoubleBuffered = true;
this.ResizeRedraw = true;
this.form = form1;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Draw a black ellipse in the rectangle represented by the control.
e.Graphics.FillEllipse(Brushes.Black, 0, 0, Width, Height);
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
mDown = e.Location;
conLoc = this.Location;
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (this.Location != conLoc)
{
Console.WriteLine("You moved me");
this.form.InsertInUnDoRedoForMove(conLoc.Y, conLoc.X, this);
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
// Call MyBase.OnMouseMove to activate the delegate.
base.OnMouseMove(e);
if (e.Button == MouseButtons.Left)
{
Location = new Point(e.X + Left - mDown.X, e.Y + Top - mDown.Y);
}
}
/* Allow resizing at the bottom right corner */
protected override void WndProc(ref Message m)
{
const int wmNcHitTest = 0x84;
const int htBottomLeft = 16;
const int htBottomRight = 17;
const int WM_EXITSIZEMOVE = 0x232;
const int WM_NCLBUTTONDWN = 0xA1;
if (m.Msg == WM_NCLBUTTONDWN)
{
if (!userResizing)
{
userResizing = true;
Console.WriteLine("Start Resizing");
this.form.InsertInUnDoRedoForResize(this.Width, this.Height, this);
}
}
else if (m.Msg == WM_EXITSIZEMOVE)
{
if (userResizing)
{
userResizing = false;
Console.WriteLine("Finish Resizing");
}
}
else if (m.Msg == wmNcHitTest)
{
int x = (int)(m.LParam.ToInt64() & 0xFFFF);
int y = (int)((m.LParam.ToInt64() & 0xFFFF0000) >> 16);
Point pt = PointToClient(new Point(x, y));
Size clientSize = ClientSize;
if (pt.X >= clientSize.Width - 16 &&
pt.Y >= clientSize.Height - 16 &&
clientSize.Height >= 16)
{
m.Result = (IntPtr)(IsMirrored ? htBottomLeft : htBottomRight);
return;
}
}
base.WndProc(ref m);
}
}
Form1.cs
public partial class Form1 : Form
{
private bool draw;
private int x, y, xe, ye;
private Stack<ICommand> _Undocommands = new Stack<ICommand>();
private Stack<ICommand> _Redocommands = new Stack<ICommand>();
public Form1()
{
InitializeComponent();
menuComboBoxShape.ComboBox.DataSource = Enum.GetValues(typeof(Item));
}
public enum Item
{
Pencil,
Rectangle,
Ellipse,
}
private void panel_MouseDown(object sender, MouseEventArgs e)
{
draw = true;
x = e.X;
y = e.Y;
}
private void panel_MouseUp(object sender, MouseEventArgs e)
{
draw = false;
xe = e.X;
ye = e.Y;
Item item;
Enum.TryParse<Item>(menuComboBoxShape.ComboBox.SelectedValue.ToString(), out item);
switch (item)
{
case Item.Pencil:
using (Graphics g = panel.CreateGraphics())
using (var pen = new Pen(System.Drawing.Color.Black)) //Create the pen used to draw the line (using statement makes sure the pen is disposed)
{
g.DrawLine(pen, new Point(x, y), new Point(xe, ye));
}
break;
case Item.Rectangle:
var box = new Box(this);
panel.Controls.Add(box);
box.Location = new Point(x, y);
box.Width = (xe - x);
box.Height = (ye - y);
//InsertCommand boxcmd = new InsertCommand(box, panel);
InsertInUnDoRedoForInsert(box);
break;
case Item.Ellipse:
var el = new Ellipse(this);
panel.Controls.Add(el);
el.Location = new Point(x, y);
el.Width = (xe - x);
el.Height = (ye - y);
//InsertCommand elcmd = new InsertCommand(el,panel);
InsertInUnDoRedoForInsert(el);
break;
default:
break;
}
}
private void undoButton_Click(object sender, EventArgs e)
{
Undo(1);
}
private void redoButton_Click(object sender, EventArgs e)
{
Redo(1);
}
private void clearAllButton_Click(object sender, EventArgs e)
{
commandList.Clear();
current = 0;
panel.Controls.Clear();
}
public void Redo(int levels)
{
for (int i = 1; i <= levels; i++)
{
if (_Redocommands.Count != 0)
{
ICommand command = _Redocommands.Pop();
command.Execute();
_Undocommands.Push(command);
}
Invalidate();
}
}
public void Undo(int levels)
{
for (int i = 1; i <= levels; i++)
{
if (_Undocommands.Count != 0)
{
ICommand command = _Undocommands.Pop();
command.UnExecute();
_Redocommands.Push(command);
}
Invalidate();
}
}
#region UndoHelperFunctions
public void InsertInUnDoRedoForInsert(Control control)
{
ICommand cmd = new InsertCommand(control, panel,shapeTreeView);
_Undocommands.Push(cmd); _Redocommands.Clear();
}
public void InsertInUnDoRedoForResize
(int width, int height, Control control)
{
ICommand cmd = new ResizeCommand(width, height, control);
_Undocommands.Push(cmd); _Redocommands.Clear();
}
public void InsertInUnDoRedoForMove
(int top, int left, Control control)
{
ICommand cmd = new MoveCommand(top,left,control);
_Undocommands.Push(cmd); _Redocommands.Clear();
}
#endregion
}

Related

Custom checkBox problem in Visual Studio 2019 using C#

I found on the internet how to create your own checkbox. Unfortunately, there is a problem with the fact that you have to click very slowly to react to a change, and when I click on the text, nothing changes. I presented the whole action in the film. Please help.
https://www.youtube.com/watch?v=s6xmVAoUVJ8
Here are the files: https://drive.google.com/file/d/10pj6DRjCvfQc8_s0rUufJpN1kdft2sml/view?usp=sharing
CheckBox.cs:
class NowyCheckbox : Control
{
#region Private members
public bool IsChecked = false;
private Label CheckBoxLabel;
private Rectangle CheckBoxRectangle;
private bool MouseOver = false;
#endregion
#region Public Members (in Attributes)
private Color CheckBoxCharColorValue = Color.FromArgb(0, 0, 0);
public Color CheckBoxCharColor
{
get
{
return CheckBoxCharColorValue;
}
set
{
if (CheckBoxCharColor != value)
{
CheckBoxCharColorValue = value;
Invalidate();
}
}
}
private Color CheckBoxCharHighlightColorValue = Color.FromArgb(0, 120, 215);
public Color CheckBoxCharHighlightColor
{
get
{
return CheckBoxCharHighlightColorValue;
}
set
{
if (CheckBoxCharHighlightColor != value)
{
CheckBoxCharHighlightColorValue = value;
Invalidate();
}
}
}
private Font CheckBoxCharFontValue;
public Font CheckBoxCharFont
{
get
{
return CheckBoxCharFontValue;
}
set
{
if (CheckBoxCharFont != value)
{
CheckBoxCharFontValue = value;
Invalidate();
}
}
}
private string CheckBoxCharValue = "!";
public string CheckBoxChar
{
get
{
return CheckBoxCharValue;
}
set
{
if (CheckBoxChar != value)
{
CheckBoxCharValue = value;
RefreshLabel();
}
}
}
private Font CheckBoxFontValue;
public Font CheckBoxFont
{
get
{
return CheckBoxFontValue;
}
set
{
if (CheckBoxFont != value)
{
CheckBoxFontValue = value;
RefreshLabel();
}
}
}
private string CheckBoxTextValue = "Nowy CheckBox";
public string CheckBoxText
{
get
{
return CheckBoxTextValue;
}
set
{
if (CheckBoxText != value)
{
CheckBoxTextValue = value;
RefreshLabel();
}
}
}
private int CheckBoxSizeValue = 12;
public int CheckBoxSize
{
get
{
return CheckBoxSizeValue;
}
set
{
if (CheckBoxSize != value)
{
CheckBoxSizeValue = value;
RefreshLabel();
Invalidate();
}
}
}
private int CheckBoxFrameStrengthValue = 1;
public int CheckBoxFrameStrength
{
get
{
return CheckBoxFrameStrengthValue;
}
set
{
if (CheckBoxFrameStrengthValue != value)
{
CheckBoxFrameStrengthValue = value;
Invalidate();
}
}
}
#region Public Member
private int CheckBoxCharOffsetXValue = 0;
public int CheckBoxCharOffsetX
{
get
{
return CheckBoxCharOffsetXValue;
}
set
{
if (CheckBoxCharOffsetX != value)
{
CheckBoxCharOffsetXValue = value;
Invalidate();
}
}
}
private int CheckBoxCharOffsetYValue = 0;
public int CheckBoxCharOffsetY
{
get
{
return CheckBoxCharOffsetYValue;
}
set
{
if (CheckBoxCharOffsetY != value)
{
CheckBoxCharOffsetYValue = value;
Invalidate();
}
}
}
private int CheckBoxOffsetXValue = 0;
public int CheckBoxOffsetX
{
get
{
return CheckBoxOffsetXValue;
}
set
{
if (CheckBoxOffsetX != value)
{
CheckBoxOffsetXValue = value;
RefreshLabel();
}
}
}
private int CheckBoxOffsetYValue = 0;
public int CheckBoxOffsetY
{
get
{
return CheckBoxOffsetYValue;
}
set
{
if (CheckBoxOffsetY != value)
{
CheckBoxOffsetYValue = value;
RefreshLabel();
}
}
}
#endregion
#region Public Colors
private Color CheckBoxFrameColorValue = Color.FromArgb(0, 0, 0);
public Color CheckBoxFrameColor
{
get
{
return CheckBoxFrameColorValue;
}
set
{
if (CheckBoxFrameColorValue != value)
{
CheckBoxFrameColorValue = value;
Invalidate();
}
}
}
private Color CheckBoxFrameHighlightColorValue = Color.FromArgb(0, 120, 250);
public Color CheckBoxFrameHighlightColor
{
get
{
return CheckBoxFrameHighlightColorValue;
}
set
{
if (CheckBoxFrameHighlightColorValue != value)
{
CheckBoxFrameHighlightColorValue = value;
Invalidate();
}
}
}
private Color CheckBoxBackColorValue = Color.FromArgb(255, 255, 255);
public Color CheckBoxBackColor
{
get
{
return CheckBoxBackColorValue;
}
set
{
if (CheckBoxBackColorValue != value)
{
CheckBoxBackColorValue = value;
Invalidate();
}
}
}
private Color CheckBoxBackHighlightColorValue = Color.FromArgb(255, 255, 255);
public Color CheckBoxBackHighlightColor
{
get
{
return CheckBoxBackHighlightColorValue;
}
set
{
if (CheckBoxBackHighlightColorValue != value)
{
CheckBoxBackHighlightColorValue = value;
Invalidate();
}
}
}
private Color CheckBoxForeHighlightColorValue = Color.FromArgb(0, 0, 0);
public Color CheckBoxForeHighlightColor
{
get
{
return CheckBoxForeHighlightColorValue;
}
set
{
if (CheckBoxForeHighlightColorValue != value)
{
CheckBoxForeHighlightColorValue = value;
RefreshLabel();
}
}
}
private Color CheckBoxForeColorValue = Color.FromArgb(0, 0, 0);
public Color CheckBoxForeColor
{
get
{
return CheckBoxForeColorValue;
}
set
{
if (CheckBoxForeColorValue != value)
{
CheckBoxForeColorValue = value;
RefreshLabel();
}
}
}
#endregion
#endregion
#region Constructor
public NowyCheckbox()
{
DoubleBuffered = true;
Size = new Size(112, 18);
CheckBoxFont = this.Font;
CheckBoxCharFont = this.Font;
var midHeight = 8 - Font.Height / 2;
CheckBoxLabel = new Label()
{
Font = CheckBoxFont,
Size = new Size(this.Width - 16, this.Height),
Location = new Point(16, midHeight),
Text = CheckBoxText
};
Controls.Add(CheckBoxLabel);
CreateMouseEvents();
}
#endregion
#region Create mouse events
private void CreateMouseEvents()
{
MouseEnter += (sender, e) =>
{
OnCustomMouseEnter(e);
};
MouseLeave += (sender, e) =>
{
OnCustomMouseLeave(e);
};
MouseDown += (sender, e) =>
{
OnCustomMouseDown(e);
};
CheckBoxLabel.MouseEnter += (sender, e) =>
{
OnCustomMouseEnter(e);
};
CheckBoxLabel.MouseLeave += (sender, e) =>
{
OnCustomMouseLeave(e);
};
CheckBoxLabel.MouseDown += (sender, e) =>
{
OnCustomMouseDown(e);
};
}
#endregion
#region Mouse Events
private void OnCustomMouseDown(EventArgs e)
{
IsChecked = !IsChecked;
Invalidate();
}
private void OnCustomMouseEnter(EventArgs e)
{
if (MouseOver == false)
{
MouseOver = true;
Invalidate();
RefreshLabel();
}
}
private void OnCustomMouseLeave(EventArgs e)
{
if (MouseOver == true)
{
MouseOver = false;
Invalidate();
RefreshLabel();
}
}
#endregion
#region Paint NowyCheckbox
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
PaintRectangle(e);
if (IsChecked == true)
{
PaintArrowChar(e);
}
}
#endregion
#region Paint NowyCheckboxRectangle
private void PaintRectangle(PaintEventArgs e)
{
var midHeight = Height / 2 - CheckBoxSize / 2;
CheckBoxRectangle = new Rectangle(0, midHeight, CheckBoxSize, CheckBoxSize);
var fillColor = MouseOver == true ? CheckBoxBackHighlightColor : CheckBoxBackColor;
var frameColor = MouseOver == true ? CheckBoxFrameHighlightColor : CheckBoxFrameColor;
using (var pen = new Pen(frameColor, CheckBoxFrameStrength))
{
var brush = new SolidBrush(fillColor);
e.Graphics.FillRectangle(brush, CheckBoxRectangle);
e.Graphics.DrawRectangle(pen, CheckBoxRectangle);
}
}
#endregion
#region Paint Checkbox Arrow
private void PaintArrowChar(PaintEventArgs e)
{
var charColor = MouseOver == true ? CheckBoxCharHighlightColor : CheckBoxCharColor;
var midX = CheckBoxSize / 2 - 3 + CheckBoxCharOffsetX;
var midY = Height / 2 - CheckBoxCharFont.Height / 2 + CheckBoxCharOffsetY;
using (var brush = new SolidBrush(charColor))
{
e.Graphics.DrawString(CheckBoxChar, CheckBoxCharFont, brush, new Point(midX, midY));
}
}
#endregion
#region [OnResize]
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
RefreshLabel();
}
#endregion
#region Refresh Label
private void RefreshLabel()
{
if (CheckBoxLabel == null) return;
CheckBoxLabel.Font = CheckBoxFont;
CheckBoxLabel.Text = CheckBoxText;
CheckBoxLabel.ForeColor = MouseOver == true ? CheckBoxForeHighlightColor : CheckBoxForeColor;
var offsetWidth = Width - CheckBoxSize;
CheckBoxLabel.Size = new Size(offsetWidth, Height);
var offsetX = CheckBoxSize + 6 + CheckBoxOffsetX;
var midHeight = Height / 2 - CheckBoxFont.Height / 2 + CheckBoxOffsetY;
CheckBoxLabel.Location = new Point(offsetX, midHeight);
Invalidate();
}
#endregion
}
}
Form1.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Test
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void nowyCheckbox1_Click(object sender, EventArgs e)
{
if (nowyCheckbox1.IsChecked)
{
label2.Text = "Jestem włączony";
}
else
{
label2.Text = "Jestem wyłączony";
}
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
label3.Text = "Jestem włączony";
}
else
{
label3.Text = "Jestem wyłączony";
}
}
}
An object like your custom check box, should have a mechanism used to notify a subscriber (like a Form) that something has happened. Notifications like mouse clicks, keystrokes, property changes...etc. In event-driven programming, your control should generate and publish an event to be received and handled by a subscriber through an Event Handler. Your custom control is missing that mechanism and this is the problem.
Let's apply that and create a simple check box control.
[DefaultProperty("Checked")]
[DefaultEvent("CheckedChanged")]
[DesignerCategory("Code")]
[ToolboxBitmap(typeof(CheckBox))]
public class NowyCheckBox : Control
{
Class members to keep:
The bounds of the check box and text areas.
The points of a check mark.
The current mouse state.
private enum MouseState : int
{
None,
Over,
Down
}
private Rectangle ChkRec, TxtRec;
private PointF[] ChkPoints;
private MouseState mouseState = MouseState.None;
In the constructor, set the shown below styles to reduce the flickering and to use the control in a container with a background image or gradient colors.
public NowyCheckBox() : base()
{
SetStyle(
ControlStyles.SupportsTransparentBackColor |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw, true);
UpdateStyles();
}
Add a method to create the drawing objects. Call it from the setters of the relevant properties
(such as CheckBoxSize) and methods (like OnSizeChanged) as shown next.
private void ResetDrawingObjects()
{
if (Width == 0 || Height == 0) return;
ChkRec = new Rectangle(new Point(0, (Height - checkBoxSize.Height) / 2), CheckBoxSize);
TxtRec = new Rectangle(ChkRec.Right + 5, 0, Width - ChkRec.Right - 10, Height);
if (this.RightToLeft == RightToLeft.Yes)
{
ChkRec.X = Width - 1 - ChkRec.Width;
TxtRec.X = ChkRec.X - 5 - TxtRec.Width;
}
var r = Rectangle.Inflate(ChkRec, -3, -3);
ChkPoints = new[]
{
new PointF(r.X, r.Y + (r.Height / 2f)),
new PointF(r.X + (r.Width / 2f) - 1, r.Bottom),
new PointF(r.Right, r.Y)
};
Invalidate();
}
Create a custom event and raise it whenever the Checked property changes.
public event EventHandler CheckedChanged;
protected virtual void OnCheckedChanged(EventArgs e) => CheckedChanged?.Invoke(this, e);
Add the properties.
private bool _checked;
[DefaultValue(false)]
public bool Checked
{
get => _checked;
set
{
if (_checked != value)
{
_checked = value;
Invalidate();
OnCheckedChanged(new EventArgs());
}
}
}
private Size checkBoxSize = new Size(12, 12);
[DefaultValue(typeof(Size), "12, 12")]
public Size CheckBoxSize
{
get => checkBoxSize;
set { checkBoxSize = value; ResetDrawingObjects(); }
}
// Add the rest...
Override the following methods to:
Update the mouseState variable and refresh the drawing by calling the Invalidate method if needed.
Call the ResetDrawingObjects method whenever the drawing objects need to resize and/or reposition.
Like the default CheckBox control, toggle the Checked property on Keys.Space press.
Draw the control.
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (mouseState != MouseState.Over)
{
mouseState = MouseState.Over;
// Pass ChkRec to update the checkbox area only.
Invalidate(ChkRec);
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Left)
{
Focus();
mouseState = MouseState.Down;
Invalidate(ChkRec);
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
Checked = !Checked;
mouseState = MouseState.Over;
Invalidate(ChkRec);
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
mouseState = MouseState.None;
Invalidate(ChkRec);
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.KeyCode == Keys.Space) Checked = !Checked;
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
Invalidate();
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
ResetDrawingObjects();
}
protected override void OnRightToLeftChanged(EventArgs e)
{
base.OnRightToLeftChanged(e);
ResetDrawingObjects();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var g = e.Graphics;
// Get your colors to fill and draw...
var clr = mouseState == MouseState.Down
? // down color...
: mouseState == MouseState.Over
? // over color...
: // default color...;
using (var pn = new Pen(clr)) g.DrawRectangle(pn, ChkRec);
if (Checked)
{
clr = mouseState == MouseState.Down
? // down color...
: mouseState == MouseState.Over
? // over color...
: // default color...;
using (var pn = new Pen(clr, 2))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.DrawLines(pn, ChkPoints);
g.SmoothingMode = SmoothingMode.None;
}
/*
* In case you prefere drawing glyphs. Example:
using (var fnt = new Font("Marlett", 12))
{
var r = ChkRec;
r.Offset(2, -2);
TextRenderer.DrawText(g, "a", fnt, r, clr,
TextFormatFlags.HorizontalCenter |
TextFormatFlags.VerticalCenter);
}
*/
}
var flags = TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis;
if (this.RightToLeft == RightToLeft.Yes)
flags |= TextFormatFlags.Right;
else
flags |= TextFormatFlags.Left;
TextRenderer.DrawText(g, Text, Font, TxtRec, ForeColor, flags);
}
}
Put it together, rebuild, drop an instance, double click on it to subscribe to the default event CheckedChanged, and handle it as you want.
private void nowyCheckBox1_CheckedChanged(object sender, EventArgs e)
{
label2.Text = nowyCheckBox1.Checked
? "Jestem włączony"
: "Jestem wyłączony";
}
If you'd like to read more.
Handle and raise events
C# - Events

How can I combine marquee scrolling and transparency into a label control

I have found the following two label controls that separately handle marquee scrolling of text in a label, and also for the label to be partly transparent. They work very well separately, but I'm having trouble combining them into one control given my limited C#.
Can anyone give me some clues?
Transparent label:
public class LabelTransparent : Label
{
private int opacity;
public Color clrTransparentColor;
public LabelTransparent()
{
this.clrTransparentColor = Color.Blue;
this.opacity = 50;
this.BackColor = Color.Transparent;
}
protected override void OnPaint(PaintEventArgs e)
{
if (Parent != null)
{
using (var bmp = new Bitmap(Parent.Width, Parent.Height))
{
Parent.Controls.Cast<Control>()
.Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
.Where(c => c.Bounds.IntersectsWith(this.Bounds))
.OrderByDescending(c => Parent.Controls.GetChildIndex(c))
.ToList()
.ForEach(c => c.DrawToBitmap(bmp, c.Bounds));
e.Graphics.DrawImage(bmp, -Left, -Top);
using (var b = new SolidBrush(Color.FromArgb(this.Opacity, this.TransparentBackColor)))
{
e.Graphics.FillRectangle(b, this.ClientRectangle);
}
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
TextRenderer.DrawText(e.Graphics, this.Text, this.Font, this.ClientRectangle, this.ForeColor, Color.Transparent);
}
}
}
public int Opacity
{
get { return opacity; }
set { if (value >= 0 && value <= 255) opacity = value; this.Invalidate(); }
}
public Color TransparentBackColor
{
get { return clrTransparentColor; }
set { clrTransparentColor = value; this.Invalidate(); }
}
[Browsable(false)]
public override Color BackColor
{
get { return Color.Transparent; }
set { base.BackColor = Color.Transparent; }
}
}
Marquee scrolling:
public class LabelMarquee : Label
{
private int CurrentPosition { get; set; }
private Timer Timer = new Timer();
private Graphics grText;
private float fTextPixels;
public int ScrollSpeed
{
get { return this.Timer.Interval; }
set { try { this.Timer.Interval = value; } catch (Exception) { this.Timer.Interval = 15; } }
}
public LabelMarquee()
{
UseCompatibleTextRendering = true;
grText = this.CreateGraphics();
Timer.Tick += new EventHandler(Timer_Tick);
Timer.Start();
}
void Timer_Tick(object sender, EventArgs e)
{
fTextPixels = grText.MeasureString(this.Text, this.Font).Width;
if (CurrentPosition < -this.fTextPixels) CurrentPosition = Width;
else CurrentPosition -= 1;
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.TranslateTransform((float)CurrentPosition, 0);
base.OnPaint(e);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (Timer != null)
Timer.Dispose();
}
Timer = null;
}
}

How to initialize collapsed GroupBox in c#?

I have customized the GroupBox to make it collapsable/expandable.
Code is working fine. But I want to initialize this GroupBox in collapsed form instead of expendable. I tried to set values (m_collapsed = true/false and m_bResizingFromCollapse = true/false)and override OnPaint, but nothing is working.
namespace ABC
{
/// <summary>
/// GroupBox control that provides functionality to
/// allow it to be collapsed.
/// </summary>
[ToolboxBitmap(typeof(CollapsibleGroupBox))]
public partial class CollapsibleGroupBox : GroupBox
{
#region Fields
private Rectangle m_toggleRect = new Rectangle(8, 2, 11, 11);
private Boolean m_collapsed = false;
private Boolean m_bResizingFromCollapse = false;
private const int m_collapsedHeight = 20;
private Size m_FullSize = Size.Empty;
#endregion
#region Events & Delegates
/// <summary>Fired when the Collapse Toggle button is pressed</summary>
public delegate void CollapseBoxClickedEventHandler(object sender);
public event CollapseBoxClickedEventHandler CollapseBoxClickedEvent;
#endregion
#region Constructor
public CollapsibleGroupBox()
{
InitializeComponent();
}
#endregion
#region Public Properties
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int FullHeight
{
get { return m_FullSize.Height; }
}
[DefaultValue(false), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool IsCollapsed
{
get { return m_collapsed; }
set
{
if (value != m_collapsed)
{
m_collapsed = value;
if (!value)
// Expand
this.Size = m_FullSize;
else
{
// Collapse
m_bResizingFromCollapse = true;
this.Height = m_collapsedHeight;
m_bResizingFromCollapse = false;
}
foreach (Control c in Controls)
c.Visible = !value;
Invalidate();
}
}
}
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int CollapsedHeight
{
get { return m_collapsedHeight; }
}
#endregion
#region Overrides
protected override void OnMouseUp(MouseEventArgs e)
{
if (m_toggleRect.Contains(e.Location))
ToggleCollapsed();
else
base.OnMouseUp(e);
}
protected override void OnPaint(PaintEventArgs e)
{
HandleResize();
DrawGroupBox(e.Graphics);
DrawToggleButton(e.Graphics);
}
#endregion
#region Implimentation
void DrawGroupBox(Graphics g)
{
// Get windows to draw the GroupBox
Rectangle bounds = new Rectangle(ClientRectangle.X, ClientRectangle.Y + 6, ClientRectangle.Width, ClientRectangle.Height - 6);
GroupBoxRenderer.DrawGroupBox(g, bounds, Enabled ? GroupBoxState.Normal : GroupBoxState.Disabled);
// Text Formating positioning & Size
StringFormat sf = new StringFormat();
int i_textPos = (bounds.X + 8) + m_toggleRect.Width + 2;
int i_textSize = (int)g.MeasureString(Text, this.Font).Width;
i_textSize = i_textSize < 1 ? 1 : i_textSize;
int i_endPos = i_textPos + i_textSize + 1;
// Draw a line to cover the GroupBox border where the text will sit
g.DrawLine(SystemPens.Control, i_textPos, bounds.Y, i_endPos, bounds.Y);
// Draw the GroupBox text
using (SolidBrush drawBrush = new SolidBrush(Color.FromArgb(0, 70, 213)))
g.DrawString(Text, this.Font, drawBrush, i_textPos, 0);
}
void DrawToggleButton(Graphics g)
{
if (IsCollapsed)
{
Image plusImage = Image.FromFile(Path.Combine(GameManager.sAssetsDir, "plus.bmp"));
g.DrawImage(plusImage, m_toggleRect);
}
else
{
Image minusImage = Image.FromFile(Path.Combine(GameManager.sAssetsDir, "minus.bmp"));
g.DrawImage(minusImage, m_toggleRect);
}
}
void ToggleCollapsed()
{
IsCollapsed = !IsCollapsed;
if (CollapseBoxClickedEvent != null)
CollapseBoxClickedEvent(this);
}
void HandleResize()
{
if (!m_bResizingFromCollapse && !m_collapsed)
m_FullSize = this.Size;
}
#endregion
}
}

Children of a UserControl disappear after moving

I've got something like a serious problem.
I made a UserControl which is movable (like a window) in its parent, by using the MouseDown, MouseMove,MouseUp events .
The [Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))] Attribute is used that I can add Controls to this UserControl in VS's Designer.
State:
Moving those UserControls works fine (the Usercontrol moves as expected...)
Controls can be added in VS's Designer and appear as designed in Runtime[visible, like it should be]
throught Moving the UserControl, the Children get invisible, but .Visible=true doesn't change
.BringToFront(); has no affect (I thought they might be behind the container)
Here's the UserControl class:
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
public partial class MovableContainer : UserControl
{
bool mdown = false;
Point mpos;
[EditorBrowsable(EditorBrowsableState.Always)]
[SettingsBindable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Axis Rasta { get; set; }
public static int DefautlRasta = 10;
public MovableContainer()
{
rasta = DefautlRasta;
InitializeComponent();
this.MouseDown += ((object o, MouseEventArgs e) =>
{
mdown = true;
mpos = this.PointToClient(MousePosition);
});
this.MouseUp += ((object o, MouseEventArgs e) => mdown = false);
this.MouseMove += MovableContainer_MouseMove;
this.Paint += (object o, PaintEventArgs e) =>
{
Console.WriteLine("BTF");
this.Parent.Controls.OfType<Control>().ToList().ForEach(x => x.BringToFront());
this.Controls.OfType<Control>().ToList().ForEach(x => x.BringToFront());
this.Controls.OfType<Control>().ToList().ForEach(x => x.Show());
};
this.ParentChanged += ((object o, EventArgs e) =>
{
if (this.Parent == null)
{
try { this.Parent.SizeChanged -= Parent_SizeChanged; }
catch { }
}
else
{
try { this.Parent.SizeChanged += Parent_SizeChanged; }
catch { }
}
}
);
// this.KeyDown += ((object o, KeyEventArgs e) => {
///kdown = (RastaKey == e.KeyCode); Console.WriteLine("K:"+kdown);
//});
//this.KeyUp += ((object o, KeyEventArgs e) => kdown = false);
}
void Parent_SizeChanged(object sender, EventArgs e)
{
this.Boundis = new Rectangle(Parent.Padding.Left, Parent.Padding.Top, Parent.Size.Width - Parent.Padding.Horizontal, Parent.Size.Height - Parent.Padding.Vertical);
{
this.Location = this.Location.Add(this.PointToClient(MousePosition).Sub(mpos)).Rasta(Rasta, rasta);
Rectangle rct = new Rectangle(this.Location, this.Size);
if (this.Boundis.X > rct.X)
{
this.Location = new Point(this.Boundis.X, this.Location.Y);
Console.Write("R");
}
//left
if (this.Boundis.Right < rct.Right)
{
this.Location = new Point(this.Boundis.Right - rct.Width, rct.Y);
Console.Write("L");
}
//top
if (this.Boundis.Y > rct.Y)
{
this.Location = new Point(rct.X, this.Boundis.Y);
Console.Write("T");
}
//bottom
if (this.Boundis.Bottom < rct.Bottom)
{
this.Location = new Point(rct.X, this.Boundis.Bottom - rct.Height);
Console.Write("B");
}
Console.WriteLine();
}
}
void MovableContainer_MouseMove(object sender, MouseEventArgs e)
{
if (mdown)
{
this.Location = this.Location.Add(this.PointToClient(MousePosition).Sub(mpos)).Rasta(Rasta, rasta);
Rectangle rct = new Rectangle(this.Location, this.Size);
if (this.Boundis.X > rct.X)
{
this.Location = new Point(this.Boundis.X, this.Location.Y);
Console.Write("R");
}
//left
if (this.Boundis.Right < rct.Right)
{
this.Location = new Point(this.Boundis.Right - rct.Width, rct.Y);
Console.Write("L");
}
//top
if (this.Boundis.Y > rct.Y)
{
this.Location = new Point(rct.X, this.Boundis.Y);
Console.Write("T");
}
//bottom
if (this.Boundis.Bottom < rct.Bottom)
{
this.Location = new Point(rct.X, this.Boundis.Bottom - rct.Height);
Console.Write("B");
}
Console.WriteLine();
}
}
public Rectangle Boundis { get; set; }
}
public enum Axis { X, Y, None }
So, how can I fix this?
Frankly the code you posted is a big mess - most of the things you put there make no sense. From what I see, you are trying to implement run time movable container with clipping. A simple inherited Panel would do the same w/o the need of those designer attributes etc. Anyway, the problem you are describing is caused by the wrong calculations in your Parent_SizeChanged handler. Here is a partially cleaned up code that does what I think yours is trying to do w/o having any problems:
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
public partial class MovableContainer : UserControl
{
bool mdown = false;
Point mpos;
int rasta;
Control parent;
[EditorBrowsable(EditorBrowsableState.Always)]
[SettingsBindable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Axis Rasta { get; set; }
public static int DefautlRasta = 10;
public MovableContainer()
{
rasta = DefautlRasta;
InitializeComponent();
this.MouseDown += (sender, e) =>
{
mdown = true;
mpos = e.Location;
};
this.MouseUp += (sender, e) =>
{
mdown = false;
};
this.MouseMove += (sender, e) =>
{
if (mdown)
SetLocation(this.Location.Add(e.Location.Sub(mpos)).Rasta(Rasta, rasta));
};
EventHandler onParentSizeChanged = (sender, e) =>
{
SetLocation(this.Location);
};
this.ParentChanged += (sender, e) =>
{
if (parent != null) parent.SizeChanged -= onParentSizeChanged;
parent = Parent;
if (parent != null) parent.SizeChanged += onParentSizeChanged;
};
}
private void SetLocation(Point location)
{
var rect = new Rectangle(location, Size);
var clipRect = Parent.DisplayRectangle;
if (rect.Right > clipRect.Right) rect.X -= (rect.Right - clipRect.Right);
if (rect.X < clipRect.X) rect.X = clipRect.X;
if (rect.Bottom > clipRect.Bottom) rect.Y -= (rect.Bottom - clipRect.Bottom);
if (rect.Y < clipRect.Y) rect.Y = clipRect.Y;
location = rect.Location;
if (this.Location == location) return;
this.Location = location;
}
}
public enum Axis { X, Y, None }
// You haven't provided these, so I'm guessing by the usage
static class Utils
{
public static Point Add(this Point left, Point right)
{
return new Point(left.X + right.X, left.Y + right.Y);
}
public static Point Sub(this Point left, Point right)
{
return new Point(left.X - right.X, left.Y - right.Y);
}
public static Point Rasta(this Point pt, Axis axis, int value)
{
// Have absolutely no idea what is this about
return pt;
}
}

Show ToolTip with ComboBox (dropdownmenu)

I am showing a little tooltip, but if I change the selecteditem/text in the dropdownmenu, tooltip shows the old text and the new text. I want it to show only the new text.
private void optionsvalueComboBox_MouseHover(object sender, EventArgs e)
{
ToolTip buttonToolTip = new ToolTip();
buttonToolTip.ToolTipTitle = "Value";
buttonToolTip.UseFading = true;
buttonToolTip.UseAnimation = true;
buttonToolTip.IsBalloon = true;
buttonToolTip.ShowAlways = true;
buttonToolTip.AutoPopDelay = 5000;
buttonToolTip.InitialDelay = 1000;
buttonToolTip.ReshowDelay = 0;
buttonToolTip.SetToolTip(optionsvalueComboBox, optionsvalueComboBox.Text);
}
Assuming what you don't like is the tooltip text changing from the old text to the new text...
The reason it's doing that is because you are creating a new tooltip instance on every hover event. Every time the hover event is fired, the old tooltip instance is replaced with a new one which is why you see both. To fix this, put the declaration outside the event, like this:
ToolTip buttonToolTip = new ToolTip();
private void optionsvalueComboBox_MouseHover(object sender, EventArgs e)
{
buttonToolTip.ToolTipTitle = "Value";
buttonToolTip.UseFading = true;
buttonToolTip.UseAnimation = true;
buttonToolTip.IsBalloon = true;
buttonToolTip.ShowAlways = true;
buttonToolTip.AutoPopDelay = 5000;
buttonToolTip.InitialDelay = 1000;
buttonToolTip.ReshowDelay = 0;
buttonToolTip.SetToolTip(optionsvalueComboBox, optionsvalueComboBox.Text);
}
Now the same tooltip is being used with the wording simply being replaced. Let me know if this works for you!
I've tried digging in the MouseHover event of a ComboBox and looks like it doesn't work normally as we expect. The MouseHover is in fact fired only when you move the mouse over the drop down button if your ComboBox has style of dropdown. The simplest solution for this is change your combobox style to dropdownlist like this:
comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
However that kind of style will make the ComboBox readonly. If that's not what you want, there is a work-around for you is to use the event MouseMove with a Timer to mimic the MouseHover, here is the code for you:
public partial class Form1 : Form {
public Form1(){
InitializeComponent();
t.Interval = 600;
t.Tick += (se, ev) => {
buttonToolTip.SetToolTip(comboBox1, (string)comboBox1.SelectedItem);
t.Stop();
};
//init the buttonToolTip
buttonToolTip.ToolTipTitle = "Value";
buttonToolTip.UseFading = true;
buttonToolTip.UseAnimation = true;
buttonToolTip.IsBalloon = true;
buttonToolTip.ShowAlways = true;
buttonToolTip.AutoPopDelay = 5000;
buttonToolTip.InitialDelay = 1000;
buttonToolTip.ReshowDelay = 0;
//register MouseMove event handler for your comboBox1
comboBox1.MouseMove += (se, ev) => {
//Restart the timer every time the mouse is moving
t.Stop();
t.Start();
};
}
Timer t = new Timer();
ToolTip buttonToolTip = new ToolTip();
}
A complete working example:
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public RECT(Rectangle rect)
{
Left = rect.Left;
Top = rect.Top;
Right = rect.Right;
Bottom = rect.Bottom;
}
public Rectangle Rect
{
get
{
return new Rectangle(Left, Top, Right - Left, Bottom - Top);
}
}
public Point Location
{
get
{
return new Point(Left, Top);
}
}
public int Left;
public int Top;
public int Right;
public int Bottom;
}
public class ToolTipComboBox: ComboBox
{
#region Fields
private ToolTip toolTip;
private bool _tooltipVisible;
private bool _dropDownOpen;
#endregion
#region Types
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct COMBOBOXINFO
{
public Int32 cbSize;
public RECT rcItem;
public RECT rcButton;
public ComboBoxButtonState buttonState;
public IntPtr hwndCombo;
public IntPtr hwndEdit;
public IntPtr hwndList;
}
public enum ComboBoxButtonState
{
// ReSharper disable once UnusedMember.Global
StateSystemNone = 0,
// ReSharper disable once UnusedMember.Global
StateSystemInvisible = 0x00008000,
// ReSharper disable once UnusedMember.Global
StateSystemPressed = 0x00000008
}
[DllImport("user32.dll")]
public static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
#endregion
#region Properties
private IntPtr HwndCombo
{
get
{
COMBOBOXINFO pcbi = new COMBOBOXINFO();
pcbi.cbSize = Marshal.SizeOf(pcbi);
GetComboBoxInfo(Handle, ref pcbi);
return pcbi.hwndCombo;
}
}
private IntPtr HwndDropDown
{
get
{
COMBOBOXINFO pcbi = new COMBOBOXINFO();
pcbi.cbSize = Marshal.SizeOf(pcbi);
GetComboBoxInfo(Handle, ref pcbi);
return pcbi.hwndList;
}
}
[Browsable(false)]
public new DrawMode DrawMode
{
get { return base.DrawMode; }
set { base.DrawMode = value; }
}
#endregion
#region ctor
public ToolTipComboBox()
{
toolTip = new ToolTip
{
UseAnimation = false,
UseFading = false
};
base.DrawMode = DrawMode.OwnerDrawFixed;
DrawItem += OnDrawItem;
DropDownClosed += OnDropDownClosed;
DropDown += OnDropDown;
MouseLeave += OnMouseLeave;
}
#endregion
#region Methods
private void OnDropDown(object sender, EventArgs e)
{
_dropDownOpen = true;
}
private void OnMouseLeave(object sender, EventArgs e)
{
ResetToolTip();
}
private void ShowToolTip(string text, int x, int y)
{
toolTip.Show(text, this, x, y);
_tooltipVisible = true;
}
private void OnDrawItem(object sender, DrawItemEventArgs e)
{
ComboBox cbo = sender as ComboBox;
if (e.Index == -1) return;
// ReSharper disable once PossibleNullReferenceException
string text = cbo.GetItemText(cbo.Items[e.Index]);
e.DrawBackground();
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
{
TextRenderer.DrawText(e.Graphics, text, e.Font, e.Bounds.Location, SystemColors.Window);
if (_dropDownOpen)
{
Size szText = TextRenderer.MeasureText(text, cbo.Font);
if (szText.Width > cbo.Width - SystemInformation.VerticalScrollBarWidth && !_tooltipVisible)
{
RECT rcDropDown;
GetWindowRect(HwndDropDown, out rcDropDown);
RECT rcCombo;
GetWindowRect(HwndCombo, out rcCombo);
if (rcCombo.Top > rcDropDown.Top)
{
ShowToolTip(text, e.Bounds.X, e.Bounds.Y - rcDropDown.Rect.Height - cbo.ItemHeight - 5);
}
else
{
ShowToolTip(text, e.Bounds.X, e.Bounds.Y + cbo.ItemHeight - cbo.ItemHeight);
}
}
}
}
else
{
ResetToolTip();
TextRenderer.DrawText(e.Graphics, text, e.Font, e.Bounds.Location, cbo.ForeColor);
}
e.DrawFocusRectangle();
}
private void OnDropDownClosed(object sender, EventArgs e)
{
_dropDownOpen = false;
ResetToolTip();
}
private void ResetToolTip()
{
if (_tooltipVisible)
{
// ReSharper disable once AssignNullToNotNullAttribute
toolTip.SetToolTip(this, null);
_tooltipVisible = false;
}
}
#endregion
}

Categories

Resources