I have a tableLayoutPanel with 16 cells. 15 of the cells have controls. I want to be able to move the controls from one cell to another at runtime.
I have used
private void button15_Click(object sender, EventArgs e)
{
tableLayoutPanel1.Controls.Remove(button15);
tableLayoutPanel1.Controls.Add(button15, 3, 3);
}
This works well but i want to know if there is any better way to do this???
In Winforms, you can only move a control inside its parent (of course there are some exceptions to some controls which in fact don't have any Parent). So the idea here is if you want to move a control of your TableLayoutPanel, you have to set its Parent to your Form of another container when mouse is held down, when moving, the position of the control is in the new parent, after mouse is released, we have to set the Parent of the control to the TableLayoutPanel back, of course we have to find the drop-down cell position and use SetCellPosition method to position the control on the TableLayoutPanel, here is the demo code for you (works great), I use 2 Buttons in this demo, you can replace them with any control you want:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
//This will prevent flicker
typeof(TableLayoutPanel).GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(tableLayoutPanel1, true, null);
}
Point downPoint;
bool moved;
//This is used to store the CellBounds together with the Cell position
//so that we can find the Cell position later (after releasing mouse).
Dictionary<TableLayoutPanelCellPosition, Rectangle> dict = new Dictionary<TableLayoutPanelCellPosition, Rectangle>();
//MouseDown event handler for all your controls (on the tableLayoutPanel1)
private void Buttons_MouseDown(object sender, MouseEventArgs e) {
Control button = sender as Control;
button.Parent = this;
button.BringToFront();
downPoint = e.Location;
}
//MouseMove event handler for all your controls (on the tableLayoutPanel1)
private void Buttons_MouseMove(object sender, MouseEventArgs e) {
Control button = sender as Control;
if (e.Button == MouseButtons.Left) {
button.Left += e.X - downPoint.X;
button.Top += e.Y - downPoint.Y;
moved = true;
tableLayoutPanel1.Invalidate();
}
}
//MouseUp event handler for all your controls (on the tableLayoutPanel1)
private void Buttons_MouseUp(object sender, MouseEventArgs e) {
Control button = sender as Control;
if (moved) {
SetControl(button, e.Location);
button.Parent = tableLayoutPanel1;
moved = false;
}
}
//This is used to set the control on the tableLayoutPanel after releasing mouse
private void SetControl(Control c, Point position) {
Point localPoint = tableLayoutPanel1.PointToClient(c.PointToScreen(position));
var keyValue = dict.FirstOrDefault(e => e.Value.Contains(localPoint));
if (!keyValue.Equals(default(KeyValuePair<TableLayoutPanelCellPosition, Rectangle>))) {
tableLayoutPanel1.SetCellPosition(c, keyValue.Key);
}
}
//CellPaint event handler for your tableLayoutPanel1
private void tableLayoutPanel1_CellPaint(object sender, TableLayoutCellPaintEventArgs e) {
dict[new TableLayoutPanelCellPosition(e.Column, e.Row)] = e.CellBounds;
if (moved) {
if (e.CellBounds.Contains(tableLayoutPanel1.PointToClient(MousePosition))) {
e.Graphics.FillRectangle(Brushes.Yellow, e.CellBounds);
}
}
}
}
Remove Lock & set dock to none and move!
Related
I have an application that scans an image to display on the application. After the image is scanned, I have the option to zoom the image. There's a combo box on the application that display the zoom percentage as well.
I can zoom fine using my mouse wheel and the combo box % changes accordingly which is fine. The problem happens if I manually select the combo box and select a zoom percentage, say 50%, then there's no changes at all.
Code:
private void ImageBox_ZoomLevelsChanged(object sender, EventArgs e)
{
this.FillZoomLevels();
}
private void ZoomComboBox_Click(object sender, EventArgs e)
{
}
private void FillZoomLevels()
{
ZoomComboBox.Items.Clear();
foreach (int zoom in ImageBox.ZoomLevels)
ZoomComboBox.Items.Add(string.Format("{0}%", zoom));
}
Am I doing anything wrong? Appreciate any help.
When there is more than one control on the Panel, and the ScrollBars are shown, the MouseWheel event of the Panel would be raised, but if there's only a PictureBox on the Panel whose Image large enough to make the panel's ScrollBars visible, then the MouseWheel event of the Panel won't be raised, instead, the Form's MouseWheel event fires.
In the following sample, I just add a PictureBox onto the Panel without any other controls added, set a large Image to the PictureBox which make the Panel's ScrollBars invisible, since there's only one control on the panel, the MouseWheel event of the Panel won't be raised, but the Form's MouseWheel event be raised, we handle this event instead, handle the Form's KeyDown and KeyUp events as well.
Furthermore, set the SizeMode of the PictureBox to StretchImage instead of AutoSize, thus when we change the size of the PictureBox, the Image in the PictureBox will resize to fit the PictureBox.
The PictureBox.Scale() method won't help you in this screnario, change the size of the PictureBox instead.
public partial class Form4 : Form
{
public Form4()
{
InitializeComponent();
}
bool ctrlKeyDown;
bool shiftKeyDown;
private void Form4_Load(object sender, EventArgs e)
{
this.ctrlKeyDown = false;
this.shiftKeyDown = false;
//If there's only PictureBox control on the panel, the MouseWheel event of the form raised
//instead of the MouseWheel event of the Panel.
this.MouseWheel += new MouseEventHandler(Form4_MouseWheel);
this.KeyDown += new KeyEventHandler(Form4_KeyDown);
this.KeyUp += new KeyEventHandler(Form4_KeyUp);
//this is important for zooming the image
this.pictureBox2.SizeMode = PictureBoxSizeMode.StretchImage;
}
void Form4_KeyUp(object sender, KeyEventArgs e)
{
this.ctrlKeyDown = e.Control;
this.shiftKeyDown = e.Shift;
}
void Form4_KeyDown(object sender, KeyEventArgs e)
{
this.ctrlKeyDown = e.Control;
this.shiftKeyDown = e.Shift;
}
void Form4_MouseWheel(object sender, MouseEventArgs e)
{
bool IsGoUp = e.Delta > 0 ? true : false;
if (this.ctrlKeyDown)
{
if (IsGoUp && this.panel1.HorizontalScroll.Value > 5)
{
this.panel1.HorizontalScroll.Value -= 5;
}
if (!IsGoUp && this.panel1.HorizontalScroll.Value < this.panel1.HorizontalScroll.Maximum - 5)
{
this.panel1.HorizontalScroll.Value += 5;
}
}
else if (this.shiftKeyDown)
{
int hStep = (int)(this.pictureBox2.Image.Width * 0.02);
int vStep = (int)(this.pictureBox2.Image.Height * 0.02);
if (IsGoUp)
{
this.pictureBox2.Width += hStep;
this.pictureBox2.Height += vStep;
}
else
{
this.pictureBox2.Width -= hStep;
this.pictureBox2.Height -= vStep;
}
}
else
{
if (IsGoUp && this.panel1.VerticalScroll.Value > 5)
{
this.panel1.VerticalScroll.Value -= 5;
}
if (!IsGoUp && this.panel1.VerticalScroll.Value < this.panel1.VerticalScroll.Maximum - 5)
{
this.panel1.VerticalScroll.Value += 5;
}
}
}
}
Im trying to open a new form when a label is double clicked. Im able to drag and drop the label .Im trying to open a new form on double click of label now.
private void control_MouseDown(object sender, MouseEventArgs e)
{
var control = sender as Control;
this.DoDragDrop(control.Name, DragDropEffects.Move);
}
private void control_DoubleClick(object sender, EventArgs e)
{
frm = new Frm();
frm.ShowDialog();
frm.Dispose();
}
EDIT 1:
I have tried both possible answers below, and they have not worked for me?
A more cleaner way is (note I changed Frm to Form1):
private void control_DoubleClick(object sender, EventArgs e)
{
using (Form1 frm = new Form1())
{
frm.ShowDialog();
}
}
You can't add DragDrop on MouseDown and then DoubleClick. That won't work.
I don't think there's an easy way to get around that, but once a control is being dragged, it won't respond to double click messages.
I've made some quick tests, and there's a "hacky" way. It'll make your dragging look weird (since it'll start after some time, instead of immediately after you press the mouse button), but here it goes:
private bool _willDrag = false;
private bool control_MouseUp(object sender, MouseEventArgs e)
{
// disable dragging if we release the mouse button
_willDrag = false;
}
private bool control_DoubleClick(object sender, EventArgs e)
{
// disable dragging also if we double-click
_willDrag = false;
// .. the rest of your doubleclick event ...
}
private void control_MouseDown(object sender, MouseEventArgs e)
{
var control = sender as Control;
if (control == null)
return;
_willDrag = true;
var t = new System.Threading.Timer(s =>
{
var callingControl = s as Control;
if (callingControl == null)
return;
// if we released the mouse button or double-clicked, don't drag
if(!_willDrag)
return;
_willDrag = false;
Action x = () => DoDragDrop(callingControl.Name, DragDropEffects.Move);
if (control.InvokeRequired)
control.Invoke(x);
else
x();
}, control, SystemInformation.DoubleClickTime, Timeout.Infinite);
}
In the form.Designer right click on your label then properties, in the properties window click in events (the thunder icon), in the double_Click event dropdown select the event handler (control_DoubleClick) this method must have two parameters an object and a eventArgs
This is tricky as the DoDragDrop will eat up any further mouse events, and MSDN posting a rather stupid example doesn't help much.
Solution: Do not start the D&D in the MouseDown if you want to still receive click or double click events but use the MouseMove instead:
Replace this
private void control_MouseDown(object sender, MouseEventArgs e)
{
var control = sender as Control;
this.DoDragDrop(control.Name, DragDropEffects.Move);
}
by this:
private void control_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
DoDragDrop((sender as Control), DragDropEffects.Move);
}
Don't forget to hook up the new event!
I have added a listBox containing a list and a toolStrip in my application. These are used to select control to added runtime in a panel. (I have added an image. The toolStrip and listBox are on the left and right side respectively.) Now what I do with these controls is,
1 - I select an item from toolStrip or listBox
2 - A runtime control is made and shown on the form
3 - On Mouse_Up event, the control is set on the Panel (If it is in the panel.)
Now the question is, as shown in the image, the runtime control is not at the tip of the cursor. I want these runtime controls on the tip of the cursor.
Below is the Image.
EDIT: Below is the code.:
private void listBox_MouseDown(object sender, MouseEventArgs e)
{
this.globalLabel1 = new Label();
this.globalLabel1.Text = this.listBox.SelectedItem.ToString() + " : ";
//other label properties like, tag, name, events, font etc.
}
private void listBox_MouseMove(object sender, MouseEventArgs e)
{
if (this.globalLabel1 != null)
{
this.globalLabel1.Left = System.Windows.Forms.Cursor.Position.X
- this.Location.X;
this.globalLabel1.Top = System.Windows.Forms.Cursor.Position.Y
- this.Location.Y;
this.globalLabel1.Show();
this.lPanel.SendToBack();
}
}
private void listBox_MouseUp(object sender, MouseEventArgs e)
{
this.globalLabel1.Parent = this.lPanel;
this.globalLabel1.Anchor = AnchorStyles.Top | AnchorStyles.Left;
this.globalLabel1.Left = Cursor.Position.X
- /*Cursor.Size.Height -*/ this.lPanel.Location.X
- this.Location.X;
this.globalLabel1.Top = Cursor.Position.Y
- /*Cursor.Size.Width -*/ this.lPanel.Location.Y
- this.Location.Y;
}
Thanks.
You have to set the Location of your runtime added control relatively to the parent, you can use the method PointToClient like this:
private void listBox_MouseUp(object sender, MouseEventArgs e) {
globalLabel1.Parent = lPanel;
globalLabel1.Anchor = AnchorStyles.Top | AnchorStyles.Left;
globalLabel1.Location = lPanel.PointToClient(Cursor.Position);
}
i). I don't know why are you not using Panel_MoveUP event, Because you wan to place it on panel.
ii). At Panel_moveUp you have to make little bit position change to cursor position by Hit and trail method, I not sure about this why is this difference coming so.
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
this.globalLabel1 = new Label();
this.globalLabel1.Text = this.listBox1.SelectedItem.ToString() + " : ";
//other label properties like, tag, name, events, font etc.
}
//////////////////////////USE PANEL_MOVEUP EVENT//////////////
private void lPanel_MouseMove(object sender, MouseEventArgs e)
{
if (this.globalLabel1 != null)
{
this.globalLabel1.Left = System.Windows.Forms.Cursor.Position.X-50 // Change
- this.Location.X;
this.globalLabel1.Top = System.Windows.Forms.Cursor.Position.Y-100 //Change
- this.Location.Y;
this.globalLabel1.Show();
this.lPanel.SendToBack();
this.lPanel.Controls.Add(globalLabel1);
}
}
I have a PictureBox that is inside a TabPage, and of course this TabPage is part of a TabView and this TabView is inside a Form. I want users be able to move this picture box within the tab page. For this I am using the MouseDown, MouseMove and MouseUp events of the picture box:
private void pictureBoxPackageView_MouseDown(object sender, MouseEventArgs e)
{
if (!_mapPackageIsMoving)
{
_mapPackageIsMoving = true;
}
}
private void pictureBoxPackageView_MouseMove(object sender, MouseEventArgs e)
{
if(_mapPackageIsMoving)
{
pictureBoxPackageView.Location = MousePosition; //This is not exact at all!
return;
}
//Some other code for some other stuff when picturebox is not moving...
}
private void pictureBoxPackageView_MouseUp(object sender, MouseEventArgs e)
{
if (_mapPackageIsMoving)
{
_mapPackageIsMoving = false; //Mouse button is up, end moving!
return;
}
}
But my problem lies in the MouseMove event. As soon as I move mouse after button down, the picture box jumps out of tab page's visible area.
I need to know how to handle the move only within the rectangle of the tab page, and if picture box is being dragged out of tab view's visible area, it shouldn't move anymore unless user brings the mouse inside the tab view's visible rectangle.
Any helps/tips will be appriciated!
You need a variable to hold the original position of the PictureBox:
Modified from a HansPassant answer:
private Point start = Point.Empty;
void pictureBoxPackageView_MouseUp(object sender, MouseEventArgs e) {
_mapPackageIsMoving = false;
}
void pictureBoxPackageView_MouseMove(object sender, MouseEventArgs e) {
if (_mapPackageIsMoving) {
pictureBoxPackageView.Location = new Point(
pictureBoxPackageView.Left + (e.X - start.X),
pictureBoxPackageView.Top + (e.Y - start.Y));
}
}
void pictureBoxPackageView_MouseDown(object sender, MouseEventArgs e) {
start = e.Location;
_mapPackageIsMoving = true;
}
I am writing a game. Player can choose items (such as weapon) and drag them to the form. Items are on the side, in PictureBox controls. I have set Form.AllowDrop to True. When I drag one of the items pictureBoxes, the pictureBox doesn't drop, neither even drag.
I want to drag a pictureBox on the form, or at least know the position in the form which the player want to drag it in.
EDIT: look at the logo above. When you click it and drag (without release) it drags.
In Winforms you need to change the cursor. Here's a complete example, start a new forms project and drop a picturebox on the form. Set its Image property to a small bitmap. Click and drag to drop copies of the image on the form.
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
this.AllowDrop = true;
this.pictureBox1.MouseDown += pictureBox1_MouseDown;
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
var dragImage = (Bitmap)pictureBox1.Image;
IntPtr icon = dragImage.GetHicon();
Cursor.Current = new Cursor(icon);
DoDragDrop(pictureBox1.Image, DragDropEffects.Copy);
DestroyIcon(icon);
}
}
protected override void OnGiveFeedback(GiveFeedbackEventArgs e) {
e.UseDefaultCursors = false;
}
protected override void OnDragEnter(DragEventArgs e) {
if (e.Data.GetDataPresent(typeof(Bitmap))) e.Effect = DragDropEffects.Copy;
}
protected override void OnDragDrop(DragEventArgs e) {
var bmp = (Bitmap)e.Data.GetData(typeof(Bitmap));
var pb = new PictureBox();
pb.Image = (Bitmap)e.Data.GetData(typeof(Bitmap));
pb.Size = pb.Image.Size;
pb.Location = this.PointToClient(new Point(e.X - pb.Width/2, e.Y - pb.Height/2));
this.Controls.Add(pb);
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
extern static bool DestroyIcon(IntPtr handle);
}
For the draggable items, you need to call the DoDragDrop method in the MouseDown event. Make sure your form (or target) has the AllowDrop property set to true.
For your target, you need to wire the dragging events:
private void Form1_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void Form1_DragDrop(object sender, DragEventArgs e)
{
// Examine e.Data.GetData stuff
}