C#: Drag drop controls on surface - c#

Is there a way that one can make a control, such as a textbox, drag-droppable in C#?
I want the user to have the ability to click and hold the control with the mouse and drag it around on its surface and drop it anywhere within that surface.
Anyone has any idea how to implement this?

This answer helped me a lot. It's working great on any type of Control and Container.

If your control is moving within one container (e.g. panel), you can override OnMouseDown / OnMouseMove events, and adjust the Location property of the control.
Based on your question, it does not seem that you need full drag-and-drop (moving data between different controls or even applications).

if you're trying to drag an item from outside the silverlight container, then your best bet is to check out silverlight 4 beta
public MainPage()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainPage_Loaded);
// wire up the various Drop events
InstallButton.Drop += new DragEventHandler(InstallButton_Drop);
InstallButton.DragOver += new DragEventHandler(InstallButton_DragOver);
InstallButton.DragEnter += new DragEventHandler(InstallButton_DragEnter);
InstallButton.DragLeave += new DragEventHandler(InstallButton_DragLeave);
}
void InstallButton_Drop(object sender, DragEventArgs e)
{
IDataObject foo = e.Data; // do something with data
}

This used to be so easy in VB6. But now we really only have what used to be called OleDrag.
Anyway, the following code should show you how. You just need a single label (dragDropLabel), and set the AllowDrop property of the form (DragDropTestForm) to True.
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 DragDropTest
{
public partial class DragDropTestForm : Form
{
// Negative offset to drop location, to adjust for position where a drag starts
// on a label.
private Point _labelOffset;
// Save the full type name for a label, since this is used to test for the control type.
private string labelTypeName = typeof(Label).FullName;
public DragDropTestForm()
{
InitializeComponent();
}
private void dragDropLabel_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_labelOffset = new Point(-e.X, -e.Y);
}
}
private void dragDropLabel_MouseMove(object sender, MouseEventArgs e)
{
const double minimumDragDistance = 4;
const double minimumDragDistanceSquared = minimumDragDistance * minimumDragDistance;
if (e.Button == MouseButtons.Left)
{
// Minimum n pixel movement before drag starts.
if (((Math.Pow(_labelOffset.X - e.X, 2)) + Math.Pow(_labelOffset.Y - e.Y, 2)) >= minimumDragDistanceSquared)
{
dragDropLabel.DoDragDrop(dragDropLabel, DragDropEffects.Move);
}
}
}
private void DragDropTestForm_DragOver(object sender, DragEventArgs e)
{
IDataObject data = e.Data;
string[] formats = data.GetFormats();
if (formats[0] == labelTypeName)
{
e.Effect = DragDropEffects.Move;
}
}
private void DragDropTestForm_DragDrop(object sender, DragEventArgs e)
{
IDataObject data = e.Data;
string[] formats = data.GetFormats();
if (formats[0] == labelTypeName)
{
Label label = (Label) data.GetData(formats[0]);
if (label == dragDropLabel)
{
Point newLocation = new Point(e.X, e.Y);
newLocation.Offset(_labelOffset);
dragDropLabel.Location = this.PointToClient(newLocation);
}
}
}
}
}

Related

How does it go to its first position if I don't drag the button where I want to drag it?

