Draggable control in WPF? - c#

I'm kind of new to WPF although I have some experience in Forms, and I decided to finally try to figure out how to use WPF. So When I got to draggable controls, this is the code I came up with (I attempted to change it to work with WPF but the control just twitches everywhere):
private void rectangle1_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed) {
double x = this.Left + (double)e.GetPosition(this).X - (double)rectangle1.Margin.Left;
double y = this.Top + (double)e.GetPosition(this).Y - (double)rectangle1.Margin.Top;
rectangle1.Margin = new Thickness(x, y, rectangle1.Margin.Right, rectangle1.Margin.Bottom);
}
}

You want to use adorners to achieve dragging, resizing, rotating, etc.

Alternative:
Install NuGet package: Microsoft.Xaml.Behaviors.Wpf
Add this to root element:
xmlns:behaviors="http://schemas.microsoft.com/xaml/behaviors"
And just place this inside your element:
<Grid>
<behaviors:Interaction.Behaviors>
<behaviors:MouseDragElementBehavior ConstrainToParentBounds="True"/>
</behaviors:Interaction.Behaviors>
</Grid>

If you want to do it by hands use following algorithm:
On MouseDown event: Save Mouse position, TopLeft position of control, and delta(offset) of these coordinates, and set some boolean field flag eg. IsDragStartted to true.
On MouseMove check that drag started and use Mouse position and offset to calculate the new value of TopLeft position of control
On MouseUp event set IsDragStartted to false

here is a pretty good article on the matter on MSDN. also, a quick search on Google revealse a veritable cornucopia of choices for you DnD dining pleasure.

Related

Image manipulations in Universal Windows app

I have an image element inside a grid along with other controls in it. I want to be able to zoom the image. I don't want to place it inside a scroll viewer as I don't know the dimensions of the image and as it is a Universal app, I am having a lot of Adpative UI and the ScrollViewer messes with whole thing. The Image is downloaded from the web. When the user swipes left or right the source of the image should change to previous or next image. I am saving the sources of the images in a LinkedList and updating the source accordingly.
I implemented the swipe to change image source using the following code
MediaControl.ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY;
MediaControl.ManipulationStarted += (s, e) => { X1 = (int)e.Position.X; Y1 = (int)e.Position.Y; };
MediaControl.ManipulationCompleted += (s, e) => {
X2 = (int)e.Position.X;
Y2 = (int)e.Position.Y;
if (Math.Abs(X1 - X2) >= 50 && Math.Abs(Y1 - Y2) < 20)
{
{
if (X1 > X2)
{
NextPostButton_Click(s, e);
}
else
{
PreviousPostButton_Click(s, e);
}
}
};
};
I want to have zoom functionality in my image at the same time when source is changed, the image should be zoomed out. I want both the zoom and swipe functionality without each affecting the other manipulation. How do I do this? I am a beginner and have no experience in very complex Pointer events. If anyone could help me with this or point me to a good source for learning about this I would be eternally grateful.
Thank you.
Use a FlipView with a ScrollViewer inside of it and an Image inside of that. Then you just bind your FlipView's ItemsSource to a collection of Uri's. This will keep zoom functionality and swipe functionality automatically. Each image will retain its zoom factor as you swipe through your images. This works very well on mobile and desktops/tablets.
Let me know if you need some sample code. I'm on a phone right now so it's a little difficult to do source.
You can register the ManipulationDelta event, and use ManipulationDelta.Scale Property to take care of the zoom.
MediaControl.ManipulationMode = ManipulationModes.Scale | ManipulationModes.TranslateX;
private void MediaControl_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
MediaControl.Height = e.Delta.Scale * MediaControl.ActualHeight;
MediaControl.Width = e.Delta.Scale * MediaControl.ActualWidth;
}
For more information on ManipulationDelta, see to
https://msdn.microsoft.com/en-us/library/system.windows.input.manipulationdelta.scale(v=vs.110).aspx
For more information on ManipulationModes, see to
https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.input.manipulationmodes

Winrt - Adjusting flyout according to where it appears

I have a GridView in a windows store project, that contains some big squares, and inside those i have a list of user images, when i tap on of those images a flyout appears on the right showing some information like this.
the problem is that my gridview extends to the edges of the screen and beyond, and when that does i get this situation, i press the user with the red border near the edge of the screen and flyout appears on left.
My flyout placement is set to Right, and im guessing since the element i press is near the edge it follows the Fall back order with according to this is Right > Left > Top > Bottom.
What i would like to know is how to detect this happens, so i can adjust my flyout position, or another viable alternative :)
After searching through properties and Dependency properties on Flyout and FlyoutBase, I haven't found a way to simply get the actual placement of the Flyout (which is unfortunate because I think that can be important, as we see in your case). Perhaps you can try what was proposed here by implementing a method that compares the desired size of the Flyout with the available space.
You can subscribe to FlyOut.Opened event and compare absolute coordinates of the flyout and the element you're showing it at. Here is an example for top/bottom flyout placement (easily extendable to check for left/right as well):
private void FlyOut_Opened(object sender, object e)
{
GeneralTransform flyOutTransform =
flyOut.Content.TransformToVisual(Window.Current.Content);
Point flyOutPosition =
flyOut.TransformPoint(new Point(0, 0));
GeneralTransform showAtElementTransform =
showAtElementTransform.TransformToVisual(Window.Current.Content);
Point showAtElementPosition =
showAtElementPosition.TransformPoint(new Point(0, 0));
if(flyOutPosition.Y < showAtElementPosition.Y)
{
// top
}
else
{
// bottom
}
}

