WPF C# PreviewDrop/Drop event not fired ( With dragadorner) - c#

I assume that previewdrop/drop events fired when it detect a drag target with an element as a drop target . In this case , my drop target is a textbox and my drag target is a label. Both of them are dynamically created from DB . I am using DragAdorner to clone the element that i am dragging , before implementing the DragAdorner , my DnD works well but after i use the dragadorner , it won't work . And i notice my previewdrop event is not firing when i try to debug .
Here are my codes :
tbox.Drop += new DragEventHandler(tbox_PreviewDrop); // text box , Drop Target
tbox.DragOver += new DragEventHandler(tbox_DragOver);
Label lbl = new Label(); // Label , Drag Target
lbl.Content = s;
lbl.Width = Double.NaN;
lbl.Height = 40;
lbl.FontSize = 19;
lbl.PreviewMouseDown += new MouseButtonEventHandler(lbl_MouseDown);
lbl.PreviewMouseMove += new MouseEventHandler(lbl_MouseMove);
lbl.PreviewGiveFeedback += new GiveFeedbackEventHandler(lbl_GiveFeedback);
private void lbl_MouseDown(object sender, MouseButtonEventArgs e)
{
startPoint = e.GetPosition(this);
// Mouse.OverrideCursor = Cursors.None;
}
private void lbl_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
// Mouse.OverrideCursor = Cursors.None;
var source = sender as UIElement;
Label lbl = sender as Label;
Point current = e.GetPosition(this);
Vector diff = startPoint - current;
if (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
{
adorner = new DragAdorner(lbl, e.GetPosition(lbl));
AdornerLayer.GetAdornerLayer(lbl).Add(adorner);
var dragData = new DataObject(this);
DragDrop.DoDragDrop(source, dragData, DragDropEffects.Copy);
AdornerLayer.GetAdornerLayer(lbl).Remove(adorner);
}
startPoint = current;
}
}
private void lbl_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
if (adorner != null)
{
Label lbl = sender as Label;
var pos = lbl.PointFromScreen(GetMousePosition());
adorner.UpdatePosition(pos);
e.Handled = true;
}
}
private void tbox_PreviewDrop(object sender, DragEventArgs e)
{
(sender as TextBox).Text = string.Empty; // Empty the textbox from previous answer.
(sender as TextBox).Background = Brushes.White;
e.Effects = DragDropEffects.Move;
e.Handled = true;
}
private void tbox_DragOver(object sender, DragEventArgs e)
{
e.Handled = true;
e.Effects = DragDropEffects.Move;
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Win32Point pt);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
public static Point GetMousePosition()
{
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
return new Point(w32Mouse.X, w32Mouse.Y);
}
private Point startPoint;
private DragAdorner adorner;
And the adorner class file :
public class DragAdorner : Adorner {
public DragAdorner(UIElement adornedElement, Point offset)
: base(adornedElement) {
this.offset = offset;
vbrush = new VisualBrush(AdornedElement);
//vbrush.Opacity = .7;
}
public void UpdatePosition(Point location) {
this.location = location;
this.InvalidateVisual();
}
protected override void OnRender(DrawingContext dc) {
var p = location;
p.Offset(-offset.X, -offset.Y);
dc.DrawRectangle(vbrush, null, new Rect(p, this.RenderSize));
}
private Brush vbrush;
private Point location;
private Point offset;
}
I seen http://www.adorkable.us/books/wpf_control_development.pdf ( page 103 ) but its way too complicated for me as i am a newbie .
It is because of my GiveFeedBack event that is in conflict with other events?

Since your DragAdorner is always under your cursor, it will be the object receiving the drop. If you set IsHitTestVisible = false; in the constructor for the Adorner, it should fix this.
Even though you haven't set AllowDrop on the Adorner, since it is under the cursor, it will intercept the drop attempt. But since it doesn't accept drop, it will just cancel it.
Update
The other issue is that you are setting the allowed effects in your drag operation to DragDropEffects.Copy, but in the DragOver and Drop handlers, you're trying to do a DragDropEffects.Move. This won't work, as those are not the same operation. These must match. If you want to enable both operations on drag, you can specify both with a bitwise or:
DragDrop.DoDragDrop(source, dragData, DragDropEffects.Copy | DragDropEffects.Move);
Update 2
If you want to drop anything other than a string onto a TextBox, you have to use the PreviewDrop and PreviewDragOver events. Otherwise, the TextBox's default handling will ignore anything else. So it would look like this:
tbox.PreviewDrop += new DragEventHandler(tbox_PreviewDrop);
tbox.PreviewDragOver += new DragEventHandler(tbox_DragOver);

