Animated gradient button flashes while painting - c#

I've created GradientButton which changes angle of gradient when mouse cursor is in its' bounds. Unfortunately the graphics is corrupted as there are random flashes during rendering.
To achieve gradient rotation I start a thread upon MouseEnter and stop it at MouseLeave. Doublebuffered set to true, helped a lot but did not solve this fully.
public partial class GradientButton : UserControl {
public Color Color1 { get; set; }
public Color Color2 { get; set; }
public bool Down { get; set; }
public bool MouseOnButton { get; set; }
[Browsable(true)]
public string TextToDraw { get; set; }
public int Angle { get; set; }
public GradientButton() {
InitializeComponent();
Color1 = Color.YellowGreen;
Color2 = Color.LightGreen;
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
var color1 = Color1;
var color2 = Color2;
if (Down) {
var temp = color1;
color1 = color2;
color2 = temp;
}
if (MouseOnButton) {
color1 = ControlPaint.Dark(Color2);
}
using (LinearGradientBrush brush = new LinearGradientBrush(this.ClientRectangle, color1, color2, Angle)) {
e.Graphics.FillRectangle(brush, this.ClientRectangle);
}
Rectangle rect1 = ClientRectangle;
// Create a StringFormat object with the each line of text, and the block
// of text centered on the page.
StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
// Draw the text and the surrounding rectangle.
e.Graphics.DrawString(TextToDraw, Font, new SolidBrush(ForeColor), rect1, stringFormat);
}
protected override void OnClick(EventArgs e) {
base.OnClick(e);
}
protected override void OnResize(EventArgs e) {
base.OnResize(e);
Invalidate();
}
protected override void OnMouseDown(MouseEventArgs e) {
base.OnMouseDown(e);
Down = true;
Invalidate();
}
protected override void OnMouseUp(MouseEventArgs e) {
base.OnMouseUp(e);
Down = false;
Invalidate();
}
protected override void OnMouseEnter(EventArgs e) {
base.OnMouseEnter(e);
MouseOnButton = true;
Thread t = new Thread(Animate);
t.Start();
}
public void Animate() {
while (MouseOnButton) {
Angle += 5;
Thread.Sleep(25);
Invalidate();
}
}
protected override void OnMouseLeave(EventArgs e) {
base.OnMouseLeave(e);
Angle = 0;
MouseOnButton = false;
Invalidate();
}
}
GradientButton.Designer.cs
partial class GradientButton {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.SuspendLayout();
//
// GradientButton
//
this.Name = "GradientButton";
this.Size = new System.Drawing.Size(78, 28);
this.ResumeLayout(false);
}
#endregion
}

Use
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlsStyles.Opaque, true);
to prevent the background from being painted prior to OnPaint processing. This should prevent the flicker inbetween the background erase and the painting.

Related

C# Graphics DrawLine Deletion

