Draw a connector between shapes in wpf - c#

I am creating some shapes from code behind dynamically and adding them to a Grid and further add the Grid to Canvas.
So when I double click on a shape I should be able to add some text which works fine. Now lets say I have two shapes on the Canvas and when I try to draw a line between these shapes for some reason the first shape gets pulled away to the bottom and the line starts from the middle of first shape.
I want the shape not to change the position and the line should start from the bottom of first shape. Please see the image for my problem.
Please help with your thoughts. Here is my code. Also I tried numerous posts eg: Getting the top left coordinates of a WPF UIElement.
But none of them seem to help.
private void CvsSurface_OnDrop(object sender, DragEventArgs e) //In this event I am creating a shape dynamically and adding to a grid which is then added to a canvas.
{
Shape result = null;
Object droppedData = e.Data; //This part is not important
/*Translate Drop Point in reference to Stack Panel*/
Point dropPoint = e.GetPosition(this.cvsSurface);
//Console.WriteLine(dropPoint);
//Label lbl = new Label();
//lbl.Content = draggedItem.Content;
UIElement element = draggedItem.Content as UIElement;
Shape s = element as Shape;
if (s is Ellipse)
{
Ellipse ellipse = new Ellipse()
{
Height = s.Height,
Width = s.Width,
Fill = s.Fill
};
result = ellipse;
}
else if (s is Rectangle)
{
Rectangle rectangle = new Rectangle()
{
Height = s.Height,
Width = s.Width,
Fill = s.Fill
};
result = rectangle;
}
Grid sp = new Grid();
sp.Children.Add(result);
sp.MouseLeftButtonDown += Sp_MouseLeftButtonDown;
sp.MouseLeftButtonUp += Sp_MouseLeftButtonUp;
//sp.PreviewMouseLeftButtonUp += Sp_PreviewMouseLeftButtonUp;
//sp.MouseLeftButtonUp += Sp_MouseLeftButtonUp;
cvsSurface.Children.Add(sp);
Canvas.SetLeft(sp, dropPoint.X);
Canvas.SetTop(sp, dropPoint.Y);
}
private void Sp_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) // The purpose of this event lets say when some one clicks on a shape and drags the mouse to the other shape and when mouse up I want to draw a line between the shapes.
{
bool mouserelease = System.Windows.Input.Mouse.LeftButton == MouseButtonState.Pressed;
if (!mouserelease)
{
x2 = e.GetPosition(stackpanel).X;
y2 = e.GetPosition(stackpanel).Y;
Line l = new Line();
l.X1 = x1;
l.Y1 = y1;
l.X2 = x2;
l.Y2 = y2;
l.Margin = new Thickness(0, 19, 0, 0);
l.Stroke = new SolidColorBrush(Colors.Black);
l.StrokeThickness = 2;
stackpanel.Children.Add(l);
}
}
private void Sp_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) //This method lets say if user clicks twice then he wants to add some text or if he single clicks then I am assuming he is trying to a drag and draw a line
{
stackpanel = sender as Grid; //Sorry, the stackpanel is a global variable name of type Grid. Its actually Grid stackpanel;
if (e.ClickCount == 2)
{
dialog = new UserDialog()
{
DataContext = this,
Height = 180,
Width = 400,
MaxHeight = 180,
MaxWidth = 400
};
dialog.ShowDialog();
}
else
{
x1 = e.GetPosition(stackpanel).X + 18;
y1 = e.GetPosition(stackpanel).Y + 18;
//x1 = GetPosition(stackpanel, cvsSurface).X;
//y1 = GetPosition(stackpanel, cvsSurface).Y;
}
}

Related

Create vertical scrollbar with multiple pictures

