WinForm - draw resizing frame using a single-pixel border - c#

In a Windows Form with a Resizing Frame, the frame border draws with a raised 3-D look. I'd like it to draw with a flat single pixel border in a color of my choosing.
Is this possible without having to owner draw the whole form?

You could try something like this:
Point lastPoint = Point.Empty;
Panel leftResizer = new Panel();
leftResizer.Cursor = System.Windows.Forms.Cursors.SizeWE;
leftResizer.Dock = System.Windows.Forms.DockStyle.Left;
leftResizer.Size = new System.Drawing.Size(1, 100);
leftResizer.MouseDown += delegate(object sender, MouseEventArgs e) {
lastPoint = leftResizer.PointToScreen(e.Location);
leftResizer.Capture = true;
}
leftResizer.MouseMove += delegate(object sender, MouseEventArgs e) {
if (lastPoint != Point.Empty) {
Point newPoint = leftResizer.PointToScreen(e.Location);
Location = new Point(Location.X + (newPoint.X - lastPoint.X), Location.Y);
Width = Math.Max(MinimumSize.Width, Width - (newPoint.X - lastPoint.X));
lastPoint = newPoint;
}
}
leftResizer.MouseUp += delegate (object sender, MouseEventArgs e) {
lastPoint = Point.Empty;
leftResizer.Capture = false;
}
form.BorderStyle = BorderStyle.None;
form.Add(leftResizer);

Related

Paint Drawline Image into Picturebox Image

