C# Custom display arrangement settings - c#

So I am trying to make a little utility for managing my screens, I want to add a screen rearrangement system like in windows, see here: Windows Display Rearangment, I already have code worked out for getting and setting the exact screen coordinates I need, however I need to work out some sort of rearrangement dragging system that "snaps" to position like the windows implementation.
so far I have made some basic strides towards this, before coming to the realization the snapping will be a bit challenging, so here is the code I have so far, and here is an attached screenshot of the program as is. It consists of a panel with 3 panels inside it. For now I would like tips on when the mouse is released and the collision checks are run to snap similarly to windows display rearangment, Just a test bed for now, I am aware that this example of my test bed will currently only work with the dragging of the 3rd panel testing against the other 2 static panels, I will implement the functionality for all screens once we solve the issue :) Current
private Point MouseDownLocation;
private void panel2_MouseMove(object sender, MouseEventArgs e)
{
var pan = (Panel)sender;
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
pan.Left = e.X + pan.Left - MouseDownLocation.X;
pan.Top = e.Y + pan.Top - MouseDownLocation.Y;
}
}
private void panel2_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
var pan = (Panel)sender;
List<Panel> allpanels = new List<Panel>();
foreach (Panel pancomp in panel1.Controls)
{
if (pancomp == pan)
{
continue;
}
allpanels.Add(pancomp);
}
//var pan = (Panel)sender;
List<Panel> collisions = new List<Panel>();
foreach (Panel pancomp in allpanels)
{
if (pancomp == pan)
{
continue;
}
if (pan.Bounds.IntersectsWith(pancomp.Bounds))
{
//PUSH AWAY
collisions.Add(pancomp);
//listBox1.Items.Add("collided!");//panel2.Location.ToString());
}
}
collisions = collisions;
if (collisions.Count > 0)
{
//WE HAVE COLLISIONS LETS SNAP TO VALID PLACE
//MouseDownLocation
}
else
{
//WE HAVE NO COLLISIONS LETS SNAP TO PLACE
}
}
}
private void panel2_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
MouseDownLocation = e.Location;
}
}
Any advice would be greatly appreciated :)
Regards
James

Related

Winforms custom cursor stops setting after a time

I have a form that contains a panel. The idea is that when the mouse is hovered over the panel it displays the OpenHand cursor. You can then drag the panel which causes the cursor to change to the Grab cursor. Upon mouse release the Grab cursor should change back to the OpenHand cursor.
Unfortunately, I'm having an issue that if I drag and release my panel (with the mouse within the bounds of the panel), eventually (usually on the second or third go) the Grab cursor will not change back into the OpenHand cursor.
This remains the case even when you move the mouse out of the panel and back in and try dragging and releasing.
Here is my Panel class:
public class MapPanel : Panel
{
private Point MouseDownLocation;
private Cursor OpenHand = new Cursor(Properties.Resources.hand.Handle);
private Cursor Grab = new Cursor(Properties.Resources.punch.Handle);
public MapPanel()
{
MouseDown += new MouseEventHandler(mapPanel_MouseDown);
MouseUp += new MouseEventHandler(mapPanel_MouseUp);
MouseMove += new MouseEventHandler(mapPanel_MouseMove);
}
private void mapPanel_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Cursor = Grab;
MouseDownLocation = e.Location;
}
}
private void mapPanel_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Cursor = OpenHand;
}
}
private void mapPanel_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Left = e.X + Left - MouseDownLocation.X;
Top = e.Y + Top - MouseDownLocation.Y;
}
}
}
Interestingly there are no issues if I don't include the MouseMove handler, however the Cursor = OpenHand; statement inside the MouseUp method does still get called with the MouseMove handler included. It just doesn't take effect.
Hopefully someone can shed some light on this. Any help would be very much appreciated!

C# - Winforms - Custom Drag Image is Not Displayed When Dragging by Touch

