Can't save forms with my custom userControl in it - c#

After I created a userControl and established it in a Windows form, this form could no longer be saved. When I press Ctrl + S or the Save button nothing happens, the small * on the form file name suggests that it was not saved. Also, my form contains a property for a color. When I change this, the color doesn't change and the property window doesn't seem to respond anymore.
Code of custom userControl
namespace FlowGui
{
public partial class FlowProgressBar: UserControl
{
// Public Variables with Setters
private int maxValue;
[Bindable(true), Category("Behavior")]
public int Maximum
{
get
{
return maxValue;
}
set
{
maxValue = value;
DrawBar();
}
}
private int minValue;
[Bindable(true), Category("Behavior")]
public int Minimum
{
get
{
return minValue;
}
set
{
minValue = value;
DrawBar();
}
}
private int currentValue;
[Bindable(true), Category("Behavior")]
public int Value
{
get
{
return currentValue;
}
set
{
currentValue = value;
DrawBar();
}
}
private Color barColor;
[Bindable(true), Category("Appearance")]
public Color BarColor
{
get
{
return barColor;
}
set
{
barColor = value;
CreateBarImage();
DrawBar();
}
}
// private Values for Drawing
private Image canvas;
private Graphics gfx;
private Bitmap barImage;
public FlowProgressBar()
{
InitializeComponent();
}
// Load
private void FlowProgressBar_Load(object sender, EventArgs e)
{
maxValue = 100;
minValue = 0;
currentValue = 0;
barColor = Color.LightBlue;
// Init Graphic
InitGraphics();
CreateBarImage();
DrawBar();
}
private void CreateBarImage()
{
barImage = new Bitmap(10, 50);
Bitmap currentBarImage = Properties.Resources.BarImage_Basic;
for(int x=0; x<10; x++)
{
for (int y = 0; y < 50; y++)
{
Color barPreColor = currentBarImage.GetPixel(x, y);
int r = barPreColor.R * barColor.R / 255;
int g = barPreColor.G * barColor.G / 255;
int b = barPreColor.B * barColor.B / 255;
Color pixelColor = Color.FromArgb(r,g,b);
currentBarImage.SetPixel(x, y, pixelColor);
}
}
}
private void InitGraphics()
{
canvas = new Bitmap(pictureBoxBar.Width, pictureBoxBar.Height);
gfx = Graphics.FromImage(canvas);
}
private void DrawBar()
{
int barWidth = pictureBoxBar.Width;
int barHeight = pictureBoxBar.Height;
Image bgImage = Properties.Resources.BarBG_Raised_Grey;
gfx.DrawImage(bgImage,-200,0,barWidth+400,barHeight);
gfx.DrawImage(barImage, 0, 0, 100, barHeight);
pictureBoxBar.Image = canvas;
}
// Behaviour
private void FlowProgressBar_SizeChanged(object sender, EventArgs e)
{
InitGraphics();
DrawBar();
}
private void pictureBoxBar_Paint(object sender, PaintEventArgs e)
{
InitGraphics();
DrawBar();
}
}
}
My first thought was that it could be because the userControll is in another project and only linked. But even after I implemented the userControll in the main project, my described problems occurred. So I undo this.
I have no idea what I'm doing wrong. Thank you very much for your help.

Solved!
The problem was the pictureBoxBar_Paint() event. After I removed it from the userControl everything runs.

Related

Create dynamically controls in columns inside a Panel