In my Form, I have 2 picturebox controls. I loaded a blue background image on pictureBox1 and left pictureBox2 control alone. With the code below I'm able to draw arrows on top of my picturebox1 image.
Goal: On my pictureBox1_MouseUp event I want to add all the arrows which I drew on pictureBox1 to pictureBox2.
Issue: The problem is on my pictureBox1_MouseUp event when I wrote pictureBox2.Image = pictureBox1.Imageit doesn't add the painted arrow which I drew on pictureBox1. It only adds the pictureBox1 image that I assigned in my form load event.
private bool isMoving = false;
private Point mouseDownPosition = Point.Empty;
private Point mouseMovePosition = Point.Empty;
private List<Tuple<Point, Point>> lines = new List<Tuple<Point, Point>>();
Pen _Pen;
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (isMoving)
{
if (pictureBox1.Image == null) e.Graphics.Clear(Color.White);
// Add this line for high quality drawing:
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
AdjustableArrowCap bigArrow = new AdjustableArrowCap(5, 5);
_Pen = new Pen(Color.IndianRed, 3);
_Pen.CustomEndCap = bigArrow;
e.Graphics.DrawLine(_Pen, mouseDownPosition, mouseMovePosition);
_Pen.Dispose();
}
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
isMoving = true;
mouseDownPosition = e.Location;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (isMoving)
{
mouseMovePosition = e.Location;
pictureBox1.Invalidate();
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if (isMoving)
{
lines.Add(Tuple.Create(mouseDownPosition, mouseMovePosition));
}
isMoving = false;
pictureBox2.Image = pictureBox1.Image;
}
Test 1: (Changed pictureBox1_Paint code)
With this code, it draws the arrow on pictureBox2, but it looks like it is drawing multiple arrows.
if (isMoving)
{
if (pictureBox1.Image == null) e.Graphics.Clear(Color.White);
// Add this line for high quality drawing:
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
AdjustableArrowCap bigArrow = new AdjustableArrowCap(5, 5);
_Pen = new Pen(Color.IndianRed, 3);
Bitmap BitImg = (Bitmap)pictureBox1.Image;
_Pen.CustomEndCap = bigArrow;
using (var graphics = Graphics.FromImage(BitImg))
{
graphics.DrawLine(_Pen, mouseDownPosition, mouseMovePosition);
}
pictureBox1.Image = BitImg;
_Pen.Dispose();
}
Test 2: (I took the code from the paint event and pasted it MouseMove Event with some modifications. This uses too much memory and it doesn't draw on pictureBox1 but the arrow is now visible in pictureBox2)
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (isMoving)
{
mouseMovePosition = e.Location;
if (isMoving)
{
AdjustableArrowCap bigArrow = new AdjustableArrowCap(5, 5);
_Pen = new Pen(Color.IndianRed, 3);
BitImg = new Bitmap(pictureBox1.Image);
_Pen.CustomEndCap = bigArrow;
using (var graphics = Graphics.FromImage(BitImg))
{
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.DrawLine(_Pen, mouseDownPosition, mouseMovePosition);
}
_Pen.Dispose();
}
pictureBox1.Invalidate();
}
}
Draw all lines to pictureBox1:
Draw only last line to pictureBox1:
In pictureBox2 control, add Paint event to pictureBox2_Paint.
I suggest you make pen and cap global varriable:
// Make pen and cap global varriable to boost the perfomane.
// Create and delete them each draw will cost alot of CPU
Pen pen = new Pen(Color.IndianRed, 3);
AdjustableArrowCap bigArrow = new AdjustableArrowCap(5, 5);
In Form1() event, add this line:
pen.CustomEndCap = bigArrow;
and do as following:
public partial class Form1 : Form
{
private bool isMoving = false;
private Point mouseDownPosition = Point.Empty;
private Point mouseMovePosition = Point.Empty;
private List<Tuple<Point, Point>> lines = new List<Tuple<Point, Point>>();
public Form1()
{
InitializeComponent();
pen.CustomEndCap = bigArrow;
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
isMoving = true;
mouseDownPosition = e.Location;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (isMoving)
{
mouseMovePosition = e.Location;
pictureBox1.Invalidate();
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if (isMoving)
{
lines.Add(Tuple.Create(mouseDownPosition, mouseMovePosition));
pictureBox2.Invalidate();
}
isMoving = false;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (isMoving)
{
if ((sender as PictureBox).Image == null) e.Graphics.Clear(Color.White);
// Add this line for high quality drawing:
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
e.Graphics.DrawLine(pen, mouseDownPosition, mouseMovePosition);
// If you want draw all previous lines here, add bellow code:
//foreach (var line in lines)
//{
// e.Graphics.DrawLine(pen, line.Item1, line.Item2);
//}
}
}
private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
if ((sender as PictureBox).Image == null) e.Graphics.Clear(Color.White);
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
foreach (var line in lines)
{
e.Graphics.DrawLine(pen, line.Item1, line.Item2);
}
}
}
Above code draw lines to PictureBox control, not to image, this allow you remove some lines or clear all lines you draw to picturebox if you want later.
If you want draw directly to image, things event much easier, you don't need pictureBox2_Paint at all:
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if (isMoving)
{
// You event don't need this line
//lines.Add(Tuple.Create(mouseDownPosition, mouseMovePosition));
if (pictureBox1.Image != null)
{
using (var g = Graphics.FromImage(pictureBox1.Image))
{
g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawLine(pen, mouseDownPosition, mouseMovePosition);
}
pictureBox2.Image = pictureBox1.Image;
}
}
isMoving = false;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (isMoving)
{
if ((sender as PictureBox).Image == null) e.Graphics.Clear(Color.White);
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
e.Graphics.DrawLine(pen, mouseDownPosition, mouseMovePosition);
}
}
private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
}
You have two problems here:
You paint on Control rather than an image. The image remains unchanged.
Both pictureBox1 and pictureBox2 refer to the same image. When you change the image, both controls will be affected on the next paint event.
So in pictureBox1_Paint you need create a copy of pictureBox1.Image (try using Bitmap), then paint on it and assign the updated image to pictureBox1.Image. It will be painted automatically.

runtime Movable rectangle windows store app

I am trying to create runt time rectangle which will move on dragging by mouse. For some reason code is not working.
private void Rectangle_Click(object sender, RoutedEventArgs e)
{
var rec = new Rectangle();
rec.Height = 100;
rec.Width = 100;
rec.Fill = new SolidColorBrush(Colors.Violet);
rec.ManipulationDelta += rec_ManipulationDelta;
board.Children.Add(rec);
}
void rec_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
dragTranslation.X += e.Delta.Translation.X;
dragTranslation.Y += e.Delta.Translation.Y;
dragTranslation = new TranslateTransform();
this.RenderTransform = this.dragTranslation;
}
The problem's when assigning the transforms. Try
private void Rectangle_Click(object sender, RoutedEventArgs e)
{
var rec = new Rectangle();
rec.Height = 100;
rec.Width = 100;
rec.Fill = new SolidColorBrush(Colors.Violet);
rec.ManipulationMode=ManipulationModes.All;
rec.ManipulationDelta += rec_ManipulationDelta;
rec.RenderTransform=new TranslateTransform(); // Create new TranslateTransform and assign to the rectangle
board.Children.Add(rec);
}
void rec_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
Rectangle recSender = (Rectangle) sender; // Get the Rectangle
TranslateTransform ttSender = recSender.RenderTransform as TranslateTransform; // Get the Rectangle's RenderTransform (which is a TranslateTransform)
ttSender.X += e.Delta.Translation.X;
ttSender.Y += e.Delta.Translation.Y;
}