enter image description hereI have 9 buttons. Three are located above, 3 are located below them. All 3 buttons are on the right side. The antonyms of the words written on the buttons above are written on the buttons on the right. I would like to place the buttons with these antonyms on the buttons at the bottom. The buttons at the bottom are colored and their text is the same as the buttons on the right, but their text is not visible because it is the same as the button color. So the buttons on the bottom look like a colored box. That's what I want to do now; for example, button 1 = cold and button7 = hot. I want to place this button 7 on button 4 located below button 1, but if I want to place it on button 5 I want it to return to its original position. In the code I wrote, as soon as I press button7, it goes directly to button4, I can't try the other buttons. how can I try other buttons and return them to the first position in Visual studio form application?
enter code here
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace materyalll
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
startlocation = new Point(button7.Left, button7.Top);
startlocation2 = new Point(button8.Left, button8.Top);
startlocation3 = new Point(button9.Left, button9.Top);
}
Point location, location2, location3, startlocation, startlocation2, startlocation3;
private void button7_MouseDown(object sender, MouseEventArgs e)
{
location = e.Location;
}
private void button7_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
button7.Left += e.X - (location.X);
button7.Top += e.Y - (location.Y);
}
}
private void button7_MouseUp(object sender, MouseEventArgs e)
{
if (button7.Text == button4.Text)
button7.Location = button4.Location;
else if (button7.Text != button5.Text)
button7.Location = startlocation;
else if (button7.Text != button6.Text)
button7.Location = startlocation;
}
private void button8_MouseDown(object sender, MouseEventArgs e)
{
location2 = e.Location;
}
private void button8_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
button8.Left += e.X - (location2.X);
button8.Top += e.Y - (location2.Y);
}
}
private void button8_MouseUp(object sender, MouseEventArgs e)
{
if (button8.Text == button5.Text)
button8.Location = button5.Location;
else if (button8.Text != button4.Text)
button8.Location = startlocation2;
else if (button8.Text != button6.Text)
button8.Location = startlocation2;
}
private void button9_MouseDown(object sender, MouseEventArgs e)
{
location3 = e.Location;
}
private void button9_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
button9.Left += e.X - (location3.X);
button9.Top += e.Y - (location3.Y);
}
}
private void button9_MouseUp(object sender, MouseEventArgs e)
{
if (button9.Text == button6.Text)
button9.Location = button6.Location;
else if (button9.Text != button4.Text)
button9.Location = startlocation3;
else if (button9.Text != button5.Text)
button9.Location = startlocation3;
}
}
}
Store the original location of the Button in the .Tag property of the Button itself. When the user releases the mouse, see if the RECTANGLES of the correct target button and the current button INTERSECT. If they don't, snap back to the stored location in the Tag; otherwise snap to the location of the correct button.
Here it is with button7:
public Form1()
{
InitializeComponent();
button7.Tag = button7.Location;
button8.Tag = button8.Location;
button9.Tag = button9.Location;
}
private void button7_MouseUp(object sender, MouseEventArgs e)
{
if (button7.Bounds.IntersectsWith(button4.Bounds)) {
button7.Location = button4.Location;
}
else
{
button7.Location = (Point)button7.Tag;
}
}
The code for the other two buttons would be very similar.
Try something like this:
Dictionary<string, Point> originalPoints = new Dictionary<string, Point>();
originalPoints.Add(nameof(button1), new Point(button1.Left, button1.Top));
// repeat this for all buttons
void SetToOriginalPosition(Button button)
{
Point p = originalPoints[nameof(button)];
button.Left = p.X;
button.Top = p.Y;
}

flickering scroll bar on side of panel whenever I use a vscrollbar I made

I have a panel with picture boxes in it and I made a seperate vscrollbar since I dont really like the one that the panel generates when you enable autoscroll, so ive got the scroll bar on the right to work but whenever I use it a scrollbar on the side of the panel starts flickering it only becomes visible when I use the scroll bar I made but it flickers like crazy until I stop moving the vscrollbar, this is the form:
and this is what ive got in the code of the scroll bar and the panel
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using MetroFramework.Forms;
using MetroFramework;
namespace WindowsForm1
{
public partial class Form2 : MetroForm
{
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
}
private void vScrollBar1_Scroll(object sender, ScrollEventArgs e)
{
panel1.VerticalScroll.Visible = false;
vScrollBar1.Refresh();
panel1.Refresh();
panel1.Select();
panel1.VerticalScroll.Value = 0;
panel1.AutoScroll = false;
panel1.VerticalScroll.Enabled = false;
}
private void panel1_Scroll(object sender, ScrollEventArgs e)
{
panel1.Invalidate();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
VerticalScroll.Enabled = false;
VerticalScroll.Visible = false;
AutoScroll = false;
}
}
}
nothing I do seems to fix the problem
The AutoScroll property should be set to true in order to enable the Panel to have a virtual size. The virtual size is the size of the control itself plus the size required to accommodate the child controls. Its automatically calculated and its the size of the DisplayRectangle property. The internal VScroll and HScroll controls are updated accordingly whenever that size changes.
As a workaround to this problem, you could use an outer Panel to hide the vertical scroll bar of the inner Panel, and use the custom VScrollBar to set the scroll position. This article is an example.
In the designer, drop a new Panel to host the main one and edit the code as follows.
public partial class Form2 : MetroForm
{
public Form2()
{
InitializeComponent();
pnlOuter.AutoScroll = false; // The new Panel.
pnlInner.AutoScroll = true; // The PictureBox controls Panel.
pnlInner.AutoScrollMargin = new Size(0, 20);
pnlInner.MouseWheel += (s, e) =>
vScrollBar1.Value = pnlInner.VerticalScroll.Value;
pnlOuter.Resize += (s, e) => ResetVScrollBar();
vScrollBar1.Scroll += (s, e) =>
{
pnlInner.AutoScrollPosition = new Point(0, vScrollBar1.Value);
pnlInner.VerticalScroll.Value = vScrollBar1.Value;
};
}
private void Form2_Load(object sender, EventArgs e)
{
ResetVScrollBar();
}
private void ResetVScrollBar()
{
if (pnlInner.Width == 0 || pnlInner.Height == 0) return;
pnlInner.SetBounds(0, 0,
pnlOuter.Width + SystemInformation.VerticalScrollBarWidth * 2,
pnlOuter.Height);
vScrollBar1.Minimum = pnlInner.VerticalScroll.Minimum;
vScrollBar1.Maximum = pnlInner.DisplayRectangle.Height;
vScrollBar1.LargeChange = vScrollBar1.Maximum / vScrollBar1.Height + pnlInner.Height;
vScrollBar1.SmallChange = 15;
vScrollBar1.Value = Math.Abs(pnlInner.AutoScrollPosition.Y);
vScrollBar1.Enabled = pnlInner.Height < vScrollBar1.Maximum;
}
}