I have a panel where I create Labels and NumericUpDown fields dinamically like:
List<string> Labels = new List<string>();
public List<Label> DeliveryBreakdownLabelsModel = new List<Label>();
public List<NumericUpDown> DeliveryBreakdownNumericUpDownModel = new List<NumericUpDown>();
private void SetDeliveryBreakdownAmountForm_Load(object sender, EventArgs e)
{
var rModel = //List data from database
AddRow(rModel);
Arrange();
}
private void AddRow(IList<DeliveryBreakdownGetViewModel> rModel)
{
for (int i = 0; i < rModel.Count; i++)
{
Labels.Add(rModel[i].DesignGroupName);
var label = new Label
{
AutoSize = true, // make sure to enable AutoSize
Name = "label" + Labels.Count,
Text = rModel[i].DesignGroupName,
Location = new Point(12, YPos)
};
this.Controls.Add(label);
pnlDeliveryBreakdown.Controls.Add(label);
DeliveryBreakdownLabelsModel.Add(label);
var numericUpDown = new NumericUpDown
{
Name = "numericUpDown" + Labels.Count,
Text = rModel[i].ContractedAmount.ToString(),
Location = new Point(12, YPos),
Size = new Size(60, 19),
DecimalPlaces = 2,
Maximum = decimal.MaxValue
};
this.Controls.Add(numericUpDown);
this.Controls.Add(numericUpDown);
pnlDeliveryBreakdown.Controls.Add(numericUpDown);
DeliveryBreakdownNumericUpDownModel.Add(numericUpDown);
YPos += 25;
}
}
void Arrange()
{
// Determine the widest label sized by the AutoSize
var maxLabelX = 0;
for (int i = 0; i < Labels.Count; i++)
{
maxLabelX = Math.Max(maxLabelX, DeliveryBreakdownLabelsModel[i].Location.X + DeliveryBreakdownLabelsModel[i].Size.Width);
}
// Move all the text boxes a little to the right of the widest label
var maxNumericX = 0;
for (int i = 0; i < Labels.Count; i++)
{
maxNumericX = Math.Max(maxNumericX, DeliveryBreakdownNumericUpDownModel[i].Location.X + DeliveryBreakdownNumericUpDownModel[i].Size.Width);
DeliveryBreakdownNumericUpDownModel[i].Location = new Point(maxLabelX + 10, DeliveryBreakdownNumericUpDownModel[i].Location.Y);
}
//Set total wi
this.Width = maxNumericX + maxLabelX + 60;
}
So it looks like:
My question is, how can I modify my code in order to create more than one column. I want to do that because sometimes I can have alot of data so shows only in vertical may be a problem in future. Expected result: I.E
I suggest using a TableLayoutPanel with a UserControl that contains exactly one label and one numeric box.
Even in the designer, this looks neat.
All you have to do is expose the properties you want from the numeric box in your user control.
In the example above, I am using the following code:
// Fixed height user control
// Some code taken from: https://stackoverflow.com/a/4388922/380384
[Designer(typeof(MyControlDesigner))]
public partial class LabelNumeric : UserControl
{
public LabelNumeric()
{
InitializeComponent();
}
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
{
base.SetBoundsCore(x, y, width, 24, specified);
}
[DefaultValue("Label")]
public string Caption
{
get => label1.Text;
set => label1.Text = value;
}
[DefaultValue(0)]
public decimal Value
{
get => numericUpDown1.Value;
set => numericUpDown1.Value =value;
}
[DefaultValue(0)]
public int DecimalPlaces
{
get => numericUpDown1.DecimalPlaces;
set => numericUpDown1.DecimalPlaces = value;
}
[DefaultValue(100)]
public decimal MaxValue
{
get => numericUpDown1.Maximum;
}
[DefaultValue(0)]
public decimal MinValue
{
get => numericUpDown1.Minimum;
}
}
internal class MyControlDesigner : ControlDesigner
{
MyControlDesigner()
{
base.AutoResizeHandles = true;
}
public override SelectionRules SelectionRules
{
get
{
return SelectionRules.LeftSizeable | SelectionRules.RightSizeable | SelectionRules.Moveable;
}
}
}
You can access all the controls from the tableLayoutPanel1.Controls property and check their position in the table like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
foreach (var item in tableLayoutPanel1.Controls.OfType<LabelNumeric>())
{
var pos = tableLayoutPanel1.GetCellPosition(item);
item.Caption = $"Row{pos.Row}Col{pos.Column}";
}
}
}
So at runtime it is as follows:
The trick is to have one extra row on the bottom autosized, and all the other rows that contain the controls be of fixed height of 24pt.

Draw Shapes and Strings with undo and redo feature