C# Drawing Lines with TextBox

I have an application where I can add a textBox on the screen and move it.
When I add more than two textBox, I double-click on top of two textbox and a line connects both.
My question is: How to make the line move along with the textBox?
code below:
public partial class principal : Form
{
int posMouseFormX, posMouseFormY;
int posMouseTXT_X, posMouseTXT_Y;
int posActTXT_X, posActTXT_Y;
bool txtPressionado = false;
int qntClick;
Pen myPen = new Pen(System.Drawing.Color.DarkGreen, 1);
Graphics Tela;
List<TextBox> listaNós = new List<TextBox>();
List<Point> origem = new List<Point>();
List<Point> destino = new List<Point>();
Point ponto1, ponto2;
ContextMenuStrip menu;
public principal()
{
InitializeComponent();
menu = new ContextMenuStrip();
menu.Items.Add("Remover");
menu.ItemClicked += new ToolStripItemClickedEventHandler(contextMenuStrip1_ItemClicked);
}
//TextBox event when the mouse moves over the TXT
private void txtMover_MouseMove(object sender, MouseEventArgs e)
{
TextBox textBox = sender as TextBox;
posMouseFormX = textBox.Location.X + e.Location.X;
posMouseFormY = textBox.Location.Y + e.Location.Y;
if (txtPressionado == true) moverTxt(textBox);
}
//Retrieve the X and Y coordinates where clicked within the component.
private void txtMover_MouseDown(object sender, MouseEventArgs e)
{
posMouseTXT_X = e.Location.X;
posMouseTXT_Y = e.Location.Y;
txtPressionado = true;
}
private void txtMover_MouseUp(object sender, MouseEventArgs e)
{
txtPressionado = false;
}
private void moverTxt(TextBox a)
{
a.Location = new System.Drawing.Point(posMouseFormX - posMouseTXT_X, posMouseFormY - posMouseTXT_Y);
posActTXT_X = a.Location.X;
posActTXT_Y = a.Location.Y;
System.Drawing.Graphics graphicsObj;
graphicsObj = this.CreateGraphics();
}
//insert new TextBox
private void sb_Inserir_No_Click(object sender, EventArgs e)
{
TextBox noFilho = new TextBox();
noFilho = new System.Windows.Forms.TextBox();
noFilho.Location = new System.Drawing.Point(379, 284);
noFilho.Size = new System.Drawing.Size(100, 30);
noFilho.TabIndex = 20;
noFilho.Text = "";
noFilho.BackColor = Color.White;
posActTXT_X = noFilho.Location.X;
posActTXT_Y = noFilho.Location.Y;
this.Controls.Add(noFilho);
noFilho.TextChanged += new System.EventHandler(this.textBox1_TextChanged);
noFilho.DoubleClick += new System.EventHandler(this.textBox1_Click);
noFilho.MouseUp += new System.Windows.Forms.MouseEventHandler(txtMover_MouseUp);
noFilho.MouseDown += new System.Windows.Forms.MouseEventHandler(txtMover_MouseDown);
noFilho.MouseMove += new System.Windows.Forms.MouseEventHandler(txtMover_MouseMove);
noFilho.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textBox1_KeyDown);
noFilho.ContextMenuStrip = menu;
}
//event to resize the txt on the screen as the content.
private void textBox1_TextChanged(object sender, EventArgs e)
{
TextBox textBox1 = sender as TextBox;
Size size = TextRenderer.MeasureText(textBox1.Text, textBox1.Font);
textBox1.Width = size.Width + 10;
textBox1.Height = size.Height;
}
//Event to control the connection between two give us when double click on the textbox
private void textBox1_Click(object sender, EventArgs e)
{
TextBox textBox1 = sender as TextBox;
int meio = textBox1.Size.Width / 2;
Tela = CreateGraphics();
qntClick = qntClick + 1;
if (this.qntClick == 1)
{
origem.Add(ponto1);
ponto1 = new Point(textBox1.Location.X + meio, textBox1.Location.Y);
}
if (this.qntClick == 2)
{
qntClick = 0;
destino.Add(ponto2);
ponto2 = new Point(textBox1.Location.X + meio, textBox1.Location.Y);
DesenhaSeta(Tela, ponto1, ponto2);
}
}
//draw arrow between two TXT
void DesenhaSeta(Graphics Tela, Point x, Point y)
{
myPen.StartCap = LineCap.Triangle;
myPen.EndCap = LineCap.ArrowAnchor;
Tela.DrawLine(myPen, x, y);
}
private void contextMenuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
ContextMenuStrip menu = sender as ContextMenuStrip;
//recuperando o controle associado com o contextmenu
Control sourceControl = menu.SourceControl;
DialogResult result = MessageBox.Show("Tem Certeza que deseja remover o nó selecionado?", "Excluir", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if(result == DialogResult.Yes)
{
sourceControl.Dispose();
}
}
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
// Determine whether the key entered is the F1 key. Display help if it is.
if (e.KeyCode == Keys.Space)
{
TextBox textBox1 = sender as TextBox;
novoNó tela = new novoNó(textBox1.Text);
tela.Show();
}
}
}
Each control, TextBox included has a Move, event. Put an Invalidate() call there!
The lines should be drawn in the Paint event of the container that holds the TextBoxes, probably the Form; if it is the Form indeed call this.Invalidate().
Please move the line drawing code out of the DoubleClick event into the Paint event or else the lines will not persist, say minimize/maximize events or other situation, when the system has to redraw the application!
You probably will need to create a data structure to maintain information about which TextBox-pairs need to be connected, maybe a List<Tuple> or a List<someStructure>. This would get filled/modified in the DoubleClick event, then call this.Invalidate() and in the Form.Paint you have a foreach loop over the list of TextBox-pairs..
If you are drawing on the Form do make sure to turn DoubleBuffered on!
Update: To compare the reults here is a minimal example the expects two TextBoxes on a Form:
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
this.DoubleBuffered = true;
pairs.Add(new Tuple<Control, Control>(textBox1, textBox2));
}
List<Tuple<Control, Control>> pairs = new List<Tuple<Control, Control>>();
Point mDown = Point.Empty;
private void Form2_Paint(object sender, PaintEventArgs e)
{
foreach (Tuple<Control, Control> cc in pairs)
drawConnection(e.Graphics, cc.Item1, cc.Item2);
}
void drawConnection(Graphics G, Control c1, Control c2)
{
using (Pen pen = new Pen(Color.DeepSkyBlue, 3f) )
{
Point p1 = new Point(c1.Left + c1.Width / 2, c1.Top + c1.Height / 4);
Point p2 = new Point(c2.Left + c2.Width / 2, c2.Top + c2.Height / 4);
G.DrawLine(pen, p1, p2);
}
}
void DragBox_MouseDown(object sender, MouseEventArgs e)
{
mDown = e.Location;
}
void DragBox_MouseMove(object sender, MouseEventArgs e)
{
TextBox tb = sender as TextBox;
if (e.Button == MouseButtons.Left)
{
tb.Location = new Point(e.X + tb.Left - mDown.X, e.Y + tb.Top - mDown.Y);
}
}
void DragBox_MouseUp(object sender, MouseEventArgs e)
{
mDown = Point.Empty;
}
private void DragBox_Move(object sender, EventArgs e)
{
this.Invalidate();
}
}