I am creating a project that needs to have a vertical scroll bar with multiple pictures like the server explorer in discord:
For example:
how can I mimic in WinForms in C# (not only having pictures scrolling but also the pictures can have events attached to them?
First you need add a Parent Panel, i'm used the pnServers.
Set property value:
AutoScroll = True;
On Code Behind you can create a List of Rounded Pictures.
private void DiscordServerBarExample_Load(object sender, System.EventArgs e)
{
// Example, in your case this looping is based on return (Database, Api, ...).
for (int i = 1; i <= 10; i++)
{
Panel pnServer = new Panel()
{
Dock = DockStyle.Top,
Height = pnServers.Width,
Padding = new Padding(10)
};
RoundedPictureBox serverImage = new RoundedPictureBox()
{
SizeMode = PictureBoxSizeMode.CenterImage,
Dock = DockStyle.Fill
};
serverImage.Image = Properties.Resources._255352;
pnServer.Controls.Add(serverImage);
pnServers.Controls.Add(pnServer);
}
}
Rounded Picture Box Code:
public class RoundedPictureBox : PictureBox
{
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (GraphicsPath gp = new GraphicsPath())
{
gp.AddEllipse(0, 0, Width - 1, Height - 1);
Region rg = new Region(gp);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.DrawEllipse(new Pen(new SolidBrush(this.BackColor), 1), 0, 0, this.Width - 1, this.Height - 1);
Region = rg;
}
}
}
And this is final result.

Drawing Ellipse on Canvas with "negative" width/height using mouse events

On MouseDownEvent I set upper left corner of Ellipse I'm trying to draw.
public MyCircle(Point location)
{
ellipseObject = new Ellipse
{
Stroke = Brushes.Black,
StrokeThickness = 2,
Margin = new Thickness(location.X, location.Y, 0, 0)
};
}
Then on MouseMoveEvent I update Width and Height properties and it works fine as long as I don't move mouse above or/and to the left of my Ellipse upper left corner, in that case I'm getting exception that these properties can't be negative (which of course makes perfect sense).
public void Draw(Point location)
{
if (ellipseObject != null)
{
ellipseObject.Width = location.X - ellipseObject.Margin.Left;
ellipseObject.Height = location.Y - ellipseObject.Margin.Top;
}
}
The problem doesn't exist with drawing lines:
public void Draw(Point location)
{
lineObject.X2 = location.X;
lineObject.Y2 = location.Y;
}
I know it's trivial, but I'm completely stuck on this. How should I handle drawing Ellipses?
I had this EXACT problem when trying to create a crop tool. Problem is that you need to create if statements for when the cursor goes negative X or negative Y from your starting point. For starters, you'll need to have a global Point that you mark as your 'start' point. Also specify a global current point position that we'll talk about in a minute.
public Point startingPoint;
public Point currentPoint;
Then, make sure you have an onMouseDown event on whatever control you are trying to put the ellipse on.
private void control_MouseDown(object sender, MouseEventArgs e)
{
startingPoint.X = e.X;
startingPoint.Y = e.Y;
}
Then, you need to create if statements in your MouseMove event to check with point (current mouse position, or starting point) has a lower X/Y value
private void control_MouseMove(object sender, MouseEventArgs e)
{
//The below point is what we'll draw the ellipse with.
Point ellipsePoint;
Ellipse ellipseObject = new Ellipse();
currentPoint.X = e.X;
currentPoint.Y = e.Y;
//Then we need to get the proper width/height;
if (currentPoint.X >= startingPoint.X)
{
ellipsePoint.X = startingPoint.X;
ellipseObject.Width = currentPoint.X - startingPoint.X;
}
else
{
ellipsePoint.X = currentPoint.X;
ellipseObject.Width = startingPoint.X - currentPoint.X;
}
if (currentPoint.Y >= startingPoint.Y)
{
ellipsePoint.Y = startingPoint.Y;
ellipseObject.Height = currentPoint.Y - startingPoint.Y;
}
else
{
ellipsePoint.Y = currentPoint.Y;
ellipseObject.Height = startingPoint.Y - currentPoint.Y;
}
ellipseObject.Stroke = Brushes.Black;
ellipseObject.StrokeThickness = 2;
ellipseObject.Margin = new Thickness(ellipsePoint.X, ellipsePoint.Y, 0, 0);
}
Hope this helps!
Save the origin point separately and set the X and Y properties of the Ellipse's Margin to the mouse position and the Width and Height to the distances between the mouse and origin point.
Untested:
public MyCircle(Point location)
{
ellipseObject = new Ellipse
{
Stroke = Brushes.Black,
StrokeThickness = 2,
Margin = new Thickness(location.X, location.Y, 0, 0)
Tag = new Point(location.X, location.Y)
};
}
public void Draw(Point location)
{
if (ellipseObject != null)
{
Point o = (Point)ellipseObject.Tag;
double x = Math.Min(location.X, o.Left);
double y = Math.Min(location.Y, o.Top);
double width = Math.Abs(Math.Max(location.X, o.Left) - x);
double height = Math.Abs(Math.Max(location.Y, o.Top) - y);
ellipseObject.Margin.X = x;
ellipseObject.Margin.Y = y;
ellipseObject.Width = width;
ellipseObject.Height = height;
}
}