Try setting background color for your label and see if it will work properly.

Related

How to keep a control within its parent control while dragging it?

So I have a control named UI_TileInformation which needs to be kept inside its parent container (a canvas), while allowing the user to drag it freely. This solution was helpful in getting it to function, it now drags perfectly fine. However, it can be dragged outside of the bounds of its container. This solution was no help as I do not see where the _transform variable comes from.
The control has the following default variables:
protected bool isDragging;
private Point clickPosition;
As well as these event handlers:
this.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(Control_MouseLeftButtonDown);
this.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(Control_MouseLeftButtonUp);
this.PreviewMouseMove += new MouseEventHandler(Control_MouseMove);
I am fairly new to c# and WPF but I figured out that those event handlers would only work with when previewed (PreviewMouseMove as opposed to MouseMove)
As for the code that makes it drag, it is similar to the first link above:
private void Control_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
isDragging = true;
var draggableControl = sender as UI_TileInformation;
clickPosition = e.GetPosition(this);
draggableControl.CaptureMouse();
}
private void Control_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
isDragging = false;
var draggableControl = sender as UI_TileInformation;
draggableControl.ReleaseMouseCapture();
}
private void Control_MouseMove(object sender, MouseEventArgs e)
{
var draggableControl = sender as UI_TileInformation;
if (isDragging && draggableControl != null)
{
Point positionInUIElement = e.GetPosition(this);
var transform = draggableControl.RenderTransform as TranslateTransform;
if (transform == null)
{
transform = new TranslateTransform();
draggableControl.RenderTransform = transform;
}
transform.X = positionInCanvas.X - clickPosition.X;
transform.Y = positionInCanvas.Y - clickPosition.Y;
}
}
How can I get my control to stay inside the canvas?

how to drag & drop image from wrap panel to canvas

I am using Visual Studio 2012 to write a C# WPF application.
I got some images from a database and put them into a wrap panel. From the wrap panel, I want to drag the images and drop them into a canvas. This is how I get the images from the database:
private void GetImages(int taskID, int activityID)
{
IList<ModelSQL.TwoCategory> ImagesList = twocat.GetImageURL(taskid, type);
foreach (ModelSQL.TwoCategory tc in ImagesList)
{
var uriSource = new Uri(root.SetupInformation.ApplicationBase + tc.Pictures);
Image img = new Image();
img.Source = new BitmapImage(uriSource);
img.VerticalAlignment = VerticalAlignment.Center;
img.HorizontalAlignment = HorizontalAlignment.Center;
img.Stretch = Stretch.Uniform;
img.Height = 100;
img.Width = 100;
img.AllowDrop = true;
img.PreviewMouseDown += new MouseButtonEventHandler(img_MouseDown);
img.PreviewMouseMove += new MouseEventHandler(img_MouseMove);
img.PreviewGiveFeedback += new GiveFeedbackEventHandler(img_GiveFeedback);
img.PreviewMouseUp += new MouseButtonEventHandler(img_PreviewMouseUp);
Canvas1.AllowDrop = true;
Canvas2.AllowDrop = true;
//Canvas1.PreviewDrop += new DragEventHandler(Canvas1_PreviewDrop);
//Canvas2.PreviewDrop += new DragEventHandler(Canvas2_PreviewDrop);
imgPanel.Children.Add(img);
}
}
I used the drag adorner to drag the images and it works fine.
private void img_MouseDown(object sender, MouseButtonEventArgs e)
{
startPoint = e.GetPosition(this);
}
private void img_MouseMove(object sender, MouseEventArgs e)
{
// int y = 0, z = 0;
if (e.LeftButton == MouseButtonState.Pressed)
{
var source = sender as UIElement;
Image img = sender as Image;
//img.Background = System.Windows.Media.Brushes.YellowGreen;
Point current = e.GetPosition(this);
Vector diff = startPoint - current;
if (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
{
adorner = new DragAdorner(img, e.GetPosition(img));
AdornerLayer.GetAdornerLayer(img).Add(adorner);
var dragData = new DataObject(this);
//string str = img.Content.ToString();
//DragDrop.DoDragDrop(lbl, lbl.Content, DragDropEffects.Move | DragDropEffects.Copy);
DragDrop.DoDragDrop(img, dragData, DragDropEffects.Move | DragDropEffects.Copy);
AdornerLayer.GetAdornerLayer(img).Remove(adorner);
}
}
}
private void img_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{ }
private void img_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
if (adorner != null)
{
Image img = sender as Image;
var pos = img.PointFromScreen(GetMousePosition());
adorner.UpdatePosition(pos);
e.Handled = true;
}
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Win32Point pt);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
public static Point GetMousePosition()
{
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
return new Point(w32Mouse.X, w32Mouse.Y);
}
But I'm having trouble dropping the image into the canvas, and I'm able to drag the image to anywhere in the application. How do I disable that?
This is the GUI
THIS IS THE DROP EVENT
private void Canvas1_Drop(object sender, DragEventArgs e)
{
ImageSource imageSource = e.Data.GetData(typeof(ImageSource)) as ImageSource;
Image img = sender as Image;
img.Source = imageSource;
Canvas Canvas1 = sender as Canvas;
Canvas1.Children.Add(img);
}
With
DragDrop.DoDragDrop(img, dragData, DragDropEffects.Move | DragDropEffects.Copy);
you managed, that the data has been given to the drag drop operation.
Now, every other Control, e.g. the canvas, can decide in their DragOver and Drop events, if they want/can do anything with the data or not.
The source of the data is more or less out of the game, when the drag and drop starts, it's the drop target that does the rest.
If you do not want your data to leave the source control, you should attach to its DragLeave-event and cancel the Drag and Drop operation, if the mouse moves away.