What is wrong with my Rectangle drawing coordinates?

I'm trying to use the following code to allow the user to draw a rectangle around something on the screen, to select that area.
public partial class MainWindow : Window
{
public enum DrawMode
{
Move,
Draw
}
private DrawMode _drawmode;
private Point _startPoint;
private Rectangle _rectangle;
public MainWindow()
{
_rectangle = new Rectangle();
_rectangle.Stroke = new SolidColorBrush(Colors.Black);
_rectangle.StrokeThickness = 1;
InitializeComponent();
}
private void MapImageOnMouseDown(object sender, MouseButtonEventArgs e)
{
_drawmode = DrawMode.Draw;
_startPoint = e.GetPosition(DrawCanvas);
}
private void MapImageOnMouseUp(object sender, MouseButtonEventArgs e)
{
_drawmode = DrawMode.Move;
}
private void MapImageOnMouseMove(object sender, MouseEventArgs e)
{
if (_drawmode == DrawMode.Draw)
{
DrawCanvas.Children.Remove(_rectangle);
var endPoint = e.GetPosition(DrawCanvas);
var width = Math.Abs(endPoint.X - _startPoint.X);
var height = Math.Abs(endPoint.Y - _startPoint.Y);
_rectangle.Width = width;
_rectangle.Height = height;
DrawCanvas.Children.Add(_rectangle);
Canvas.SetTop(_rectangle, _startPoint.X);
Canvas.SetLeft(_rectangle, _startPoint.Y);
}
}
}
However, although when I mousedown, the top left of the rectangle is nowhere near the point that I mousedown at. Are there different coordinate systems or something?
You have confused X and Y (or Left and Top). Change
Canvas.SetTop(_rectangle, _startPoint.X);
Canvas.SetLeft(_rectangle, _startPoint.Y);
to
Canvas.SetLeft(_rectangle, _startPoint.X);
Canvas.SetTop(_rectangle, _startPoint.Y);
It is also not necessary to remove and add the Rectangle each time its position changes:
public MainWindow()
{
InitializeComponent();
_rectangle = new Rectangle
{
Stroke = Brushes.Black,
StrokeThickness = 1
};
DrawCanvas.Children.Add(_rectangle); // add it once
}
private void MapImageOnMouseMove(object sender, MouseEventArgs e)
{
if (_drawmode == DrawMode.Draw)
{
var endPoint = e.GetPosition(DrawCanvas);
_rectangle.Width = Math.Abs(endPoint.X - _startPoint.X);
_rectangle.Height = Math.Abs(endPoint.Y - _startPoint.Y);
Canvas.SetLeft(_rectangle, Math.Min(_startPoint.X, endPoint.X));
Canvas.SetTop(_rectangle, Math.Min(_startPoint.Y, endPoint.Y));
}
}
Point startPoint = new Point(0, 0);
Point endPoint = new Point(1, 1);
bool mousePress = false;
control.MouseDown += (s, e) =>
{
startPoint = new Point(e.X,e.Y);
mousePress = true;
};
control.MouseUp += (s, e) =>
{
mousePress = false;
};
control.MouseMove += (s, e) =>
{
if (mousePress)
{
endPoint = new Point(e.X, e.Y);
control.Invalidate();
}
};
control.Paint += (s, e) =>
{
Graphics g = e.Graphics;
g.DrawRectangle(Pens.Black, new Rectangle(startPoint.X, startPoint.Y,endPoint.X-startPoint.X, endPoint.Y-startPoint.Y));
};
I have been doing drawing in the past and this worked for me for rectangles, dont know how it translates to wpf tho