Create new label on location of mouse position

Hello I have got this code:
private Label newLabel = new Label();
Int32 mouseX;
Int32 mouseY;
private void form_MouseMove(object sender, MouseEventArgs e)
{
mouseY = Cursor.Position.Y;
mouseX = Cursor.Position.X;
}
private void button1_Click(object sender, EventArgs e)
{
int txt = Int32.Parse(textBox1.Text);
for (int i = 0; i < txt; i++)
{
newLabel = new Label();
newLabel.Location = new Point(mouseY, mouseX);
newLabel.Size = new System.Drawing.Size(25, 25);
newLabel.Text = i.ToString();
newLabel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
newLabel.ForeColor = Color.Red;
newLabel.Font = new Font(newLabel.Font.FontFamily.Name, 10);
newLabel.Font = new Font(newLabel.Font, FontStyle.Bold);
newLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
newLabel.MouseMove += new MouseEventHandler(this.MyControl_MouseMove);
newLabel.MouseDown += new MouseEventHandler(this.MyControl_MouseDown);
this.Controls.Add(newLabel);
}
}
And I try to make it create a label according to where the mouse is, but it seems that it is creating with location within the whole display. I thought that if I assign the coordinates to form mouse move it would get coordinates within the form.
May someone help me solve this out please?
The Cursor.Position coordinate is relative to the whole screen. You need a position relative to the upper left of your form. You could simply get that info from the MouseEventArgs passed to your MouseMove event handler
private void form_MouseMove(object sender, MouseEventArgs e)
{
mouseY = e.Location.Y;
mouseX = e.Location.X;
}
The MouseEventArgs.Location property is (according to MSDN)
A Point that contains the x- and y- mouse coordinates, in pixels,
relative to the upper-left corner of the form.
Steve is correct, and in order to convert screen coordinates to control or form coordinates you can use method described here:
How to convert screen coordinates to form relative coordinates (winforms)?
In your case:
Point clientPoint = PointToClient( new Point( e.X, e.Y ) );

How to Add Rectangle one after other vertically on Canvas in WPF using C#?