Is there a way to drawstring and then remove it?
I've used following classes to Undo/Redo Rectangle, Circle, Line, Arrow type shapes but cant figure how i can remove drawn string.
https://github.com/Muhammad-Khalifa/Free-Snipping-Tool/blob/master/Free%20Snipping%20Tool/Operations/UndoRedo.cs
https://github.com/Muhammad-Khalifa/Free-Snipping-Tool/blob/master/Free%20Snipping%20Tool/Operations/Shape.cs
https://github.com/Muhammad-Khalifa/Free-Snipping-Tool/blob/master/Free%20Snipping%20Tool/Operations/ShapesTypes.cs
Here is how i'm adding Rectangle in shape list: This works well when i undo or redo from the list.
DrawString
Shape shape = new Shape();
shape.shape = ShapesTypes.ShapeTypes.Rectangle;
shape.CopyTuplePoints(points);
shape.X = StartPoint.X;
shape.Y = StartPoint.Y;
shape.Width = EndPoint.X;
shape.Height = EndPoint.Y;
Pen pen = new Pen(new SolidBrush(penColor), 2);
shape.pen = pen;
undoactions.AddShape(shape);
This is how i'm drawing text:
var fontFamily = new FontFamily("Calibri");
var font = new Font(fontFamily, 12, FontStyle.Regular, GraphicsUnit.Point);
Size proposedSize = new Size(int.MaxValue, int.MaxValue);
TextFormatFlags flags = TextFormatFlags.WordEllipsis | TextFormatFlags.NoPadding | TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.WordBreak;
Size size = TextRenderer.MeasureText(e.Graphics, textAreaValue, font, proposedSize, flags);
Shape shape = new Shape();
shape.shape = ShapesTypes.ShapeTypes.Text;
shape.X = ta.Location.X;
shape.Y = ta.Location.Y;
shape.Width = size.Width;
shape.Height = size.Height;
shape.Value = textAreaValue;
Pen pen = new Pen(new SolidBrush(penColor), 2);
shape.pen = pen;
undoactions.AddShape(shape);
But this does not work with undo-redo list. Maybe problem is with pen and font-size but i cant figure it out how to use pen with DrawString.
Edit:
Here's how i'm drawing in paint event
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
foreach (var item in undoactions.lstShape)
{
if (item.shape == ShapesTypes.ShapeTypes.Line)
{
e.Graphics.DrawLine(item.pen, item.X, item.Y, item.Width, item.Height);
}
else if (item.shape == ShapesTypes.ShapeTypes.Pen)
{
if (item.Points.Count > 1)
{
e.Graphics.DrawCurve(item.pen, item.Points.ToArray());
}
}
else if (item.shape == ShapesTypes.ShapeTypes.Text)
{
var fontFamily = new FontFamily("Calibri");
var font = new Font(fontFamily, 12, FontStyle.Regular, GraphicsUnit.Point);
e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
e.Graphics.DrawString(item.Value, font, new SolidBrush(item.pen.Color), new PointF(item.X, item.Y));
}
}
}
Shape.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Drawing
{
public class Shape : ICloneable
{
public ShapesTypes.ShapeTypes shape { get; set; }
public List<Point> Points { get; }
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public Pen pen { get; set; }
public String Value { get; set; }
public Shape()
{
Points = new List<Point>();
}
public void CopyPoints(List<Point> points)
{
for (int i = 0; i < points.Count; i++)
{
Point p = new Point();
p.X = points[i].X;
p.Y = points[i].Y;
Points.Add(p);
}
}
public void CopyCopyPoints(List<List<Point>> points)
{
for (int j = 0; j < points.Count; j++)
{
List<Point> current = points[j];
for (int i = 0; i < current.Count; i++)
{
Point p = new Point();
p.X = current[i].X;
p.Y = current[i].Y;
Points.Add(p);
}
}
}
public void CopyTuplePoints(List<Tuple<Point, Point>> points)
{
foreach (var line in points)
{
Point p = new Point();
p.X = line.Item1.X;
p.Y = line.Item1.Y;
Points.Add(p);
p.X = line.Item2.X;
p.Y = line.Item2.Y;
Points.Add(p);
}
}
public object Clone()
{
Shape shp = new Shape();
shp.X = X;
shp.Y = Y;
shp.Width = Width;
shp.Height = Height;
shp.pen = pen;
shp.shape = shape;
shp.Value = Value;
for (int i = 0; i < Points.Count; i++)
{
shp.Points.Add(new Point(Points[i].X, Points[i].Y));
}
return shp;
}
}
}
DrawCircle
if (currentshape == ShapesTypes.ShapeTypes.Circle)
{
Shape shape = new Shape();
shape.shape = ShapesTypes.ShapeTypes.Circle;
shape.CopyTuplePoints(cLines);
shape.X = StartPoint.X;
shape.Y = StartPoint.Y;
shape.Width = EndPoint.X;
shape.Height = EndPoint.Y;
Pen pen = new Pen(new SolidBrush(penColor), 2);
shape.pen = pen;
undoactions.AddShape(shape);
}
Undo
if (currentshape != ShapesTypes.ShapeTypes.Undo)
{
oldshape = currentshape;
currentshape = ShapesTypes.ShapeTypes.Undo;
}
if (undoactions.lstShape.Count > 0)
{
undoactions.Undo();
this.Invalidate();
}
if (undoactions.redoShape.Count > 0)
{
btnRedo.Enabled = true;
}
UndoRedo
public class UndoRedo
{
public List<Shape> lstShape = new List<Shape>();
public List<Shape> redoShape = new List<Shape>();
public void AddShape(Shape shape)
{
lstShape.Add(shape);
}
public void Undo()
{
redoShape.Add((Shape)lstShape[lstShape.Count - 1].Clone());
lstShape.RemoveAt(lstShape.Count - 1);
}
public void Redo()
{
lstShape.Add((Shape)redoShape[redoShape.Count - 1].Clone());
redoShape.RemoveAt(redoShape.Count - 1);
}
}
you can create a TextShape deriving from Shape, having Text, Font, Location and Color properties and treat it like other shapes, so redo and undo will not be a problem.
Here are some tips which will help you to solve the problem:
Create a base Shape class or interface containing basic methods like Draw, Clone, HitTest, etc.
All shapes, including TextShape should derive from Shape. TextShape is also a shape, having Text, Font, Location and Color properties.
Each implementation of Shape has its implementation of base methods.
Implement INotifyPropertyChanged in all your shapes, then you can listen to changes of properties and for example, add something to undo buffer after change of color, border width, etc.
Implement IClonable or base class Clone method. All shapes should be clonable when adding to undo buffer.
Do dispose GDI objects like Pen and Brush. It's not optional.
Instead of adding a single shape to undo buffer, create a class like drawing context containing List of shapes, Background color of drawing surface and so on. Also in this class implement INotifyPropertyChanged, then by each change in the shapes or this class properties, you can add a clone of this class to undo buffer.
Shape
Here is an example of Shapeclass:
public abstract class Shape : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string name = "") {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public abstract void Draw(Graphics g);
public abstract Shape Clone();
}
TextShape
Pay attention to the implementation of properties to raise PropertyChanged event and also Clone method to clone the object for undo buffer, also the way that GDI object have been used in Draw:
public class TextShape : Shape {
private string text;
public string Text {
get { return text; }
set {
if (text != value) {
text = value;
OnPropertyChanged();
}
}
}
private Point location;
public Point Location {
get { return location; }
set {
if (!location.Equals(value)) {
location = value;
OnPropertyChanged();
}
}
}
private Font font;
public Font Font {
get { return font; }
set {
if (font!=value) {
font = value;
OnPropertyChanged();
}
}
}
private Color color;
public Color Color {
get { return color; }
set {
if (color!=value) {
color = value;
OnPropertyChanged();
}
}
}
public override void Draw(Graphics g) {
using (var brush = new SolidBrush(Color))
g.DrawString(Text, Font, brush, Location);
}
public override Shape Clone() {
return new TextShape() {
Text = Text,
Location = Location,
Font = (Font)Font.Clone(),
Color = Color
};
}
}
DrawingContext
This class in fact contains all shapes and some other properties like back color of drawing surface. This is the class which you need to add its clone to undo buffer:
public class DrawingContext : INotifyPropertyChanged {
public DrawingContext() {
BackColor = Color.White;
Shapes = new BindingList<Shape>();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string name = "") {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
private Color backColor;
public Color BackColor {
get { return backColor; }
set {
if (!backColor.Equals(value)) {
backColor = value;
OnPropertyChanged();
}
}
}
private BindingList<Shape> shapes;
public BindingList<Shape> Shapes {
get { return shapes; }
set {
if (shapes != null)
shapes.ListChanged -= Shapes_ListChanged;
shapes = value;
OnPropertyChanged();
shapes.ListChanged += Shapes_ListChanged;
}
}
private void Shapes_ListChanged(object sender, ListChangedEventArgs e) {
OnPropertyChanged("Shapes");
}
public DrawingContext Clone() {
return new DrawingContext() {
BackColor = this.BackColor,
Shapes = new BindingList<Shape>(this.Shapes.Select(x => x.Clone()).ToList())
};
}
}
DrawingSurface
This class is in fact the control which has undo and redo functionality and also draws the current drawing context on its surface:
public class DrawingSurface : Control {
private Stack<DrawingContext> UndoBuffer = new Stack<DrawingContext>();
private Stack<DrawingContext> RedoBuffer = new Stack<DrawingContext>();
public DrawingSurface() {
DoubleBuffered = true;
CurrentDrawingContext = new DrawingContext();
UndoBuffer.Push(currentDrawingContext.Clone());
}
DrawingContext currentDrawingContext;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false)]
public DrawingContext CurrentDrawingContext {
get {
return currentDrawingContext;
}
set {
if (currentDrawingContext != null)
currentDrawingContext.PropertyChanged -= CurrentDrawingContext_PropertyChanged;
currentDrawingContext = value;
Invalidate();
currentDrawingContext.PropertyChanged += CurrentDrawingContext_PropertyChanged;
}
}
private void CurrentDrawingContext_PropertyChanged(object sender, PropertyChangedEventArgs e) {
UndoBuffer.Push(CurrentDrawingContext.Clone());
RedoBuffer.Clear();
Invalidate();
}
public void Undo() {
if (CanUndo) {
RedoBuffer.Push(UndoBuffer.Pop());
CurrentDrawingContext = UndoBuffer.Peek().Clone();
}
}
public void Redo() {
if (CanRedo) {
CurrentDrawingContext = RedoBuffer.Pop();
UndoBuffer.Push(CurrentDrawingContext.Clone());
}
}
public bool CanUndo {
get { return UndoBuffer.Count > 1; }
}
public bool CanRedo {
get { return RedoBuffer.Count > 0; }
}
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var brush = new SolidBrush(CurrentDrawingContext.BackColor))
e.Graphics.FillRectangle(brush, ClientRectangle);
foreach (var shape in CurrentDrawingContext.Shapes)
shape.Draw(e.Graphics);
}
}
In the future, please follow the guidelines for a Minimal, Complete, and Verifiable example. This will help us to help you. For example, you could have excluded all of the code related to cloning, since it's not related to your problem.
I refactored your code a little and created a small, reproducible example. This example works with the general approach you outlined, so I can't tell you exactly why your code doesn't work unless you could also post a similar example that I can copy / paste into my environment. Please do not link to external code - it must be hosted here.
I refactored it to highlight some language features which could help to make your code more maintainable. Please let me know if you have any questions about what I put here. Please let me know if this helps. If not, please use it as a template and replace my code with yours so I can assist you.
public partial class Form1 : Form
{
private EntityBuffer _buffer = new EntityBuffer();
private System.Windows.Forms.Button btnUndo;
private System.Windows.Forms.Button btnRedo;
public Form1()
{
this.btnUndo = new System.Windows.Forms.Button();
this.btnRedo = new System.Windows.Forms.Button();
this.SuspendLayout();
this.btnUndo.Location = new System.Drawing.Point(563, 44);
this.btnUndo.Name = "btnUndo";
this.btnUndo.Size = new System.Drawing.Size(116, 29);
this.btnUndo.TabIndex = 0;
this.btnUndo.Text = "Undo";
this.btnUndo.UseVisualStyleBackColor = true;
this.btnUndo.Click += new System.EventHandler(this.btnUndo_Click);
this.btnRedo.Location = new System.Drawing.Point(563, 79);
this.btnRedo.Name = "btnRedo";
this.btnRedo.Size = new System.Drawing.Size(116, 29);
this.btnRedo.TabIndex = 0;
this.btnRedo.Text = "Redo";
this.btnRedo.UseVisualStyleBackColor = true;
this.btnRedo.Click += new System.EventHandler(this.btnRedo_Click);
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.btnRedo);
this.Controls.Add(this.btnUndo);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
protected override void OnLoad(EventArgs e)
{
_buffer.Add(new Rectangle(10, 10, 10, 10, Color.Red));
_buffer.Add(new Rectangle(20, 20, 10, 10, Color.Red));
_buffer.Add(new Rectangle(30, 30, 10, 10, Color.Red));
_buffer.Add(new Text(40, 40, "Test", Color.Black));
_buffer.Add(new Rectangle(50, 50, 10, 10, Color.Red));
_buffer.Add(new Text(60, 60, "Test", Color.Black));
base.OnLoad(e);
}
protected override void OnPaint(PaintEventArgs e)
{
foreach (var entity in _buffer.Entities)
entity.Draw(e.Graphics);
base.OnPaint(e);
}
private void btnUndo_Click(object sender, EventArgs e)
{
if (!_buffer.CanUndo)
return;
_buffer.Undo();
Invalidate();
}
private void btnRedo_Click(object sender, EventArgs e)
{
if (!_buffer.CanRedo)
return;
_buffer.Redo();
Invalidate();
}
}
public abstract class Entity
{
public int X { get; set; }
public int Y { get; set; }
public Color Color { get; set; }
public abstract void Draw(Graphics g);
public Entity(int x, int y, Color color)
{
X = x;
Y = y;
Color = color;
}
}
public class Text : Entity
{
private static Font _font = new Font(new FontFamily("Calibri"), 12, FontStyle.Regular, GraphicsUnit.Point);
public string Value { get; set; }
public Text(int x, int y, string value, Color color) : base(x,y,color) => Value = value;
public override void Draw(Graphics g) => g.DrawString(Value, _font, new SolidBrush(Color), new PointF(X, Y));
}
public abstract class Shape : Entity
{
public int Width { get; set; }
public int Height { get; set; }
public Pen Pen { get; set; }
public Shape(int x, int y, int width, int height, Color color) : base(x, y, color)
{
Width = width;
Height = height;
}
}
public class Rectangle : Shape
{
public Rectangle(Point start, Point end, Color color) : this(start.X, start.Y, end.X, end.Y, color) { }
public Rectangle(int x, int y, int width, int height, Color color) : base(x, y, width, height, color) { }
public override void Draw(Graphics g) => g.DrawRectangle(new Pen(new SolidBrush(Color)), X, Y, Width, Height);
}
public class EntityBuffer
{
public Stack<Entity> Entities { get; set; } = new Stack<Entity>();
public Stack<Entity> RedoBuffer { get; set; } = new Stack<Entity>();
public bool CanRedo => RedoBuffer.Count > 0;
public bool CanUndo => Entities.Count > 0;
public void Add(Entity entity)
{
Entities.Push(entity);
RedoBuffer.Clear();
}
public void Undo() => RedoBuffer.Push(Entities.Pop());
public void Redo() => Entities.Push(RedoBuffer.Pop());
}
I have done a similar kind of project, after drawing shapes and writing it's dimension as a string on images; after pressing Ctrl-Z/Ctrl-Y it does undo/redo the operations performed on images.
Here is a link to my Github project, a C# win-form soln. After running the soln, tool usage instruction will get appear on the tool itself.
Hope this helps you...