Tagging an array of bitmaps with corresponding string values

I am creating a "rock paper scissors lizard spock" game for school. I am stuck on the part of the assignment where we need to tag the bitmap images with the corresponding names using the tag property. I have created an array of names, and an array of bitmap images.
I am unsure of how to use the tag property to do this. The exact instructions are:
Create an array of string objects, and initialize it to contain the string values "rock", "paper", "scissors", "lizard", "spock" Add code to each of the bitmaps with the corresponding string values. (ex. bitmap "properties.resources.rock" should be tagged with the string "rock".
private void Form1_Load(object sender, EventArgs e)
{
string[] names =
{
"rock",
"paper",
"scissors",
"lizard" ,
"spock"
};
Bitmap[] bitmaps =
{
Properties.Resources.rock,
Properties.Resources.paper,
Properties.Resources.scissors,
Properties.Resources.lizard,
Properties.Resources.spock,
};
}
I've tried adding rock.Tag = properties.resources.rock.
I've tried names[0].tag = properties.resources.rock.
I've also tried properties.resources.rock.Tag.
The professor hasn't shown us how to use the tag property yet, so I'm sure I'm just missing something obvious. I am new to coding and any help is appreciated.
My full code is here, though it is very incomplete.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Lab5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
string[] names =
{
"rock",
"paper",
"scissors",
"lizard" ,
"spock"
};
Bitmap[] bitmaps =
{
Properties.Resources.rock,
Properties.Resources.paper,
Properties.Resources.scissors,
Properties.Resources.lizard,
Properties.Resources.spock,
};
// Following array will not actually be used
// PictureBox[] pics = new PictureBox[bitmaps.Length];
for (int i = 0; i < bitmaps.Length; i++)
{
PictureBox pic = new PictureBox();
pic.Image = bitmaps[i];
pic.Location = new Point(20 + (i * 100), 20);
pic.SizeMode = PictureBoxSizeMode.AutoSize;
Controls.Add(pic);
pic.Click += clickHandler;
}
}
private void displayImages()
{
// Move code from form1_load to here
}
//click handler for every picture
private void clickHandler(object sender, EventArgs e)
{
MessageBox.Show("You clicked a picture box");
}
private void playAgainButton_Click(object sender, EventArgs e)
{
// call display images here
}
}
}
All winforms controls, including PictureBox, have a Tag property that can be set to any object. Presumably, your professor wants you to use that to link the pictures to their corresponding names.
Add this line to your for loop where you are initializing the PictureBoxes:
pic.Tag = names[i];
Then in your click handler you can show the name of the picture that was clicked like this:
private void clickHandler(object sender, EventArgs e)
{
PictureBox pic = (PictureBox)sender; // get the control that was clicked on
string name = (string)pic.Tag; // retrieve the name from the Tag property
MessageBox.Show("You clicked " + name);
}

How to draw sketch lines with C# and GDI+

