Snap-To lines when aligning controls at Runtime - c#

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

Related

How to position a window on multi-monitor displays in WPF?

I'm trying to position a window in the top right corner of my secondary display. In the Window_Loaded event handler, I have the following code:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.Left = Screen.AllScreens[1].WorkingArea.Left;
this.Top = Screen.AllScreens[1].WorkingArea.Top;
}
This works perfectly well when both my displays have a scale factor of 100%, but as soon as I change the scale of the primary display, the window loads completely offscreen.
Does anyone know a way to absolutely position a window in WPF? Most of the answers I found are pre-Win8.1 and don't have to worry about scaling. I can't seem to figure out the pattern behind the Top and Left properties. Thank you for your help.
I have found a solution to this problem on CodeProject
Wpf windows on two screens
The solution is to calculate the ratio between the real resolution and the WorkingArea's resolution.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var scaleRatio = Math.Max(Screen.PrimaryScreen.WorkingArea.Width / SystemParameters.PrimaryScreenWidth,
Screen.PrimaryScreen.WorkingArea.Height / SystemParameters.PrimaryScreenHeight);
this.Left = Screen.AllScreens[1].WorkingArea.Left / scaleRatio ;
this.Top = Screen.AllScreens[1].WorkingArea.Top / scaleRatio ;
}
You can do the following
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var p = this.PointFromScreen(new Point(Screen.AllScreens[1].WorkingArea.X, Screen.AllScreens[1].WorkingArea.Y));
this.Left += p.X;
this.Top += p.Y;
}
I figured out that the PointFromScreen function tells you the offset of an absolute coordinate from your window. Pick the position of the top left of your display and you know how much you have to move - but critically it's in the same units as your window's Top and Bottom. This is a pretty narrow case, but it be can extrapolated to positioning in general. Hope it Helps!
Maybe I'm completly wrong, but center on a screen (multimonitor) with different dpi I used this and measured it with an ruler (no joke):
var handle = new System.Windows.Interop.WindowInteropHelper(this).Handle;
var screen = System.Windows.Forms.Screen.FromHandle(handle);
var scaleRatio = Math.Max(VisualTreeHelper.GetDpi(this).DpiScaleX, VisualTreeHelper.GetDpi(this).DpiScaleY);
this.Left = (screen.WorkingArea.Left + (screen.WorkingArea.Width - this.Width * scaleRatio) / 2) / scaleRatio;
this.Top = (screen.WorkingArea.Top + (screen.WorkingArea.Height - this.Height * scaleRatio) / 2) / scaleRatio;
And the wpf form is center on actual screen.
To put it to left side (on windows 10), then the solution is:
this.Left = screen.WorkingArea.Left/ scaleRatio - SystemParameters.ResizeFrameVerticalBorderWidth - SystemParameters.FixedFrameVerticalBorderWidth;

Programmatically Scrolling is not working C# WinForm

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!

Check if usercontrol is within Window Bounds

I have a project that requires the ability to drag a user control (hosted within a grid) around the screen. This works just fine with the below code:
void MyUserControl_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
var ct = (CompositeTransform) RenderTransform ?? new CompositeTransform {CenterX = 0.5, CenterY = 0.5};
ct.TranslateX += e.Delta.Translation.X;
ct.TranslateY += e.Delta.Translation.Y;
}
The issue is that the user control can be dragged all the way out of the screen area. To prevent this, I tried the example shown here: http://msdn.microsoft.com/en-us/library/system.windows.uielement.manipulationdelta.aspx
Unfortunately, it uses containingRect.Contains(shapeBounds) whereas in windows 8, we are expected to replace shapeBounds(this is a rect) with a Point. I am not sure on how to work with this.
So the question is how can we ensure that a user control or any UIElement cannot be dragged out of the Window.Current.Bounds area in windows 8 store app?
Thanks.
EDIT: More details on the xaml structure:
The mainpage contains a Grid with horizontal and vertical alignment set to stretch. Usercontrols are added to this grid as required. Each usercontrol has a parent grid that contains 3 different views (fullscreen, window and small). The views are shown based on the user's choice. The drag behavior must be applied only when the window grid is shown. So we have this
<Grid> <!-- this is the parent grid on mainpage with horizontal and vertical alignment to stretch-->
<Grid> <!-- this is the usercontrol's main grid (added to above grid via code). This grid must be draggable if the below grid is window -->
<Grid /> <!-- this is one of the child grids that is shown based on user's choice (fullscreen, window or small view).-->
</Grid>
</Grid>
I dont have much choice in changing the layout. Using the above ManipulationDelta event in the usercontrol (which is enabled/disabled based on the child grid shown), I am able to get the drag behavior, but the control can go out of window bounds. So is there any way we can add the below FlickBehavior in WinRTXamlToolkit through code instead of xaml OR enable/disable the behavior based on some condition?
<i:Interaction.Behaviors>
<views:HeavyFlickBehavior />
</i:Interaction.Behaviors>
You can check the FlickBehavior in WinRT XAML Toolkit for an example of how to achieve that with a parent Canvas and Canvas.Top/Left properties instead of RenderTransform, assuming the Canvas defines your bounds.
Here's an abstract:
private void OnAssociatedObjectManipulationStarting(object sender, ManipulationStartingRoutedEventArgs e)
{
_startPosition = new Point(
Canvas.GetLeft(this.AssociatedObject),
Canvas.GetTop(this.AssociatedObject));
}
private void OnAssociatedObjectManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs manipulationDeltaRoutedEventArgs)
{
var dx = manipulationDeltaRoutedEventArgs.Cumulative.Translation.X;
var dy = manipulationDeltaRoutedEventArgs.Cumulative.Translation.Y;
var x = _startPosition.X + dx;
var y = _startPosition.Y + dy;
if (manipulationDeltaRoutedEventArgs.IsInertial)
{
while (x < 0 ||
x > _canvas.ActualWidth - this.AssociatedObject.ActualWidth)
{
if (x < 0)
x = -x;
if (x > _canvas.ActualWidth - this.AssociatedObject.ActualWidth)
x = 2 *
(_canvas.ActualWidth - this.AssociatedObject.ActualWidth) -
x;
}
while (y < 0 ||
y > _canvas.ActualHeight - this.AssociatedObject.ActualHeight)
{
if (y < 0)
y = -y;
if (y > _canvas.ActualHeight - this.AssociatedObject.ActualHeight)
y = 2 * (_canvas.ActualHeight - this.AssociatedObject.ActualHeight) -
y;
}
}
else
{
if (x < 0)
x = 0;
if (x > _canvas.ActualWidth - this.AssociatedObject.ActualWidth)
x = _canvas.ActualWidth - this.AssociatedObject.ActualWidth;
if (y < 0)
y = 0;
if (y > _canvas.ActualHeight - this.AssociatedObject.ActualHeight)
y = _canvas.ActualHeight - this.AssociatedObject.ActualHeight;
}
Canvas.SetLeft(this.AssociatedObject, x);
Canvas.SetTop(this.AssociatedObject, y);
}

Getting mouse enter event on full panel c#

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;

Table layout panel cell bounds and control collision

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;
}

Categories