Getting the scrolling offset when storing coordinates

I am working on a Form that takes and draws a point on a mouse click. I am confused about how to properly get and add the scrolling offset so that the points can be drawn correctly. For example, right now when I add a point where the upper left coordinate is (0,0), the point redraws itself and moves with the scrolling action instead of staying at the spot it was originally created at. I have set
this.AutoScroll = true
and have set the minimum size manually
this.AutoScrollMinsSize = new Size(800,600);
Here is what my mouse click event looks like so far:
if (e.Button == MouseButtons.Left)
{
Point newPoint = new Point(e.X, e.Y);
p.X += this.AutoScrollOffset.X;
p.Y += this.AutoScrollOffset.Y;
this.Invalidate();
}
What is the proper way to use the AutoScrollOffset property to keep the points where they belong instead of moving as I scroll?
I should add that my program also overrides the Scroll event to repaint when a scroll event occurs to fix the problem of a drawing disappearing once the visible area was left.
AutoScrollOffset is not the correct property to use. It has very limited use, it can apply an offset to the scroll position when the ScrollControlIntoView() method is used. Which is pretty rare, never once used it myself.
You need to use the AutoScrollPosition property instead:
if (e.Button == MouseButtons.Left) {
var newPoint = new Point(e.X - this.AutoScrollPosition.X,
e.Y - this.AutoScrollPosition.Y);
// etc..
}
Note that substraction is required, a bit unintuitive.

Mouse Click Location On A PictureBox Not Detected Within Label

I have a Form that contains only 2 things, a PictureBox and a Label.
I added a mouse click event handler to the picture box.
this.pictureBox1.MouseClick += picture_MouseClick;
Inside the handler I need to check if the location of the mouse click is within the bounds of the label. To do this, I am using the mouse event location and checking to see whether that location is within the bounds of the label.
private void picture_MouseClick(object sender, MouseEventArgs e)
{
if (label1.Bounds.Contains(e.Location))
{
MessageBox.Show("FOUND YOU!");
}
}
I expected this to work as it seems easy enough however the click location (the orange box in the image) that leads to the MessageBox being shown is offset down and to the right of the label.
Is this because the mouse event is relative to the PictureBox and the Label bounds are relative to the Form? Or vice versa?
By the way, the label you see in the image is hidden at runtime. I am just using the label as a "hack" way of knowing if the user clicked in a certain spot.
public Form1()
{
InitializeComponent();
this.label1.Visible = false;
this.pictureBox1.MouseClick += picture_MouseClick;
}
(I tried subtracting the width of the label from e.X and the height of the label from e.Y but that didn't seem to work.)
Thank you,
Jan
The e.Location is the mouse position (a point) relative to the upper-left corner of the picturebox.
The Bounds property is relative to the container of the control.
(And in this case, the container is the form, as you and SLacks have rightly pointed out)
To check the correct position I will try with this code (now tested)
Point p = e.Location;
p.X += pictureBox1.Left;
p.Y += pictureBox1.Top;
if(label1.Bounds.Contains(p))
.....

Moving buttons in a Canvas

The following code is supposed to move UIElements in a canvas, when the mouse hovers over them, and the user hits Ctrl.
void keydown(Object sender, KeyEventArgs e)
{
if (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl)
{
control++;
if (control == 1)
{
drag = true;
elem = (UIElement)Mouse.DirectlyOver;
}
else
drag = false;
control %= 2;
}
}
void mousemove(object sender, MouseEventArgs e)
{
Point p = e.GetPosition(canvas);
if (drag)
{
if (elem == null) return;
//Canvas.SetLeft(myButton, p.X); <-- this works, but then why doesn't it work when I generalize it?
Canvas.SetLeft(elem, p.X);
Canvas.SetTop(elem, p.Y);
}
}
Any Shapes component, e.g. Rectangles will move when I hover the mouse over them, and hit control..But it doesn't work with Buttons, TextBoxes, TextViews, etc. Can anyone explain?
The documentation for Mouse.DirectlyOver says:
Controls can be composed of multiple elements. DirectlyOver reports the specific element in the composite control the mouse pointer is over and not the control itself. For example, depending on which part of a Button the pointer is over, the DirectlyOver property could report the TextBox of the Content property or the ButtonChrome.
In other words: A Button is made up of several sub-elements, like a ButtonChrome and a TextBlock (usually not a TextBox -- I think that's a typo on the MSDN page). When you call Mouse.DirectlyOver, you're probably getting one of those elements, rather than the Button.
Since those elements are not parented to a Canvas (they're parented to something in the Button's control template, most likely a Grid), setting the Canvas.Left and Canvas.Top attached properties will have no effect.
You probably want to walk up the visual tree (using VisualTreeHelper.GetParent) until you find something you're interested in dragging. How you determine whether you're interested in any given element is up to you. You could go until you find something that's parented to your Canvas, or you could just go until you find something that's one of a given set of types (stopping when you find something that descends from Control might be a decent place to start).
My guess is that those events are swallowed by the implementation of the controls that don't work.
I'm not in front of my computer now, but isn't there a previewmousemove event? Try using that?
Update: scratch that!
Just use e.Souce. The event source is designed to be the element your likely to be interested in. You'll need to cast to UIElement, but I'm pretty sure that'll work.

Categories