my drawings are deleted in the code I wrote. If "refresh" is not used, the drawing does not appear. Is there a mistake in my drawing? I don't understand
How can I draw a new line without erasing my lines ?
Here's the code
Pen pen;
Point startXY;
Point endXY;
bool ismoving = false;
string choice = "Line";
public Form1(){
InitializeComponent();}
private void drawArea_MouseDown(object sender, MouseEventArgs e){
startXY = e.Location;
ismoving = true;}
private void drawArea_MouseMove(object sender, MouseEventArgs e){
if (ismoving == true){
endXY = e.Location;
drawArea.Refresh();}}
private void drawArea_MouseUp(object sender, MouseEventArgs e){
endXY = e.Location;
ismoving = false;}
private void Form1_Load(object sender, EventArgs e){
//Graphics graph = drawArea.CreateGraphics();
//graph.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
pen = new Pen(Color.White,1);
pen.StartCap = pen.EndCap = System.Drawing.Drawing2D.LineCap.Round;}
private void drawArea_Paint(object sender, PaintEventArgs e){
switch (choice){
case "Line":{
e.Graphics.DrawLine(pen, startXY, endXY);
break;}}}
Your code needs to know how to recreate the entire graphics window at any time, because windows may ask it to redraw at any time.
So you need to keep track of the things to be drawn in a list and use this list in the Paint event to render to the screen.
Below is an example code that does this. The result looks like this
public enum Tool
{
Line,
PolyLine,
Circle,
}
public partial class Form1 : Form
{
Point startXY;
Point endXY;
bool isMoving;
Tool next;
public Form1()
{
InitializeComponent();
this.Drawing = new List<IDrawObject>();
this.isMoving = false;
this.next = Tool.Line;
this.drawArea.BackColor = Color.Black;
this.drawArea.BorderStyle = BorderStyle.FixedSingle;
}
public List<IDrawObject> Drawing { get; }
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
drawArea.Resize += (s, ev) => drawArea.Refresh();
drawArea.Paint += pictureBox1_Paint;
drawArea.MouseDown += PictureBox1_MouseDown;
drawArea.MouseMove += PictureBox1_MouseMove;
drawArea.MouseUp += PictureBox1_MouseUp;
this.KeyDown += (s, ev) =>
{
switch (ev.KeyCode)
{
case Keys.Space:
next = NextTool(next);
UpdateFormTitle();
break;
}
};
UpdateFormTitle();
}
public void UpdateFormTitle()
{
this.Text = $"Draw Area. Tool={next}. Press [SPACE] to switch.";
}
private void PictureBox1_MouseUp(object sender, MouseEventArgs e)
{
isMoving = false;
endXY = e.Location;
Drawing.Add(NextDrawObject());
drawArea.Invalidate();
}
private void PictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (isMoving)
{
endXY = e.Location;
drawArea.Invalidate();
}
}
private void PictureBox1_MouseDown(object sender, MouseEventArgs e)
{
isMoving = true;
startXY = e.Location;
}
protected void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var pen = new Pen(Color.White, 1))
{
pen.EndCap = LineCap.Round;
pen.StartCap = LineCap.Round;
foreach (var item in Drawing)
{
item.Draw(e.Graphics, pen);
}
if (isMoving)
{
pen.Color = Color.SteelBlue;
pen.DashStyle = DashStyle.Dash;
NextDrawObject().Draw(e.Graphics, pen);
}
}
}
public static float Distance(Point from, Point to)
{
var dx = (float)(to.X-from.X);
var dy = (float)(to.Y-from.Y);
return (float)Math.Sqrt(dx*dx+dy*dy);
}
public Tool NextTool(Tool current)
{
switch (current)
{
case Tool.Line:
return Tool.Circle;
case Tool.Circle:
return Tool.Line;
case Tool.PolyLine:
throw new NotImplementedException();
default:
throw new NotSupportedException();
}
}
public IDrawObject NextDrawObject()
{
switch (next)
{
case Tool.Line:
return new Line(startXY, endXY);
case Tool.PolyLine:
throw new NotImplementedException();
case Tool.Circle:
var radius = Distance(startXY, endXY);
return new Circle(startXY, radius);
default:
throw new NotSupportedException();
}
}
}
public interface IDrawObject
{
void Draw(Graphics g, Pen pen);
}
public struct Line : IDrawObject
{
public Line(PointF from, PointF to)
{
this.From=from;
this.To=to;
}
public PointF From { get; }
public PointF To { get; }
public void Draw(Graphics g, Pen pen)
{
g.DrawLine(pen, From, To);
}
}
public struct PolyLine : IDrawObject
{
public PolyLine(params PointF[] nodes)
{
this.Nodes=nodes;
}
public PointF[] Nodes { get; }
public void Draw(Graphics g, Pen pen)
{
g.DrawLines(pen, Nodes);
}
}
public struct Circle : IDrawObject
{
public Circle(PointF center, float radius)
{
this.Center=center;
this.Radius=radius;
}
public PointF Center { get; }
public float Radius { get; }
public void Draw(Graphics g, Pen pen)
{
g.DrawEllipse(pen, Center.X-Radius, Center.Y-Radius, 2*Radius, 2*Radius);
}
}