C# progress bar bad render

I created custom progress bar with image but i have problem. From 50% on progress bar are showing spaces.
Progress Bar here: http://i.stack.imgur.com/d686B.png
There is control library code:
public partial class UserControl1: UserControl
{
public UserControl1() : base()
{
InitializeComponent();
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
}
private void UserControl1_Load(object sender, EventArgs e)
{
}
bool _revealImage = true;
int _maximum = 100;
int _value = 0;
Image _progressbarImage = Properties.Resources.progress;
Color _progressbarColor = Color.Transparent;
protected override void OnResize(EventArgs e)
{
// Invalidate the control to get a repaint.
this.Invalidate();
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle r = this.ClientRectangle;
ControlPaint.DrawBorder(g, r, Color.Transparent, ButtonBorderStyle.Solid);
float percent = (float)_value / (float)(_maximum);
//r.Inflate(-1, -1);
r.Width = (int)((float)r.Width * percent);
if ((r.Width < 1))
{
return;
}
if (_progressbarImage == null)
{
using (SolidBrush b = new SolidBrush(_progressbarColor))
{
g.FillRectangle(b, r);
b.Dispose();
}
}
else
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
if (_revealImage)
{
g.DrawImage(_progressbarImage, r, r, GraphicsUnit.Pixel);
}
else
{
g.DrawImage(_progressbarImage, r);
}
}
g.Dispose();
}
public int Maximum
{
get
{
return _maximum;
}
set
{
_maximum = value;
this.Invalidate();
}
}
public int Value
{
get
{
return _value;
}
set
{
// _value = value;
int oldValue = _value;
// Make sure that the value does not stray outside the valid range.
if (value < 0)
{
_value = 0;
}
else if (value > Maximum)
{
_value = Maximum;
}
else
{
_value = value;
}
// Invalidate only the changed area.
float percent;
Rectangle newValueRect = this.ClientRectangle;
Rectangle oldValueRect = this.ClientRectangle;
// Use a new value to calculate the rectangle for progress.
percent = (float)(_value - 0) / (float)(Maximum - 0);
newValueRect.Width = (int)((float)newValueRect.Width * percent);
// Use an old value to calculate the rectangle for progress.
percent = (float)(oldValue - 0) / (float)(Maximum - 0);
oldValueRect.Width = (int)((float)oldValueRect.Width * percent);
Rectangle updateRect = new Rectangle();
// Find only the part of the screen that must be updated.
if (newValueRect.Width > oldValueRect.Width)
{
updateRect.X = oldValueRect.Size.Width;
updateRect.Width = newValueRect.Width - oldValueRect.Width;
}
else
{
updateRect.X = newValueRect.Size.Width;
updateRect.Width = oldValueRect.Width - newValueRect.Width;
}
updateRect.Height = this.Height;
// Invalidate the intersection region only.
this.Invalidate(updateRect);
}
}
public Image ProgressbarImage
{
get
{
return _progressbarImage;
}
set
{
_progressbarImage = value;
this.Invalidate();
}
}
public Color ProgressbarColor
{
get
{
return _progressbarColor;
}
set
{
_progressbarColor = value;
this.Invalidate();
}
}
public bool RevealImage
{
get
{
return _revealImage;
}
set
{
_revealImage = value;
this.Invalidate();
}
}
}
Thank you for help.

