I've just finished building a portion of a program which deals with a UI in a table layout panel. This has worked so far but i've noticed that, before i could try my controls (functionality i added) at runtime around the form, but now it is in cells, they can't be moved outside of their container cell. HOWEVER, this is great and exactly what i needed, but im finding that the controls (for example a button) will be correctly contained in the cell on the left, and top boundaries of the cell, but the bottom and right boundaries allow the control to disappear off of it. Heres some screenshots to demonstrate:
Here we see that the button control cannot move past the top and left bounds of the cell.
However here it seems to be able to move past the bottom and right bounds of the cell.
Looking back at how i allow my controls to move, i came across a section where i had set up some variables, shown below:
public static void MouseMove(object sender, MouseEventArgs e)
{
Control control = sender as Control;
Control container = sender as Control;
if (control != null)
{
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);
}
}
}
}
I figured here i'm not setting a bottom and right container bounds, which would make sense, however upon exploring the intelisense, i can't seem to get container.right and container.bottom as they come with the following tooltip:
"gets the distance, in pixels, between the right edge of the control, and the left edge of it's container's client area"
and the bottom does the same, only for the bottom of the control and top of the container area.
Is there away around this? perhaps an option somewhere which connects the bottom of the control to the bottom bound of the cell, and the same for the right?
edit 1: alternatively perhaps i need to alter my mousemove event to handle collision better, so if anyone has any ideas on this too, that'd be great, i've not really looked at much collision detection before, especially in winforms.
Control.right is a read only property. Try setting
if (direction != Direction.Vertical)
{
container.Left = Math.Max(0, e.X + container.Left - DragStart.X);
container.Left = Math.Min(container.Left, container.Parent.Width - container.Width;
}
if (direction != Direction.Horizontal)
{
container.Top = Math.Max(0, e.Y + container.Top - DragStart.Y);
container.Top = Math.Min(container.Top, container.Parent.Height - container.Height;
}
Related
I've done a mini test program to prototype a way to drag and drop an image (at the right)(using a button as a support) to a panel (left part)(The current panel background letter are only for test purpose) and to move it inside the panel perimeter.
The image on the moving control is manually drawn during the Paint event :
void bouton_Paint( object sender, PaintEventArgs e )
{
Button but = sender as Button;
Bitmap img = new Bitmap(WindowsFormsApplication1.Properties.Resources.triaxe);
img.MakeTransparent(Color.White);
e.Graphics.DrawImage(img, 0, 0, but.Width, but.Height);
}
As you can see below, during the moving process, the background of the moved control is frezzed. So it is not very pretty
Is it to make the progress smoother ?
Thank you.
This is the code to move my triaxe on the form :
void bouton_MouseUp( object sender, MouseEventArgs e )
{
Button button = sender as Button;
button.Tag = false;
button.BackColor = Color.Transparent;
}
void bouton_MouseMove( object sender, MouseEventArgs e )
{
Button button = sender as Button;
if (button.Tag != null && (bool)button.Tag == true)
{
button.Left += e.X - MouseDownLocation.X;
button.Top += e.Y - MouseDownLocation.Y;
}
}
private Point MouseDownLocation;
void bouton_MouseDown( object sender, MouseEventArgs e )
{
Button button = sender as Button;
MouseDownLocation = e.Location;
button.Tag = true;
button.BackColor = SystemColors.Control;
}
Let's assume you do not want to draw the graphics alone but do want a Control with transparency move on top of another Control.
You have to solve two problems:
Winforms doesn't support transparency well; you have two options:
Either let the system fake it for you
Or do the faking yourself.
For the first it is enough to have a control with a transparent backcolor and an Image or BackgroundImage (depending on the control type) with transparency and then nest the moving control in the background control.
For a simplistic test you can add if (button.Parent != panel1) button.Parent = panel1; to your mousedown code. This will look jumpy but should display transparency correctly. (Don't forget to make the Backcolor transparent; in your code your make it SystemColors.Control.
To fake it yourself you would do exactly what the system does:
You get the target suface in a bitmaps (with DrawToBitmap) and then combine the area the mover currently covers with the image; here getting that area is key.
Then you can set that image as backgroundimage or draw it onto the mover.
The other problem is with the moving code. Here the issue is that when you move the mouse the MouseMove event is triggered and you can move the control. But by moving it the mouse will have moved relativly to the new location and the event will be triggered again leading to flicker an jumpiness!
To avoid this you can test the numbers or use a flag or unregister the move event before setting the location and re-register afterwards.
Also setting the location in one go instead of the two coordinates is a good idea..: button.Location = new Point(button.Left + e.X - MouseDownLocation.X, button.Top + e.Y - MouseDownLocation.Y);
Imo you should still consider drawing just the graphics on On panel.MouseMove and panel.Paint. On panel.MouseUp you can still add a Button to the panel.Controls right there. Both issues are avoided and you are free to add the functionality you want as well..
i have one panel and i put a flow layout panel in the main panel and flow layout panel has many images. my UI looks like this
now i got a code which scroll the container in the panel. i mean the flow layout will scroll in main panel when i will place my mouse at the left or right most area on the main panel. their code is working i checked but when i place their code in my project then that is not working, i means scrolling is not working when i place my mouse at the left or right most area on the main panel.
here i am attaching main code which causes to scroll the flow layout panel inside in main panel
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
int _myval = 5;
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.Y < metroPanel1.Top || e.Y > metroPanel1.Top + metroPanel1.Height) return;
if (e.X <= metroPanel1.Left && e.X >= metroPanel1.Left - 40)
{
if (metroPanel1.HorizontalScroll.Value <= _myval)
{
metroPanel1.HorizontalScroll.Value = _myval;
}
else
{
metroPanel1.HorizontalScroll.Value -= _myval;
}
}
if (e.X <= (metroPanel1.Left + metroPanel1.Width + 40) && e.X >= (metroPanel1.Left + metroPanel1.Width))
{
metroPanel1.HorizontalScroll.Value += _myval;
}
}
i just do not understand this value 40 used here if (e.X <= metroPanel1.Left && e.X >= metroPanel1.Left - 40)
i guess i need to use different value rather than 40 but i used 10 & 20 but did not work.
here is my full project link from where anyone can download and see what is wrong in my routine which prevent the scrolling. here is the link https://onedrive.live.com/embed?cid=C4A6F16F34D7540A&resid=C4A6F16F34D7540A%21134&authkey=AM5Fq2gcFLtcw_A
so it is my request that please some one see my code and guide me what i need to change in code and why. thanks
When you want to scroll dont use
metroPanel1.HorizontalScroll.Value -= _myval;
or
metroPanel1.HorizontalScroll.Value += _myval;
but instead
_myval -= 5;
or _myval += 5;
metroPanel1.HorizontalScroll.Value = _myval;
metroPanel1.Refresh();
valter
Yes.
Yes, programmatically scrolling a FlowLayoutPanel is not working, if the Scrollbars are not shown, due to its AutoScroll being off.
Neither setting HorizontalScroll.Value (in any way) nor using
[DllImport("user32.dll")]
static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
will do anything.
Not even doing this has any effect:
flowLayoutPanel1ScrollControlIntoView(someControlThatsOutOfSight);
So, as far as I can tell: Without active Scrollbar(s) no scrolling a FlowLayoutPanel!
The containing Panel can however indeed be scrolled the way valter shows. However my first tests showed even when using a double-buffered Panel subclass a terrible flicker..
Update:
I can't get the Panel to scroll reliably. I have checked up on the link and found that the behaviour is really much simpler than anything I had written: No up down scrolling, no edge detection, no speed detection, no direction detection. In fact there are two scroll zones and when the mouse is in one, it scrolls.. It also relies on the 'constant firing of the mousemove event bug', which only works for me when I don't want it..!
So, here is a solution that implements this behaviour and is, of course much shorter, than the original code.
I include a screenshot to show you the layout: You need two Panels and the FlowLayoutPanel.
The outer Panel is a little wider so that its right and left part work as the scroll zones.
The inner Panel contains the FLP; the FLP has AutoSize=true.
There is a timer scrollTimer with a fast Intervall of 5-10ms.
Here is the code:
int speed = 10;
int delta = 0;
private void panOuter_MouseMove(object sender, MouseEventArgs e)
{
delta = e.X < panOuter.Width / 2 ? speed : -speed;
scrollTimer.Start();
}
private void panOuter_MouseLeave(object sender, EventArgs e)
{
scrollTimer.Stop();
}
private void scrollTimer_Tick(object sender, EventArgs e)
{
int newLeft = FLP.Left + delta;
int alpha = panInner.ClientRectangle.Width - FLP.ClientRectangle.Width;
if (newLeft > 0) { newLeft = 0; scrollTimer.Stop(); }
else if ( newLeft < alpha)
{ newLeft = alpha; scrollTimer.Stop(); }
FLP.Left = newLeft;
}
That's practically all you need. Only in case you Dock or Anchor the outer Panel you should script the Resize event to keep the inner Panel centered!
So what i have is a panel that's programmatically filled with custom controls using DockStyle.Top.
What i need is for the panel to get focus somehow when mouse cursor enters the panel so that the user can use mousewheel to scroll the panel.
I don't really want to give each control a handler because there could be hundreds of controls.
One way could be checking for mouse position and check if the panel contains it, which would probably require an extra thread or mousehook but perhaps there's a better way?
You may implement the MouseDetector class posted by Amen Ayach as an answer to a similar question and activate the form when the mouse hovers it:
void m_MouseMove(object sender, Point p)
{
Point pt = this.PointToClient(p);
if (this.ClientSize.Width >= pt.X &&
this.ClientSize.Height >= pt.Y &&
pt.X > 0 && pt.Y > 0)
{
this.Activate();
}
}
You should also set the Panel's AutoScroll value to true.
panel.AutoScroll = true;
I have a Windows Forms application which is using a FlowLayoutPanel control to display a Picture boxes that are built dynamically. I have enabled the drag drop effect as they may want to reorder them, this works fine with only a few picture boxes (right now the screen shows about 6) but if there are more you try to drag an item below the control it will not scroll, so you cannot put an image that is currently on the screen (say image 4) to an image that is below what is visible (say image 13).
I have seen several posts where the ScrollControllIntoViewMethod should be used, I have tried in a few spots unsuccessfully.
Thanks!
Here is what I ended up doing.
Create the event on the DragLeave event
Getting the position of the control
Calculating the height of the control to get the lower boundary.
check the mouse position and if above the bounds, change the vertical scroll (or horizontal scroll) by a value in a Constant..
private void thumbFlow_DragLeave(object sender, EventArgs e)
{
int BegY_ThumbFlow = this.thumbFlow.FindForm().PointToClient(this.thumbFlow.Parent.PointToScreen(this.thumbFlow.Location)).Y;
int thumbFlowBound_Y = this.thumbFlow.Height + BegY_ThumbFlow;
int mouseY = this.thumbFlow.FindForm().PointToClient(MousePosition).Y;
while (mouseY >= thumbFlowBound_Y)
{
thumbFlow.VerticalScroll.Value = thumbFlow.VerticalScroll.Value + DRAG_DROP_SCROLL_AMT;
mouseY = thumbFlow.FindForm().PointToClient(MousePosition).Y;
thumbFlow.Refresh();
}
while (mouseY <= BegY_ThumbFlow)
{
thumbFlow.VerticalScroll.Value = thumbFlow.VerticalScroll.Value - DRAG_DROP_SCROLL_AMT;
mouseY = thumbFlow.FindForm().PointToClient(MousePosition).Y;
thumbFlow.Refresh();
}
}
Hope this helps others.
I have an app where users can drag controls around on a Form. But they
re asking me for Snap-To lines to make the alignment of controls easier. I have no idea about the snep-to lines and how to implement them - I have looked at:
http://msdn.microsoft.com/en-us/library/ms752100.aspx Adorner's, but it says it's only for WPF. And I tried it in WinForms but (as expected) didn't work.
How can I get snap-to lines (something like the ones in VS) in my app?
Thank you
Bael
Have you seen this article on CodeProject:
Form Designer
It features snap-to to the grid on the design surface.
In your move control you could adjust the Left and Top by dividing and then multiplying by the width of your lines:
left = (left/10)*10;
top = (top/10)*10;
It's not perfect but it it simple. Of course since controls don't have a MoveEnd event you'll have to track the MouseButton state or something similiar.
Edit: A better implementation would properly round the division results so 134 = 130 and 136 = 140.
I had the same issue, I'm still looking for a solution ; here is what I did so far, it could be a solution for you
const grid = 12;
private void MyControl_LocationChanged(object sender, EventArgs e)
{
if (this.Left % grid != 0)
this.Left -= this.Left % grid;
if (this.Top % grid != 0)
this.Top -= this.Top % grid;
}
or in a usercontrol
protected override void OnMove(EventArgs e)
{
if (this.Left % grid != 0)
this.Left -= this.Left % grid;
if (this.Top % grid != 0)
this.Top -= this.Top % grid;
}
My current challenge is the drawing activity; my controls are hosted in a panel, I am looking for a way to lock and unlock that panel drawing when necessary; for example: only after left or top changed