Drag and drop a CustomControl working only from it's background

I have a custom control containing a button with a label underneath. I want to make these controls to be draggable over another one to arrange them as I want in a flowlayoutpanel.
Now is working only if I drag the control from it's background (marked with yellow in the picture bellow) to the other control's yellow marked area, but not if i drag from the button or label area..
How can I make it so I can move the custom control no matter from where I grab and drop it on the other control. Basically to be only one control not like a container for the button and label..
This is my code so far:
private void flowLayoutPanel1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void flowLayoutPanel1_DragDrop(object sender, DragEventArgs e)
{
CustomControl target = sender as CustomControl;
if (target != null)
{
int targetIndex = FindCSTIndex(target);
if (targetIndex != -1)
{
string pictureBoxFormat = typeof(CustomControl).FullName;
if (e.Data.GetDataPresent(pictureBoxFormat))
{
CustomControl source = e.Data.GetData(pictureBoxFormat) as CustomControl;
int sourceIndex = this.FindCSTIndex(source);
if (targetIndex != -1)
this.flowLayoutPanel1.Controls.SetChildIndex(source, targetIndex);
}
}
}
}
private int FindCSTIndex(CustomControl cst_ctr)
{
for (int i = 0; i < this.flowLayoutPanel1.Controls.Count; i++)
{
CustomControl target = this.flowLayoutPanel1.Controls[i] as CustomControl;
if (cst_ctr == target)
return i;
}
return -1;
}
private void OnCstMouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
CustomControl cst = sender as CustomControl;
cst.DoDragDrop(cst, DragDropEffects.Move);
}
}
And the custom control class:
public class CustomControl : Control
{
private Button _button;
private Label _label;
public CustomControl(Button button, Label label)
{
_button = button;
_label = label;
button.Width = 50;
button.Height = 50;
label.Width = 65;
button.BackgroundImageLayout = ImageLayout.Stretch;
Height = button.Height + label.Height;
Width = 68;
// Width = Math.Max(button.Width, label.Width);
Controls.Add(_button);
_button.Location = new Point(0, 0);
Controls.Add(_label);
_label.Location = new Point(0, button.Height);
}
}
Use MouseDown instead of MouseMove to initiate drag-and-drop (MSDN). You can initiate drag-and-drop in the control code itself (assuming what all CustomControls will be drag-and-drop-able), otherwise you may want to create public method to sign childs (exposing childs is bad idea, unless you already use them outside).
public class CustomControl : Control
{
...
public CustomControl(Button button, Label label)
{
...
_button.MouseDown += OnMouseDown;
_label.MouseDown += OnMouseDown;
}
override void OnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
(sender as Control).DoDragDrop(this, DragDropEffects.Move);
}
}
Untested, but should give you idea.
Managed to solve it:
private void flowLayoutPanel1_DragDrop(object sender, DragEventArgs e)
{
Control target = new Control();
target.Parent = sender as Control;
if (target != null)
{
int targetIndex = FindCSTIndex(target.Parent);
if (targetIndex != -1)
{
string cst_ctrl = typeof(CustomControl).FullName;
if (e.Data.GetDataPresent(cst_ctrl))
{
Button source = new Button();
source.Parent = e.Data.GetData(cst_ctrl) as CustomControl;
if (targetIndex != -1)
this.flowLayoutPanel1.Controls.SetChildIndex(source.Parent, targetIndex);
}
}
}
}
private int FindCSTIndex(Control cst_ctr)
{
for (int i = 0; i < this.flowLayoutPanel1.Controls.Count; i++)
{
CustomControl target = this.flowLayoutPanel1.Controls[i] as CustomControl;
if (cst_ctr.Parent == target)
return i;
}
return -1;
}
private void OnCstMouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Control cst = sender as Control;
cst.DoDragDrop(cst.Parent, DragDropEffects.Move);
}
}