I have a Canvas in my WPF application. I am adding the Rectangle on button click. The Width is Fixed but, Height is value entered by user in TextBox/GridCell.
When i add rectangle on Canvas with specifying Height. it adds the rectangle but, it doesnt appear one after other. Any idea?
In .xaml.cs:
int width=200;
Reactangle rect;
static int val=0;
Protected void Add()
{
rect = new Rectangle();
rect.Stroke = Brushes.Red;
rect.StrokeThickness = 1;
rect.Height = Convert.ToInt32(txtheight.Text);
rect.Width = width;
Canvas.SetLeft(rect,100);
Canvas.SetTop(rect,rect.Height);
rect.Tag = val;
canvasboard.Children.Add(rect);
val=val+1;
}
This adds Rectangle but not Exactly one after other on canvas.
<Canvas Name="canvasboard" Background="White" Margin="2">
</Canvas>
<TextBox Name="txtheight" Width="150"/>
Note: I cant use WrapPanel or StackPanel for this form. and want to make changes in existing code.
Help Appreciated!
If all your adding is sequential elements vertically into this Canvas, you can do it without having to add a new variable in class scope as well.
private void Add() {
rect = new Rectangle {
Stroke = Brushes.Red,
StrokeThickness = 1,
Height = Convert.ToDouble(txtheight.Text),
Width = width
};
Canvas.SetLeft(rect, 100);
double canvasTop = 0.0;
if (canvasboard.Children.Count > 0) {
var lastChildIndex = canvasboard.Children.Count - 1;
var lastChild = canvasboard.Children[lastChildIndex] as FrameworkElement;
if (lastChild != null)
canvasTop = Canvas.GetTop(lastChild) + lastChild.Height + 1;
}
Canvas.SetTop(rect, canvasTop);
rect.Tag = val++;
canvasboard.Children.Add(rect);
}
Try storing a local variable that maintains the combined height of all Rectangles:
private double _top = 0;
protected void Add()
{
var rect = new Rectangle();
rect.Stroke = Brushes.Red;
rect.StrokeThickness = 1;
rect.Height = double.Parse(txtheight.Text);
rect.Width = 20;
Canvas.SetLeft(rect, 100);
Canvas.SetTop(rect, _top);
_top += rect.Height;
rect.Tag = val;
canvasboard.Children.Add(rect);
val = val + 1;
}

Extracting an Event (tabControl_DrawItem) to Class Library

I have the following code under a TabConttrols DrawItem event that I am trying to extract into a class file. I am having trouble since it is tied to an event. Any hints or pointers would be greatly appreciated.
private void tabCaseNotes_DrawItem(object sender, DrawItemEventArgs e)
{
TabPage currentTab = tabCaseNotes.TabPages[e.Index];
Rectangle itemRect = tabCaseNotes.GetTabRect(e.Index);
SolidBrush fillBrush = new SolidBrush(Color.Linen);
SolidBrush textBrush = new SolidBrush(Color.Black);
StringFormat sf = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
//If we are currently painting the Selected TabItem we'll
//change the brush colors and inflate the rectangle.
if (System.Convert.ToBoolean(e.State & DrawItemState.Selected))
{
fillBrush.Color = Color.LightSteelBlue;
textBrush.Color = Color.Black;
itemRect.Inflate(2, 2);
}
//Set up rotation for left and right aligned tabs
if (tabCaseNotes.Alignment == TabAlignment.Left || tabCaseNotes.Alignment == TabAlignment.Right)
{
float rotateAngle = 90;
if (tabCaseNotes.Alignment == TabAlignment.Left)
rotateAngle = 270;
PointF cp = new PointF(itemRect.Left + (itemRect.Width / 2), itemRect.Top + (itemRect.Height / 2));
e.Graphics.TranslateTransform(cp.X, cp.Y);
e.Graphics.RotateTransform(rotateAngle);
itemRect = new Rectangle(-(itemRect.Height / 2), -(itemRect.Width / 2), itemRect.Height, itemRect.Width);
}
//Next we'll paint the TabItem with our Fill Brush
e.Graphics.FillRectangle(fillBrush, itemRect);
//Now draw the text.
e.Graphics.DrawString(currentTab.Text, e.Font, textBrush, (RectangleF)itemRect, sf);
//Reset any Graphics rotation
e.Graphics.ResetTransform();
//Finally, we should Dispose of our brushes.
fillBrush.Dispose();
textBrush.Dispose();
}
Depends on what you're trying to achieve. You could always sub class TabControl or you could encapsulate the drawing code in a class that you pass a TabControl to.
public class TabRenderer
{
private TabControl _tabControl;
public TabRenderer(TabControl tabControl)
{
_tabControl = tabControl;
_tabControl.DrawMode = TabDrawMode.OwnerDrawFixed;
_tabControl.DrawItem += TabControlDrawItem;
}
private void TabControlDrawItem(object sender, DrawItemEventArgs e)
{
// Your drawing code...
}
}

Categories

Resources