Button needs to change status of an object, seems to work in debugging, but nothing happens?

So, I'm making a homework assignment in which I need to build an intersection along with automatic traffic lights, etc. in Windows Form.
Now, for testing purposes I made a button that changes the traffic light status from on to off (or vice-versa). The status is a enum called LampStatus which uses Aan (On), Uit (Off) and Storing (This doesn't need to get used yet).
The lamp class looks like this:
public class Lamp
{
protected Color kleur;
protected int x, y, straal;
protected LampStatus status;
public Color Kleur
{
get
{
if (status == LampStatus.Uit)
return Color.Gray;
else
return kleur;
}
set { kleur = value; }
}
public LampStatus Status
{
set
{
status = value;
}
get
{
return status;
}
}
// constructor
public Lamp()
{
kleur = Color.Red;
x = y = 0;
straal = 1;
status = LampStatus.Uit;
}
// constructor
public Lamp(Color kleur, int x, int y, int r)
{
this.kleur = kleur;
this.x = x;
this.y = y;
this.straal = r;
status = LampStatus.Uit;
}
public virtual void Teken(Graphics g)
{
if (g != null)
{
SolidBrush Brush = new SolidBrush(Kleur);
g.FillEllipse(Brush, x, y, straal, straal);
g.DrawEllipse(new Pen(Color.Black), x, y, straal, straal);
}
}
}
Now, when I press the button in Form1:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Lamp lamp = new Lamp(Color.Red, 15, 15, 50);
private void Form1_Paint(object sender, PaintEventArgs e)
{
lamp.Teken(e.Graphics);
}
private void testButton_Click(object sender, EventArgs e)
{
if (lamp.Status == LampStatus.Uit)
lamp.Status = LampStatus.Aan;
else
lamp.Status = LampStatus.Uit;
}
}
Nothing seems to happen, though when I debug the object lamp, both the color has changed to Color.Red and the status has changed to LampStatus.Aan.
When I hardcode: lamp.Status = LampStatus.Aan in the Form1_Paint method the color does change to red.
Edit; if there is any confusion, just comment and I'll try to explain.
Using this.Refresh() fixed the problem and made the lamp color properly change.