Drag multiple selected controls

I have on a WinForms Form a UserControl, of which multiple instances can be created dynamically. If I select it, I can drag it. Now, I want, if I select multiple UserControls (with ctrl + button click), to be able to drag all of them at the same time.
Can I do that like I did for one UserControl?
What I have tried until now:
// For dragging I use this method and also I have
// overridden MouseUp,MouseDown,MouseMove from .net
public void StartDrag()
{
dragging = true;
Point p = PointToClient(Cursor.Position);
dragStart = new Point(Math.Max(0, p.X), Math.Max(0, p.Y));
buttondrag.Capture = true;
}
private void Usercontrol1_SelectedChanged(object sender, EventArgs e)
{
if (((UserControl)sender).Selected)
{
if (SelectedUserControl.Count > 1)
{
foreach (UserControl c in panel1.Controls)
{
c.StartDrag();
}
}
}
}
// put your controls in the panel, and use this class.
class ControlMover
{
public enum Direction
{
Any,
Horizontal,
Vertical
}
public static void Init(Control control)
{
Init(control, Direction.Any);
}
public static void Init(Control control, Direction direction)
{
Init(control, control, direction);
}
public static void Init(Control control, Control container, Direction direction)
{
bool Dragging = false;
Point DragStart = Point.Empty;
control.MouseDown += delegate(object sender, MouseEventArgs e)
{
Dragging = true;
DragStart = new Point(e.X, e.Y);
control.Capture = true;
};
control.MouseUp += delegate(object sender, MouseEventArgs e)
{
Dragging = false;
control.Capture = false;
};
control.MouseMove += delegate(object sender, MouseEventArgs e)
{
if (Dragging)
{
if (direction != Direction.Vertical)
container.Left = Math.Max(0, e.X + container.Left - DragStart.X);
if (direction != Direction.Horizontal)
container.Top = Math.Max(0, e.Y + container.Top - DragStart.Y);
}
};
}
}
public Form1()
{
InitializeComponent();
ControlMover.Init(this.panel1);
ControlMover.Init(this.panel1, ControlMover.Direction.Vertical);
}
If you want the program to activate when ctrl + key is pressed then you will need to look at key hooks, and then set up some code in the key down event telling the program to select all items.
This is quite advanced and difficult to do if your new to key hooks, but here's a link for key hooks.
Good luck!
http://www.codeproject.com/Articles/19004/A-Simple-C-Global-Low-Level-Keyboard-Hook

how to transmit updates for network clients