I have an old WinForms app written in C# with custom drag and drop capabilities. And this app has an issue when it is run on a touch screen device, such as the Surface Pro 3.
Basically there is a treeview control that allows the items to be dragged to a different area of the app and have some calculations done. If I use the mouse or the stylus the custom drag image is drawn on the screen. If I drag an item using touch the image is not displayed but the code is executed properly, including the drawing of a custom cursor.
It seems that the custom drag image is not displayed because the mouse cursor is hidden by the O.S. during a touch drag operation. How do I get the drag image to display?
UPDATE
Here is some code to demonstrate what I am trying to fix. Create a new WinForms app, add a treeview to it and wire up the events. You'll notice that if you use a stylus or the mouse the drag operation will show an icon. If you touch-drag an item, nothing shows.
public Form1()
{
InitializeComponent();
TreeNode node = new TreeNode("welp");
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
}
private void treeView1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void treeView1_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
e.UseDefaultCursors = true;
}
private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
{
((TreeView)sender).DoDragDrop(e.Item, DragDropEffects.Move);
}
Ok, So I got this working exactly the way that I want it to. Wasn't easy and it's probably not the preferred way to go about it but it does what I want and I was on a time crunch to get this finished.
First, I found this article Shell Style Drag and Drop. It worked, for the most part, but it was written about 7 years ago and doesn't take into consideration how the O.S. has changed. Too much to go into the details about that and why it didn't work for me.
The main issue I was trying to fix was that my original drag and drop code used the old school way of drawing a new cursor in the GiveFeedback event of a drag and drop operation. Worked great except on touch devices because the Windows O.S. hid the cursor during a touch-drag operation so my custom drawn cursor would never be displayed. A few (used lightly) Google searches revealed that Windows won't allow two pointer devices to be active at the same time, even with calling the native ShowCursor method. Fair enough.
The biggest issue with trying to draw on the screen while in a drag and drop operation is that most of the messages sent to a window, such as WM_MOUSEMOVE and WM_PAINT are suspended. The only way I could draw anything was in the GiveFeedback event.
So, I found this article that covers drawing using a transparent overlay. Again, it didn't do exactly what I wanted but with a little tweaking it worked perfectly. I was able to grab the HDC of the overlay and the HDC of my form and do a nice BitBlt operation to display my custom bitmap on the screen and erase the old drawing by grabbing the underlying image of the form. I also changed the method that draws a custom cursor to a method that draws a custom bitmap.
Here is what I changed in regards to the article above
this.BackColor = Color.White;
this.Opacity = 1; // Tweak as desired
this.TransparencyKey = Color.White;
And what I added to allow the message pump to go through
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= Win32.WS_EX_TRANSPARENT;
return createParams;
}
}
The details of the rest are not overly complex but require a nice blending of the drag and drop operations, the overlay, drawing and erasing and double buffering. And there are situations, like in my case, where I have a TreeView control updating while I am dragging and I have to expand the rectangle of the draw operation so the bitblt image covers the areas outside of my drawing.
If you are interested in this article and want to see what I did and how I accomplished it, let me know in the comments and I will post code.
try this code
hope is helped you
public Form1()
{
InitializeComponent();
TreeNode node;
for (int x = 0; x < 3; ++x)
{
node = treeView1.Nodes.Add(String.Format("Node{0}", x * 4));
for (int y = 1; y < 4; ++y)
{
node = node.Nodes.Add(String.Format("Node{0}", x * 4 + y));
}
}
treeView1.AllowDrop = true;
treeView1.Dock = DockStyle.Fill;
treeView1.ItemDrag += new ItemDragEventHandler(treeView1_ItemDrag);
treeView1.DragEnter += new DragEventHandler(treeView1_DragEnter);
treeView1.DragOver += new DragEventHandler(treeView1_DragOver);
treeView1.DragDrop += new DragEventHandler(treeView1_DragDrop);
}
private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
DoDragDrop(e.Item, DragDropEffects.Move);
}
else if (e.Button == MouseButtons.Right)
{
DoDragDrop(e.Item, DragDropEffects.Copy);
}
}
private void treeView1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = e.AllowedEffect;
}
private void treeView1_DragOver(object sender, DragEventArgs e)
{
Point targetPoint = treeView1.PointToClient(new Point(e.X, e.Y));
treeView1.SelectedNode = treeView1.GetNodeAt(targetPoint);
}
private void treeView1_DragDrop(object sender, DragEventArgs e)
{
Point targetPoint = treeView1.PointToClient(new Point(e.X, e.Y));
TreeNode targetNode = treeView1.GetNodeAt(targetPoint);
TreeNode draggedNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
if (!draggedNode.Equals(targetNode) && !ContainsNode(draggedNode, targetNode))
{
if (e.Effect == DragDropEffects.Move)
{
draggedNode.Remove();
targetNode.Nodes.Add(draggedNode);
}
else if (e.Effect == DragDropEffects.Copy)
{
targetNode.Nodes.Add((TreeNode)draggedNode.Clone());
}
targetNode.Expand();
}
}
private bool ContainsNode(TreeNode node1, TreeNode node2)
{
if (node2.Parent == null) return false;
if (node2.Parent.Equals(node1)) return true;
return ContainsNode(node1, node2.Parent);
}

