Situation :
I'm currently working a project where the aim is to develop a VS-like IDE, where users can drap/drop new controls to a design surface, and modify properties of these controls.
So i implemented IDesignerHost, IServiceContainer, IContainer, IComponentChangeService, and some others useful interface, made to design that.
Everything works fine, i've my toolbox, my design surface, and my propertyGrid working just fine.
Problem is :
Attached to the drag'n'droped controls is a label, which has to follow the control while the user move it with his mouse.
I tried to use the LocationChanged event of the controls, to move the label when the control move. But this event occurs only one time, after the control has moved so the label doesn't move while the control move.
I'm not able to find a way for make this work. Does anyone have any good ideas please ?
Thank you
Edit :
I use a custom class, implementing IDesignerHost. Controls on this design surface doesn't fire events Mouse----- (e.g. : MouseDown, MouseMove).
I finally found how to do it :
I implemented ISelectionService and in the SetSelectedComponents function, I managed to select the label control associated with any selected control.
I overrided the designer of the label, so that no border/resize-rectangle would show when the label is selected.
This is a not very elegant solution, but it works well =).
Every Control has a ControlDesigner, provides additional methods to support extending and altering the behavior of an associated Control at design time.
In the ControlDesigner, you have a BehaviorService, which is responsible to control on DesignSurface behavior of the control.
BehaviorService has multiple Glyphs and Adorners which are like UI decorators for the control. The control re-size rubber-band like rectangle is a Glyph, called System.Windows.Forms.Design.Behavior.SelectionBorderGlyph a private class to .Net 2.0.
This links might be of help:
http://msdn.microsoft.com/en-us/library/ms171820.aspx
http://msdn.microsoft.com/en-us/library/bb514670%28VS.90%29.aspx
You should be able to add your custom Glyph which has a Label attached with the Control.
HTH
Form2 contains a panel1, label1
panel1.MouseMove += panel1_MouseMove
panel1.MouseDown += panel1_MouseDown
when MouseDown+Left Button clicked -> save initial mouse position
when MouseMove+Left Button clicked -> move (panel1+label1) by the difference between current mouse position and the saved initial position.
it's done.
public partial class Form2 : Form
{
private int _x, _y;
public Form2()
{
InitializeComponent();
}
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
panel1.Location = new Point(panel1.Location.X + (e.X - _x), panel1.Location.Y + (e.Y - _y));
label1.Location = new Point(label1.Location.X + (e.X - _x), label1.Location.Y + (e.Y - _y));
}
}
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_x = e.X;
_y = e.Y;
}
}
}
Related
i'm are trying to mimic the behavior of web browsers in a WinForm application where you can drag and drop tabs in and out of the browser and create another instance of the it when you drop the tab somewhere with no existing tabs.
Currently the WinForm application only has one main TabControl and I was looking at the DoDragDrop() related events, but they seem to only work when you have two TabControls and move TabPages around those two.
Is there a way to make it work with only one TabControl? Meaning, If you Drop a TabPage out of the TabControl then it will create a new TabControl with the TabPage in it?
I can only think of using:
private void TabControl_DragLeave(object sender, EventArgs e)
{
Form newInstance = new Form();
TabControl newTabControl = new TabControl();
newInstance.Controls.Add(newTabControl);
newTabControl.TabPages.Add(sender as TabPage);
newInstance.Show();
}
but that is pretty crud and will create the new tab every time you leave the TabControl.
It seems you are looking for an event which raises at the end of drop, regardless of ending over your control or outside of the control.
You can rely on QueryContinueDrag and check if the action is Drop, then check the mouse position and for example if it's not inside your control, just create another window and add the selected tab into a tab control inside the new window.
private void tabControl1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
tabControl1.DoDragDrop(tabControl1.SelectedTab, DragDropEffects.All);
}
}
private void tabControl1_DragOver(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(TabPage)))
e.Effect = DragDropEffects.Move;
}
private void tabControl1_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
{
if (e.Action == DragAction.Drop)
{
var tabPage = tabControl1.SelectedTab;
if (!tabControl1.RectangleToScreen(tabControl1.Bounds).Contains(Cursor.Position))
{
var form = new Form();
form.Text = tabPage.Text;
var tabControl = new TabControl();
tabControl.TabPages.Add(tabPage);
tabControl.Dock = DockStyle.Fill;
form.Controls.Add(tabControl);
form.FormBorderStyle = FormBorderStyle.SizableToolWindow;
form.StartPosition = FormStartPosition.Manual;
form.Location = new Point(Cursor.Position.X - form.Width / 2,
Cursor.Position.Y - SystemInformation.CaptionHeight / 2);
form.Show();
e.Action = DragAction.Cancel;
//You can comment tabControl.TabPages.Add
//Then set e.Action = DragAction.Continue
//Then the DragDrop event will raise and add the tab there.
}
}
}
private void tabControl1_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(TabPage)))
{
var tabPage = (TabPage)e.Data.GetData(typeof(TabPage));
tabControl1.TabPages.Remove(tabPage);
tabControl1.TabPages.Add(tabPage);
}
}
For more advanced scenarios and to enhance the code:
When start dragging, you can start dragging just if the mouse dragged at least for a specific points, for example 16 points. It's easy to calculate. Having p1 as mouse down point and p2 as mouse move point, and d as drag threshold. start dragging just in case (p1.X-p2.X)*(p1.X-p2.X) + (p1.Y-p2.Y)*(p1.Y-p2.Y) > d*d.
You can use GiveFeedback event to disable default cursor of the mouse and instead show a more suitable cursor while dragging, easily by e.UseDefaultCursors = false; and setting Cursor.Current = Cursors.SizeAll; for example.
You can encapsulate the logic and put it in a derived TabControl. Then in DragEnter and DragLeave events set a static property for tracking drop target. In case the drop-target has value, it means you are dropping on a derived tab control, otherwise it means you are dropping outside. Then drag and drop will be easily enabled for all your custom tab controls.
You can close the tool form, after a drag and drop, in case the form doesn't contain any other tab.
When adding the tab, you can insert it before/after the selected tab or the tab under cursor in target.
I have this menu (Android style), which is in a FlowLayoutPanel that organizes the elements (Bunifu Tile Buttons):
Well, I thought about implementing a drag function where you could reposition the elements in execution time by dragging them with the mouse as it is done in Android.
To do this, I used a FlowLayoutPanel to organize the elements and this code::
...
Interval = 100, Enabled = true;
private void Timer_0_Tick(object sender, EventArgs e)
{
var cursor = Cursor.Position;
if(bunifuTileButton1.DisplayRectangle.Contains(cursor))
{
if(Hector.Framework.Utils.Peripherals.Mouse.MouseButtonIsDown(Hector.MouseButton.Left))
{
bunifuTileButton1.Location = cursor;
}
}
}
But when I drag the elements, they simply return to their original position by releasing the mouse button.
So, my question is:
Is it possible to implement a function that in C # Winforms that reorganize the elements as Android does using a FlowLayoutPanel that automatically organizes the elements in execution time?
Firstly , you need to implement the functionality which will help you drag-and-drop your controls.But before that, you need to store the control's current position in some class-level variables.Consider the following code snippet :
int xloc;
int yloc;
///other codes in-between
private void btn1_Click()
{
xloc = this.btn1.Location.X;
yloc = this.btn1.Location.Y;
}
Now the drag-and-drop feature :
private Point setNewLocation;
private void btn1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
setNewLocation= e.Location;
}
}
private void btn1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
btn1.Left = e.X + btn1.Left - setNewLocation.X;
btn1.Top = e.Y + btn1.Top - setNewLocation.Y;
}
}
This will help you move the control...But what happens when you place a control over another one ? I mean you definitely don't want one control to overlap the other.Rather you may want to,when one control is placed over the other, the other control will change it's position to the previous position of the control that is overlapping it.So, on the MouseUP event of the currently dragged control , use this :
private void btn1_MouseUP()
{
foreach (Control other in FlowlayoutPanel1.Controls)
{
///change control type as required
if (!other.Equals(btn1) && other is Button && btn1.Bounds.IntersectsWith(other.Bounds))
{
other.Location = new Point(xloc, yloc)
}
}
I haven't debugged the code so if you encounter any bug, make sure to leave a comment :)
I am creating a Word Add-In and in order to allow dragging something from a custom task pane to the document, I have followed the following guide:
http://msdn.microsoft.com/en-us/library/office/hh780901(v=office.14).aspx
There are some real drawbacks using this approach.
First, the transparent Windows Form (or WPF in my case) that catches the drop event is the size of the Window, not the document, and RangeFromPoint always returns a value, even if we aren't over the document (for instance, if we are over the Ribbon). So once you drag something and this form is created, no matter where you drop, it will be placed in the document. There is no graceful way to cancel once you've started.
My question is:
Has anyone done any work with Drag and Drop in a Word Add In, and found a better way to handle it than the supplied example by Microsoft?
It would be nice to either use the current solution, but know when the user is not dragging over the document or have that transparent window only show over the document area.
Hope you already had your answer.
I got a solution for my own.
So, my requirement:
I have a custom pane, which contains a listbox, each item is a normal string. When I drag an item from the listbox into the document, at a specific location, I want to insert a merge field at that location. The name of the merge field is the text of the item.
It was simple at first, then I got a problem just like you describe in your question.
About the code
So, there is a listbox, you need to handle mouseDown and mouseMove, don't worry about mouseUp.
In mouseDown handler, I record the boundary, if the mouse moves out of that boundary, the drag will start.
Then, in listBox_MouseMoveHandler, I check position of the mouse to start the dragdrop. And I have to use DragDropEffects.Copy for the DoDragDrop method.
DoDragDrop((sender as ListControl).SelectedValue, DragDropEffects.Copy);
With that option, SelectedValue will be inserted at the drop position, and after it is inserted, it will also be selected.
Then, I just check if selection is not empty, and replace the selected text with the merge field. Of course, I collapsed the selection before DoDragDrop. And that is the whole trick.
private int _selectedItemIndex;
private Rectangle dragBoxFromMouseDown;
private void CustomizationForListBox(ListBox listBox)
{
listBox.ItemHeight = 25;
listBox.DrawMode = DrawMode.OwnerDrawFixed;
listBox.DrawItem += ListBox_DrawItem;
listBox.MouseDoubleClick += listBox_MouseDoubleClick;
listBox.MouseMove += listBox_MouseMoveHandler;
listBox.MouseUp += listBox_MouseUp;
listBox.MouseDown += (sender, e) =>
{
// Handle drag/drop
if (e.Button == MouseButtons.Left)
{
_selectedItemIndex = listBox.IndexFromPoint(e.Location);
// Remember the point where the mouse down occurred. The DragSize indicates
// the size that the mouse can move before a drag event should be started.
Size dragSize = SystemInformation.DragSize;
// Create a rectangle using the DragSize, with the mouse position being
// at the center of the rectangle.
dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2),
e.Y - (dragSize.Height / 2)), dragSize);
}
};
}
private void listBox_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// Reset the drag rectangle when the mouse button is raised.
dragBoxFromMouseDown = Rectangle.Empty;
}
}
private void listBox_MouseMoveHandler(object sender, MouseEventArgs e)
{
// Handle drag and drop
// To check if the Mouse left button is clicked
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
// If the mouse moves outside the rectangle, start the drag.
if (dragBoxFromMouseDown != Rectangle.Empty &&
!dragBoxFromMouseDown.Contains(e.X, e.Y))
{
// Collapse current selection, now we know nothing is selected
Globals.ThisAddIn.Application.Selection.Collapse(WdCollapseDirection.wdCollapseEnd);
//Start Drag Drop
DoDragDrop((sender as ListControl).SelectedValue, DragDropEffects.Copy);
if (_selectedItemIndex != -1)
{
// If the drag/drop was successful, there dropped text must be selected
if (!String.IsNullOrWhiteSpace(Globals.ThisAddIn.Application.Selection.Text))
{
// Replace the selected text with a merge field MergeFieldHelper.InsertSingleMergeField(mergeFieldInfos[_selectedItemIndex].Name);
}
}
}
}
}
I have a winform on which i want to allow the user to move a control.
The control is (for now) a vertical line : label with border and a width of 1.
The context is not very important but i'll give it to you anyways. I have a background with some graphics and i'd like the user to be able to slide a guideline above the graphics. The graphics are made with the NPlots library. It looks something like this:
http://www.ibme.de/pictures/xtm-window-graphic-ramp-signals.png
If i can find out how the user can click and drag the label/line control around the screen, i can solve my guideline problem. Please help.
The code for this can get a bit complex, but essentially you will need to capture the MouseDown, MouseMove, and MouseUp events on your form. Something like this:
public void Form1_MouseDown(object sender, MouseEventArgs e)
{
if(e.Button != MouseButton.Left)
return;
// Might want to pad these values a bit if the line is only 1px,
// might be hard for the user to hit directly
if(e.Y == myControl.Top)
{
if(e.X >= myControl.Left && e.X <= myControl.Left + myControl.Width)
{
_capturingMoves = true;
return;
}
}
_capturingMoves = false;
}
public void Form1_MouseMove(object sender, MouseEventArgs e)
{
if(!_capturingMoves)
return;
// Calculate the delta's and move the line here
}
public void Form1_MouseUp(object sender, MouseEventArgs e)
{
if(_capturingMoves)
{
_capturingMoves = false;
// Do any final placement
}
}
In WinForms, you can handle the MouseDown, MouseMove and MouseUp events of a control. On MouseDown, set some bit or reference telling your form what control the mouse was clicked on, and capture the X and Y of the mouse from MouseEventArgs. On MouseMove, if a control is set, adjust its X and Y by the difference between your last captured X and Y and the current coordinates. On MouseUp, release the control.
I would set up an "edit mode" for this; when the user enters this mode, the current event handlers of your form's controls should be detached, and the movement handlers attached. If you want to persist or revert these changes (like you're making a custom form designer your client can use to customize window layouts), you'll also need to be able to take some sort of snapshot of the before and after layouts of the controls.
I wrote a component to do exactly that: move a control on a form (or move a borderless form on the screen). You can even use it from the designer, without writing any code.
http://www.thomaslevesque.com/2009/05/06/windows-forms-automatically-drag-and-drop-controls-dragmove/
Here's an extension method that you can use for any control. It uses Rx and is based on the A Brief Introduction to the Reactive Extensions for .NET, Rx post and sample by Wes Dyer.
public static class FormExtensions
{
public static void EnableDragging(this Control c)
{
// Long way, but strongly typed.
var downs =
from down in Observable.FromEvent<MouseEventHandler, MouseEventArgs>(
eh => new MouseEventHandler(eh),
eh => c.MouseDown += eh,
eh => c.MouseDown -= eh)
select new { down.EventArgs.X, down.EventArgs.Y };
// Short way.
var moves = from move in Observable.FromEvent<MouseEventArgs>(c, "MouseMove")
select new { move.EventArgs.X, move.EventArgs.Y };
var ups = Observable.FromEvent<MouseEventArgs>(c, "MouseUp");
var drags = from down in downs
from move in moves.TakeUntil(ups)
select new Point { X = move.X - down.X, Y = move.Y - down.Y };
drags.Subscribe(drag => c.SetBounds(c.Location.X + drag.X, c.Location.Y + drag.Y, 0, 0, BoundsSpecified.Location));
}
}
Usage:
Button button1 = new Button();
button1.EnableDragging();
Well in all honesty there is a simpler way where by you initialize a global boolean variable called whatever you like, in this case, isMouseClicked. On your control you wish to allow dragging you go to its mouse down event,
Make sure these event are your control events not your forms event.
if (e.button == MouseButtons.left)
//this is where you set the boolean to true
Then go to its mouse move event
if (isMouseClicked == true)
//You then set your location of your control. See below:
Button1.Location = new Point(MousePosition.X, MousePosition.Y);
On your mouse up make sure to set your isMouseClicked to false;
Purpose is to have the opacity event trigger when the form loses focus. The form has a setting for STAY ON TOP. The visual effect would be to click on a possibly overlapping window, and yet the form when not focused on would stay on top, but in the corner slightly transparent, keeping it within easy access, but providing visibility to the stuff underneath.
I 've googled and googled, and can't figure out how to get this event to properly fire when form loses focus, and then when form gains focus back to restore opacity to 100% or the level determined elsewhere.
Tips?
// under designer.cs
//
// CollectionToolForm
//
//other code....
this.LostFocus += new System.EventHandler(goTransparent);
//method
private void goTransparent(object sender, EventArgs e)
{
if (transparentCheck.Checked == true)
{
this.Opacity = 0.50;
}
else
{
this.Opacity = 1;
}
}
It sounds as if you are looking for the Activated and Deactivate events.
Update
In response to the comment about LostFocus event, it could be of interest to clarify how it works. The LostFocus event of the Form is inherited from Control. It is raised when a controls loses focus; either because the form as such is being deactivated (focus moves to another application for instance), or because focus moves to another control within the same form.
If you hook up an event handler for the LostFocus event of a form that contains only at least one control that can receive focus, you will find that the LostFocus event of the form is raised immediately after the form is displayed. This is because focus moves from the form (which is a Control) to the first focusable control on the form.
So, the form being active and the form being focused are two separate behaviours.
You tried doing it with mouse enter/leave events?
public Form1()
{
this.MouseEnter += new System.EventHandler(this.Form1_MouseEnter);
this.MouseLeave += new System.EventHandler(this.Form1_MouseLeave);
}
private void Form1_MouseLeave(object sender, EventArgs e)
{
this.Opacity = 0.5;
}
private void Form1_MouseEnter(object sender, EventArgs e)
{
this.Opacity = 1;
}