Round shaped buttons

How do I make a button in a round shape rather than the conventional rectangle.
I am using winforms(2.0)
First make a class. Give it name: "RoundButton".
Then write the code directly as this:
using System;
using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Linq;
using System.Text;
namespace WindowsFormsApplication1
{
public class RoundButton : Button
{
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
GraphicsPath grPath = new GraphicsPath();
grPath.AddEllipse(0, 0, ClientSize.Width, ClientSize.Height);
this.Region = new System.Drawing.Region(grPath);
base.OnPaint(e);
}
}
}
Then, build your application and close this.
Now go to the toolbox and you will see a control named RoundButton.
Then drag and drop this on your Windows form and test it.
GraphicsPath p = new GraphicsPath();
p.AddEllipse(1, 1, button1.Width - 4, button1.Height - 4);
button1.Region = new Region(p);
Code project has many articles about these kinds of things, especially the article RoundButton Windows Control - Ever Decreasing Circles might be of interest since it shows you have to do different kinds of round buttons.
This or this could help if you need to implement your own Button class based on the default Windows Forms button. You can also search online for more examples.
What about 'GDI'?
Implementation Example:
#region <Round Corners> : (Properties)
// [Use Round Corners]
private bool useRoundCorners = false;
[Category("Control Corners"), DisplayName("Round Corners")]
[Description("Set Round Corners.")]
[Browsable(true)]
public bool UseRoundBorders
{
get { return useRoundCorners; }
set { if (useRoundCorners != value) { useRoundCorners = value; Invalidate(); } }
}
// [Ellipse Radius]
private int ellipseRadius = 20;
[Category("Control Corners"), DisplayName("Radius")]
[Description("Set Corner (Ellipse) Radius")]
[Browsable(true)]
public int EllipseRadius
{
get { return ellipseRadius.FixedValue(0, 90); }
set { if (ellipseRadius != value.FixedValue(0, 90)) { ellipseRadius = value; Invalidate(); } }
}
#endregion
#region <Round Corners> : (Draw)
[DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
private static extern IntPtr CreateRoundRectRgn
(
int nLeftRect, // x-coordinate of upper-left corner
int nTopRect, // y-coordinate of upper-left corner
int nRightRect, // x-coordinate of lower-right corner-
int nBottomRect, // y-coordinate of lower-right corner
int nWidthEllipse, // width of ellipse
int nHeightEllipse // height of ellipse
);
/// <summary> Draw Corners (Round or Square). </summary>
/// <param name="e"></param>
private void DrawCorners()
{
if (useRoundCorners) { this.Region = Region.FromHrgn(CreateRoundRectRgn(0, 0, Width, Height, ellipseRadius, ellipseRadius)); }
else { this.Region = Region.FromHrgn(CreateRoundRectRgn(0, 0, Width, Height, 0, 0)); }
}
#endregion
/// <summary> Redraw (Update) the Control. </summary>
/// <param name="e"></param>
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
DrawCorners();
}
Extension to Limit Round Corner Radius (*Range: 0-90)
Author: Salvador
// Extension to Set Min. & Max. Values to Properties
public static class Extensions
{
public static int FixedValue(this int value, int min, int max)
{
if (value >= min && value <= max) { return value; }
else if (value > max) { return max; }
else if (value < min) { return min; }
else { return 1; }
}
}
This is what you want
public class RoundButton : Control
{
private readonly Label lbl;
public RoundButton() : base()
{
lbl = new Label
{
Text = Text,
ForeColor = ForeColor,
BackColor = BackColor,
Font = Font
};
CenterInParent();
}
private void CenterInParent()
{
lbl.Left = (Width - lbl.Width) / 2;
lbl.Top = (Height - lbl.Height) / 2;
}
protected override void OnPaint(PaintEventArgs e)
{
GraphicsPath grPath = new GraphicsPath();
grPath.AddEllipse(0, 0, ClientSize.Width, ClientSize.Height);
Region = new Region(grPath);
base.OnPaint(e);
}
protected override void OnMove(EventArgs e)
{
CenterInParent();
base.OnMove(e);
}
protected override void OnTextChanged(EventArgs e)
{
lbl.Text = Text;
base.OnTextChanged(e);
}
protected override void OnForeColorChanged(EventArgs e)
{
lbl.ForeColor = ForeColor;
base.OnForeColorChanged(e);
}
protected override void OnBackColorChanged(EventArgs e)
{
lbl.BackColor = BackColor;
base.OnBackColorChanged(e);
}
protected override void OnFontChanged(EventArgs e)
{
lbl.Font = Font;
base.OnFontChanged(e);
}
}
Pros:
No cuts
Round
Cons:
Wrong designer loader for round button
public class OptionsMenu : Button
{
public OptionsMenu(int NoOfOptions, Point Location, ControlCollection controls,
Size ButtonSize, int DistanceBetweenOptions)
{
Button[] buttons = new Button[NoOfOptions];
for (int i = 0; i < NoOfOptions; i++)
{
buttons[i] = new Button()
{
Size = ButtonSize,
};
GraphicsPath p = new GraphicsPath();
p.AddEllipse(1, 1, buttons[i].Width - 4, buttons[i].Height - 4);
buttons[i].Region = new Region(p);
buttons[i].Location = new Point(Location.X, Location.Y + DistanceBetweenOptions * i);
controls.Add(buttons[i]);
}
}
}
You can call it like this:
OptionsMenu menu = new OptionsMenu(4, new Point(50, 50), Controls, new Size(20, 20), 40);
Controls.AddRange(new Control[]
{
menu
});

Categories