Redrawing issues when moving a control

I am trying to move some controls around on a WinForm with the mouse. I am using the code below. To see my issue start a new project in VS add the code below. Set the form BackGroundImage to any image then add any control. Set the control events for MouseUp, MouseDown, and MouseMove. Start debugging and click and move the control. The image in the form starts getting erased. I have tried several different suspend drawing classes and methods I have found on the net but nothing I have found lets me move the controls around without serious flickering or not being able to see the move. Any help would be appreciated.
P.S. If you set the same events to the up, move, and down events of the form, it moves fine with out any flickering.
private bool _mouseDown;
private Point _startPoint;
private void Event_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_mouseDown = true;
_startPoint = new Point(e.X, e.Y);
}
}
private void Event_MouseUp(object sender, MouseEventArgs e)
{
_mouseDown = false;
}
private void Event_MouseMove(object sender, MouseEventArgs e)
{
if (_mouseDown)
{
Control s = sender as Control;
s.Location = new Point(e.X + s.Left - _startPoint.X, e.Y + s.Top - _startPoint.Y);
}
}

C# Can't drag my button with mouse

So i searched a little around on google to find some code to drag buttons with the mouse. I found a lot, even though none of these actually worked for me.
So i'm asking you! :-)
The code i'm trying to get working in my form:
bool isDragged = false;
Point ptOffset;
private void button1_MouseDown( object sender, MouseEventArgs e )
{
if ( e.Button == MouseButtons.Left )
{
isDragged = true;
Point ptStartPosition = button1.PointToScreen(new Point(e.X, e.Y));
ptOffset = new Point();
ptOffset.X = button1.Location.X - ptStartPosition.X;
ptOffset.Y = button1.Location.Y - ptStartPosition.Y;
}
else
{
isDragged = false;
}
}
private void button1_MouseMove( object sender, MouseEventArgs e )
{
if ( isDragged )
{
Point newPoint = button1.PointToScreen(new Point(e.X, e.Y));
newPoint.Offset(ptOffset);
button1.Location = newPoint;
}
}
private void button1_MouseUp( object sender, MouseEventArgs e )
{
isDragged = false;
}
I of course changed the pictureBox1 to button1.
But i just can't get this to work.
Anyone who might know why?
Oh, and i want to use this at all my buttons, so what should i replace button1 with to make it work at all of the buttons?
-Btw, i use Visual studio Express.
Thank you in advance!
even though i'm really just copy pasting when it comes to windows
forms
The code you posted won't actually do anything by itself!
To work, the MouseDown() and MouseMove() events of the control have to be wired up to those methods:
Select the control on the form (pictureBox1).
In the Properties Pane (bottom right by default), click the
"Lightning Bolt" icon to get a list of the events for that control.
Find the MouseDown entry and change the dropdown to the right of
it to pictureBox1_MouseDown.
Find the MouseMove entry and change the dropdown to the right of
it to pictureBox1_MouseMove.
Now run it and drag the pictureBox1 around.
EDIT: Here is how to make the code work for multiple controls, as outlined in my comment below.
bool isDragged = false;
Point ptOffset;
private void button1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
isDragged = true;
Button btn = (Button)sender;
ptOffset = new Point(btn.Location.X - Cursor.Position.X, btn.Location.Y - Cursor.Position.Y);
}
else
{
isDragged = false;
}
}
private void button1_MouseMove(object sender, MouseEventArgs e)
{
if (isDragged)
{
Point newPoint = Cursor.Position;
newPoint.Offset(ptOffset);
Button btn = (Button)sender;
btn.Location = newPoint;
}
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
isDragged = false;
}
Ok, the good news: hopefully we'll have a CSI desktop layout once you're done.
The not so good news: I think you're trying to do the wrong thing.
I'm assuming you're using WinForms, but the idea should be the same for WPF.
When you run your program, the layout is set (either dynamically, for example it changes when you resize the window, or statically, for example when you set it with absolute X,Y position).
What you want to do is kind of: I want to get the benefits of them creating all the boiler plate code, but then, I want to dynamically change it at run time ...
I think you need to rethink your approach, and take this one step at a time (after all, it took Microsoft years to get the GUI to where it's at at the moment ... It'll take some time to get the cool futuristic GUI's out).
Take baby steps, but keep in mind the big picture:
Start with having a Canvas defined.
Then you can place a button on it.
Then fool around with moving that button on the canvas. (The code you have in your question should work for an object on a canvas).
From there, keep taking steps that will get you closer to your destination ...

"Click and Drag-Mouse-Over" behavior for Objects in a Panel

I'll try and keep this generic for future refference.
Let's suppose our Form has a grid of Squares (RectangleShapes which we'll reffer to individually as "points".) Since the size of this grid can vary before the Form loads, we've create all the points in a panel when we first load our Form and can use...
foreach (Squares point in mySquares)
...when we want to change their behavior or appearance. Now, we've already developed code to change the color of each individual point when the user clicks left or right. It would look something like:
private void panelGrid_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left || e.Button == MouseButtons.Right)
{
foreach (Squares point in mySquares)
{
if (point.Rectangle.Contains(e.Location)) // determine which point to use
{
if (e.Button == MouseButtons.Left)
{
Pencil(point); // left-click to fill color
}
else
{
Erase(point); // right-click to erase color
}
}
}
panelGrid.Invalidate(); // refreshes grid
}
}
And this works like a charm. But suppose we need to change this code: Now we also want the color to change when a mouse button is held down, and the cursor is moved onto a new point. (Sort of like MS-Paint: Dragging the pencil tool over multiple pixels fills each one in turn.)
What confuses me is the proper way to implement this behavior properly. To my understanding, I would want the Pencil/Eraser methods to call when:
A.) The mouse enters a "point" AND a button is already held down.
OR
B.) A mouse button becomes pressed. (See above code)
Where this gets tricky for me is determining how best to implement the new checks, and how to perform them on the individual points in the grid - or even if that's necessary. Any tips?
Sounds like you want the MouseDown and MouseMove events to do the same thing:
private void MouseStuff(MouseEventArgs e) {
if (e.Button == MouseButtons.Left || e.Button == MouseButtons.Right)
{
foreach (Squares point in mySquares) {
if (point.Square.Contains(e.Location)) {
if (e.Button == MouseButtons.Left) {
Pencil(point);
} else {
Erase(point);
}
}
}
panelGrid.Invalidate();
}
}
then try calling it from both events:
private void panelGrid_MouseDown(object sender, MouseEventArgs e) {
MouseStuff(e);
}
private void panelGrid_MouseMove(object sender, MouseEventArgs e) {
MouseStuff(e);
}
Look into using a double-buffered panel to make the control less flicky since invalidating the panel on a MouseMove event can be quite intensive:
public class BufferedPanel : Panel {
public BufferedPanel() {
this.DoubleBuffered = true;
this.ResizeRedraw = true;
}
}

Categories