Retain one element and refresh another on drawing on a picturebox control?

I want to draw a continous line on a picturebox and also a cross located at the cursor. How can it be done to paint the cursor only at the mouse position (and clear it on the next move) and retain the line?
This is just to give you an idea of how to approach this...
Add a PictureBox to a Form. Run it and click and drag in the PictureBox:
public partial class Form1 : Form
{
Point cursor;
Point lastPoint;
System.Drawing.Drawing2D.GraphicsPath curPath = null;
private List<System.Drawing.Drawing2D.GraphicsPath> paths = new List<System.Drawing.Drawing2D.GraphicsPath>();
public Form1()
{
InitializeComponent();
pictureBox1.MouseDown += new MouseEventHandler(pictureBox1_MouseDown);
pictureBox1.MouseMove += new MouseEventHandler(pictureBox1_MouseMove);
pictureBox1.Paint += new PaintEventHandler(pictureBox1_Paint);
}
void pictureBox1_Paint(object sender, PaintEventArgs e)
{
foreach (System.Drawing.Drawing2D.GraphicsPath gp in paths)
{
e.Graphics.DrawPath(Pens.Red, gp);
}
e.Graphics.DrawLine(Pens.Black, new Point(cursor.X, 0), new Point(cursor.X, pictureBox1.Height));
e.Graphics.DrawLine(Pens.Black, new Point(0, cursor.Y), new Point(pictureBox1.Width, cursor.Y));
}
void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
lastPoint = new Point(e.X, e.Y);
curPath = new System.Drawing.Drawing2D.GraphicsPath();
paths.Add(curPath);
}
}
void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
cursor = new Point(e.X, e.Y);
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
Point newPoint = new Point(e.X, e.Y);
curPath.AddLine(lastPoint, newPoint);
lastPoint = newPoint;
pictureBox1.Refresh();
}
pictureBox1.Refresh();
}
}

Categories