How to make control Scalable by Mouse? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
Im trying to code a simple slideshow program.
I have a class named SlideShowText.
I want it to be scalable by mouse like scaling the control in Visual Studio.
I want it to have dotted borders.
Any help?
(I didn't try anything since im such a noob)
You can make any control sizeable with the mouse, overriding its CreateParams property.
For example
class SizeableRichTextBox : RichTextBox
{
protected override CreateParams CreateParams
{
get
{
const int WS_SIZEBOX = 0x40000;
var cp = base.CreateParams;
cp.Style |= WS_SIZEBOX;
return cp;
}
}
}
class SizeablePictureBox : PictureBox
{
protected override CreateParams CreateParams
{
get
{
const int WS_SIZEBOX = 0x40000;
var cp = base.CreateParams;
cp.Style |= WS_SIZEBOX;
return cp;
}
}
}
Now they have a thick border and can be resized with the mouse.
The image scale in the SizeablePictureBox will change automatically, if you set the ImageLayout.Zoom parameter.
var pictureBox = new SizeablePictureBox { Parent = this, Width = 500, Height = 500 };
pictureBox.BackgroundImageLayout = ImageLayout.Zoom;
pictureBox.BackgroundImage = Image.FromFile("pic.jpg");
To change the text scale in the TextBox you have to manually calculate the aspect ratio and change the font size. Or you can try to change the value of ZoomFactor property.
Here is a Resizer control:
public partial class Resizer : Control
{
public Resizer()
{
InitializeComponent();
Size = new System.Drawing.Size(12, 12);
}
public Control control { get; set; }
Point mDown = Point.Empty;
public Resizer(Control ctl)
{
InitializeComponent();
control = ctl;
Size = new System.Drawing.Size(12, 12);
}
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);
if (control != null)
{
BackColor = Color.DarkOrange;
Location = new Point(control.Width - Size.Width,
control.Height - Size.Height);
Parent = control;
BringToFront();
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
mDown = e.Location;
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
Hide();
base.OnMouseUp(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (e.Button.HasFlag(MouseButtons.Left))
{
Left += -(mDown.X - e.X);
Top += -(mDown.Y - e.Y);
control.Size = new Size(Right, Bottom);
}
base.OnMouseMove(e);
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
}
After adding it to the project you can use it maybe like this:
Resizer rz = null;
private void pictureBox1_DoubleClick(object sender, EventArgs e)
{
if (rz == null) rz = new Resizer(pictureBox1); else rz.Show();
}
Result:

C# alpha image overlapping

I've created a transparent control as suggested here
public partial class TransparentControl : UserControl
{
private readonly Timer refresher;
private Image _image;
public TransparentControl()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
BackColor = Color.Transparent;
refresher = new Timer();
refresher.Tick += TimerOnTick;
refresher.Interval = 50;
refresher.Enabled = true;
refresher.Start();
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
protected override void OnMove(EventArgs e)
{
RecreateHandle();
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawImage(_image, new Rectangle((Width / 2) - (_image.Width / 2), (Height / 2) - (_image.Height / 2), _image.Width, _image.Height));
}
protected override void OnPaintBackground(PaintEventArgs e)
{}
public void Redraw()
{
RecreateHandle();
}
private void TimerOnTick(object source, EventArgs e)
{
RecreateHandle();
refresher.Stop();
}
public Image Image
{
get { _image; }
set { _image = value; RecreateHandle(); }
}
}
The problem is that if i overlap two TransparentControl object, the one that stays on top is not being rendered (or maybe it's being rendered but is not visible at all).
Is there something to add to the control to fix this?
Because using PictureBox for the top image works, but on Linux with Mono its not rendering alpha pixels. That's why I need to use this TransparentControl.
Thanks everyone.

Change my Winform button behavior when hover, click

Please take a look at this implementation from Telerik:
When hover:
When click:
Can I implement it without using any 3rd party program ?
I have created an example to demonstrate how to create your custom control.
Create a UserControl and give the name Sample and place the following code in .cs file.
[DefaultEvent("Click")]
public partial class Sample : UserControl
{
private string _text;
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override string Text
{
get
{
return _text;
}
set
{
_text = value;
}
}
private bool _mouseDown = false;
private bool _mouseHover = false;
private bool _invalidateRequired = true;
public Sample()
{
InitializeComponent();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
_mouseDown = true;
_invalidateRequired = true;
this.Invalidate();
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
_mouseDown = false;
_invalidateRequired = true;
this.Invalidate();
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
_mouseHover = true;
if (_invalidateRequired)
{
this.Invalidate();
_invalidateRequired = false;
}
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
_mouseHover = false;
this.Invalidate();
_invalidateRequired = true;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Rectangle r = new Rectangle(0, 0, this.ClientRectangle.Width - 1, this.ClientRectangle.Height - 1);
Color bg = Color.White;
Color fr = Color.Gray;
Color br = Color.FromArgb(173, 178, 173);
if (_mouseDown)
{
bg = Color.FromArgb(24, 162, 231);
fr = Color.White;
}
if (_mouseHover)
br = Color.FromArgb(24, 162, 231);
e.Graphics.FillRectangle(new SolidBrush(bg), r);
e.Graphics.DrawRectangle(new Pen(br, 3), r);
StringFormat sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
e.Graphics.DrawString(Text, this.Font, new SolidBrush(fr), r, sf);
}
}
Code with Designer.cs

How to autoscroll single line textbox continuous?

I need to write something to autoscroll a text in a single line TextBox continuously. Do I need to scroll to caret then set it to first char and start over?
So i found a marquee control and modify it a little and used it instead of textbox
public class Marquee : System.Windows.Forms.UserControl
{
private System.ComponentModel.IContainer components;
private System.Windows.Forms.Timer timer1;
private int currentPos = 0;
private bool mBorder;
private string mText;
public string MarqueeText
{
get { return mText; }
set { mText = value; }
}
public bool Border
{
get { return mBorder; }
set { mBorder = value; }
}
public int Interval
{
get { return timer1.Interval * 10; }
set { timer1.Interval = value / 10; }
}
public Marquee()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
// TODO: Add any initialization after the InitForm call
this.Size = new Size(this.Width, this.Font.Height);
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.timer1 = new System.Windows.Forms.Timer(this.components);
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Interval = 1000;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
//
// Marquee
//
this.Name = "Marquee";
this.Size = new System.Drawing.Size(150, 136);
this.Resize += new System.EventHandler(this.Marquee_Resize);
}
#endregion
private void Marquee_Resize(object sender, System.EventArgs e)
{
this.Height = this.Font.Height;
}
private void timer1_Tick(object sender, System.EventArgs e)
{
this.Invalidate();
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
if (mBorder)
{
e.Graphics.DrawRectangle(new Pen(this.ForeColor), 0, 0, this.Width - 1, this.Height - 1);
}
float dif = e.Graphics.MeasureString(mText, this.Font).Width- this.Width;
if (this.Width < e.Graphics.MeasureString(mText, this.Font).Width)
{
e.Graphics.DrawString(mText, this.Font, new SolidBrush(this.ForeColor),
currentPos, 0);
e.Graphics.DrawString(mText, this.Font, new SolidBrush(this.ForeColor),
this.Width + currentPos + dif, 0);
currentPos--;
if ((currentPos < 0) && (Math.Abs(currentPos) >= e.Graphics.MeasureString(mText, this.Font).Width))
currentPos = this.Width + currentPos;
}
else
{
e.Graphics.DrawString(mText, this.Font, new SolidBrush(this.ForeColor),-dif/2, 0);
}
}
}
So if the text width is longer than the width of the control the text will be static else the text will auto-scroll

Categories

Resources