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);
}
Related
I have few textblocks in a grid which can be dragged. I want to restrict the user so that user can't drag a textblock outside the grid.
I've tried a few ways like getting the position of the grid so that somehow i can control but it didn't work as expected.
Thanks in advance.
This can be done pretty easily using a Canvas inside the Grid, calculating the coordinates of the TextBlock inside of the Canvas and then continuously checking whether the TextBlock is still within its bounds. When the TextBlock leaves its bounds, the Transform then reverts back to it's last known 'good' coordinates.
XAML
<Grid>
<Grid Name="GridBounds" Width="600" Height="600" Background="Aqua">
<Canvas>
<TextBlock Name="TextBlock1" Text="Drag Me" FontSize="40" ManipulationDelta="TextBlock1_ManipulationDelta" ManipulationMode="TranslateX, TranslateY" HorizontalAlignment="Stretch" Canvas.Left="216" Canvas.Top="234" VerticalAlignment="Stretch"/>
</Canvas>
</Grid>
</Grid>
Code Behind
CompositeTransform TextDrag = new CompositeTransform();
CompositeTransform savedTransform = new CompositeTransform();
public MainPage()
{
this.InitializeComponent();
TextBlock1.RenderTransform = TextDrag;
}
// Copy Transform X and Y if TextBlock is within bounds
private void CopyTransform(CompositeTransform orig, CompositeTransform copy)
{
copy.TranslateX = orig.TranslateX;
copy.TranslateY = orig.TranslateY;
}
private void TextBlock1_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
TextDrag.TranslateX += e.Delta.Translation.X;
TextDrag.TranslateY += e.Delta.Translation.Y;
CompositeTransform transform = TextBlock1.RenderTransform as CompositeTransform;
// Get current Top-Left coordinates of TextBlock
var TextTopLeft = TextBlock1.TransformToVisual(GridBounds).TransformPoint(new Point(0, 0));
// Get current Bottom-Right coordinates of TextBlock
var TextBottomRight = TextBlock1.TransformToVisual(GridBounds).TransformPoint(new Point(TextBlock1.ActualWidth, TextBlock1.ActualHeight));
// Get Top-Left grid coordinates
var GridTopLeft = (new Point(0, 0));
// Get Bottom-Right grid coordinates
var GridBottomRight = (new Point(GridBounds.ActualWidth, GridBounds.ActualHeight));
if (TextTopLeft.X < GridTopLeft.X || TextBottomRight.X > GridBottomRight.X)
{
// Out of bounds on X axis - Revert to copied X transform
TextDrag.TranslateX = savedTransform.TranslateX; ;
}
else if (TextTopLeft.Y < GridTopLeft.Y || TextBottomRight.Y > GridBottomRight.Y)
{
// Out of bounds on Y axis - Revert to copied Y transform
TextDrag.TranslateY = savedTransform.TranslateY;
}
else
{
// TextBlock is within bounds - Copy X and Y Transform
CopyTransform(transform, savedTransform);
}
}
Here it is in action.
I'm trying to realize a chess game in C# with WPF. By now I visualized the chess grid and the figures in it. My .xaml file contains just a grid (named "playground") which is 8x8. The initializing in the .xaml.cs file looks like the following:
for (int x = 0; x < 8; x++)
{
for (int y = 0; y < 8; y++)
{
Border border = new Border();
if (y % 2 == (x % 2 == 0 ? 0 : 1))
{ // Chess like look
border.Background = black; //black is a static SolidColorBrush
}
// a ChessTile is an Image which can be a Figure or an EmptyTile
// omitted code... evaluate whether what figure the tile is or empty
ChessTile tile;
Grid.SetColumn(border, x);
Grid.SetRow(border, y);
border.HorizontalAlignment = HorizontalAlignment.Stretch;
border.VerticalAlignment = VerticalAlignment.Stretch;
if (tile is Figure)
{
// Set Event to border so the user is able to click outside the image
border.MouseDown += ClickFigure;
}
border.Child = tile; // Set tile as Child
playground.Children.Add(border); // Add border to Child
}
}
After initializing I want to move a chess figure to another Column and or Row in the grid. Currently I am evaluating in ClickFigure on which empty tile the figure is able to move to, then adding a new Handler to these empty tiles and moving my Figures there. I'm calling tile.Move(otherTile) in the Handler:
(from ChessTile.cs)
public Vector Coordinates
{
get
{
return position; //private Vector
}
private set
{
position = value;
Grid.SetRow(this, (int)position.X); //relocate this (ChessTile)
Grid.SetColumn(this, (int)position.Y);
}
}
public void Move(ChessTile other)
{
Vector temp = position;
Coordinates = other.position; //relocating both tiles through properties
other.Coordinates = temp;
}
The problem now is that the grid does not do anything at all. I searched for some method to update/repaint the grid manually but I didn't find anything. I also tried to remove the child first and then to add it again but that gave me an error saying that the child was already bound to an UIElement and that I would have to seperate it first.
Do you have any idea what I am doing wrong?
You need to change the row and column on the original Border placed inside the Grid.
In your setup code, you set column and row on a border object:
Grid.SetColumn(border, x);
Grid.SetRow(border, y);
But then in your Coordinates property, you're setting the column and row on this.
Grid.SetRow(this, (int)position.X); //relocate this (ChessTile)
Grid.SetColumn(this, (int)position.Y);
Assuming this is a custom class you've written, then you need to make sure it has a reference to the original Border and change your Coordinate setter to:
Grid.SetRow(border, (int)position.X); //relocate the border!
Grid.SetColumn(border, (int)position.Y);
Notice the change from this => border.
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!
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