I have an application in C# and using forms, I am placing a label each time I right click on the form. This label can be moved around, re-sized and modified its color.
So far so good, but I want to make a server that will receive everything I do and send this to other clients so they can see everything I do, and also they can do exactly the same things. I have made eventhandlers, but I have no idea how to send the information through the network, or what information to send to update the form for each client.
internal System.Windows.Forms.ContextMenu mnuForm;
internal System.Windows.Forms.MenuItem mnuNewSquare;
internal System.Windows.Forms.ContextMenu mnuLabel;
internal System.Windows.Forms.MenuItem mnuColorChange;
private void mnuNewSquare_Click(object sender, System.EventArgs e)
{
// Create and configure the "square".
Label newLabel = new Label();
newLabel.Size = new Size(40, 40);
newLabel.BorderStyle = BorderStyle.FixedSingle;
// To determine where to place the label, you need to convert the
// current screen-based mouse coordinates into relative form coordinates.
newLabel.Location = this.PointToClient(Control.MousePosition);
// Attach a context menu to the label.
newLabel.ContextMenu = mnuLabel;
// Connect the label to all its event handlers.
newLabel.MouseDown += new MouseEventHandler(lbl_MouseDown);
newLabel.MouseMove += new MouseEventHandler(lbl_MouseMove);
newLabel.MouseUp += new MouseEventHandler(lbl_MouseUp);
// Add the label to the form.
this.Controls.Add(newLabel);
}
// Keep track of when fake drag or resize mode is enabled.
private bool isDragging = false;
private bool isResizing = false;
// Store the location where the user clicked on the control.
private int clickOffsetX, clickOffsetY;
private void lbl_MouseDown(object sender,
System.Windows.Forms.MouseEventArgs e)
{
// Retrieve a reference to the active label.
Control currentCtrl;
currentCtrl = (Control)sender;
if (e.Button == MouseButtons.Right)
{
// Show the context menu.
currentCtrl.ContextMenu.Show(currentCtrl, new Point(e.X, e.Y));
}
else if (e.Button == MouseButtons.Left)
{
clickOffsetX = e.X;
clickOffsetY = e.Y;
if ((e.X + 5) > currentCtrl.Width && (e.Y + 5) > currentCtrl.Height)
{
// The mouse pointer is in the bottom right corner,
// so resizing mode is appropriate.
isResizing = true;
}
else
{
// The mouse is somewhere else, so dragging mode is
// appropriate.
isDragging = true;
}
}
}
private void lbl_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
// Retrieve a reference to the active label.
Control currentCtrl;
currentCtrl = (Control)sender;
if (isDragging)
{
// Move the control.
currentCtrl.Left += e.X - clickOffsetX;
currentCtrl.Top += e.Y - clickOffsetY;
}
else if (isResizing)
{
// Resize the control.
currentCtrl.Width = e.X;
currentCtrl.Height = e.Y;
}
else
{
// Change the pointer if the mouse is in the bottom corner.
if ((e.X + 5) > currentCtrl.Width && (e.Y + 5) > currentCtrl.Height)
{
currentCtrl.Cursor = Cursors.SizeNWSE;
}
else
{
currentCtrl.Cursor = Cursors.Arrow;
}
}
}
private void lbl_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
isDragging = false;
isResizing = false;
}
private void mnuColorChange_Click(object sender, System.EventArgs e)
{
// Show color dialog.
ColorDialog dlgColor = new ColorDialog();
dlgColor.ShowDialog();
// Change label background.
mnuLabel.SourceControl.BackColor = dlgColor.Color;
}
private void DrawingSquares_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
this.ContextMenu.Show(this, new Point(e.X, e.Y));
}
}
This is the code for form1.cs, and the next code is for form1.designer.cs
private void InitializeComponent()
{
// this.SuspendLayout();
//
// Form1
//
this.mnuForm = new System.Windows.Forms.ContextMenu();
this.mnuNewSquare = new System.Windows.Forms.MenuItem();
this.mnuLabel = new System.Windows.Forms.ContextMenu();
this.mnuColorChange = new System.Windows.Forms.MenuItem();
//
// mnuForm
//
this.mnuForm.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.mnuNewSquare});
//
// mnuNewSquare
//
this.mnuNewSquare.Index = 0;
this.mnuNewSquare.Text = "Create New Square";
this.mnuNewSquare.Click += new System.EventHandler(this.mnuNewSquare_Click);
//
// mnuLabel
//
this.mnuLabel.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.mnuColorChange});
//
// mnuColorChange
//
this.mnuColorChange.Index = 0;
this.mnuColorChange.Text = "Change Color";
this.mnuColorChange.Click += new System.EventHandler(this.mnuColorChange_Click);
//
// DrawingSquares
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(628, 426);
this.ContextMenu = this.mnuForm;
this.Name = "DrawingSquares";
this.Text = "DrawingSquares";
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DrawingSquares_MouseDown);
}
This does the client side of the application, in which you can draw a label and modify its properties. I need some help on how to do the server side, any help is much appreciated, thank you in advance.
You might want to look at SignalR:
A client and server side library for .NET that provides messaging and an abstraction over a persistent connection.

Categories

Resources