Is it possible to draw lines and figures like shown below in GDI+ and c#?
May be there is some way to do that in easy way in c#?
Update:
I mean that I need to imitate Hand-Drawing effect in GDI+
I would like to write something like:
graphics.DrawHandDrawnLine(Pens.Black, x1, y1, x2, y2);
and see something like this
I believe this will be hard to top in the 'more easy' department..:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Imaging;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace Doodle
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
List<Point> curPoints = new List<Point>();
List<List<Point>> allPoints = new List<List<Point>>();
private void pnlPaint_MouseDown(object sender, MouseEventArgs e)
{
if (curPoints.Count > 1)
{
// begin fresh line
curPoints.Clear();
// startpoint
curPoints.Add(e.Location);
}
}
private void pnlPaint_MouseUp(object sender, MouseEventArgs e)
{
if (curPoints.Count > 1)
{
// ToList creates a copy
allPoints.Add(curPoints.ToList());
curPoints.Clear();
}
}
private void pnlPaint_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
// here we should check if the distance is more than a minimum!
curPoints.Add(e.Location);
// let it show
pnlPaint.Invalidate();
}
private void pnlPaint_Paint(object sender, PaintEventArgs e)
{
using (Pen pen = new Pen(Color.Black, 3f))
{
// regular edges:
pen.MiterLimit = 1.5f
// current lines
if (curPoints.Count > 1) e.Graphics.DrawCurve(pen, curPoints.ToArray());
// other lines
foreach (List<Point> points in allPoints)
if (points.Count > 1) e.Graphics.DrawCurve(pen, points.ToArray());
}
}}
private void btn_undo_Click(object sender, EventArgs e)
{
if (allPoints.Count > 0)
{
allPoints.RemoveAt(allPoints.Count - 1);
pnlPaint.Invalidate();
}
}
private void btn_save_Click(object sender, EventArgs e)
{
string fileName = #"d:\sketch.png";
Bitmap bmp = new Bitmap(pnlPaint.ClientSize.Width, pnlPaint.ClientSize.Width);
pnlPaint.DrawToBitmap(bmp, pnlPaint.ClientRectangle);
bmp.Save(fileName, ImageFormat.Png);
}
}
class DrawPanel : Panel
{
public DrawPanel ()
{
DoubleBuffered = true;
}
}
}
Just add one DrawPanel and two Buttons..
(I really should have used my Wacom, and a little more space..)
Update: Instead of a Panel, which is a Container control and not really meant to draw onto you can use a Picturebox or a Label (with Autosize=false); both have the DoubleBuffered property turned on out of the box and support drawing better than Panels do.
I found this solution Creating a Hand-Drawn effect using .NET
Maybe there is something more easy, for example something with transformations?

Zedgraph Control accessible from any function

I have this function:
public void MainFormLoad(object sender, EventArgs e)
{
GraphPane myPane = GRAPH.GraphPane;
}
Where myPane is a reference to GraphPane (GRAPH is name of ZedGraphControl which is displayed in GUI)
And now I want to change things like name of "x" or "y" axis, title, colors etd. or whatever you can change, but based on events. For example: I have textbox where I can write text and this text will be displayed in graph as title after textbox_textchanged_event trigger like this:
void TitleTextChanged(object sender, EventArgs e)
{
myPane.Title.Text = textbox1.Text;
}
There will be more functions like this to change properties of the graph. But this is not working.
Is there a way to come around this?
I have also tried this:
void TitleTextChanged(object sender, EventArgs e)
{
GRAPH.GraphPane.Title.Text = textbox1.text.Text;
}
but no help at all.
Please help, any advices are welcome.
**ANSWER:
So far, i have found this solution:
public void MainFormLoad(object sender, EventArgs e)
{
EditGraph(GRAPH);
}
This is the event that handles text change in text box:
public void TB_GRAPH_TITLE_VALUETextChanged(object sender, EventArgs e)
{
//GraphPane myPane2 = GRAPH.GraphPane;
changedGraphTitle = true;
EditGraph(GRAPH);
}
This is the function that find what is changed and update it:
public void EditGraph(ZedGraphControl zgc)
{
GraphPane myPane = zgc.GraphPane;
if(changedGraphTitle)
{
myPane.Title.Text = TB_GRAPH_TITLE_VALUE.Text;
changedGraphTitle = false;
zgc.Refresh();
}
}
"bool changedGraphTitle = false" must be declared also.**
If I've understood your question correctly, here's my simple code to update Zedgraph Axis Titles by a single ButtonClick event.
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;
using ZedGraph;
namespace updateZedGraph
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
myPane = zedGraphControl1.GraphPane;
}
GraphPane myPane;
private void btn_UpdateChart_Click(object sender, EventArgs e)
{
// Update x Axis Text
myPane.XAxis.Title.Text = textBox1.Text;
// Update x Axis Text
myPane.YAxis.Title.Text = textBox2.Text;
// Refresh Chart
zedGraphControl1.Invalidate();
}
}
}
hope that helps..

Categories