Drag and drop an image in WPF - c#

I'm trying to drag and drop an image from one spot on the canvas to another (should be relatively simple), but can't figure it out. The image which I want to move has the following XAML:
<Image Height="28" HorizontalAlignment="Left" Margin="842,332,0,0" Name="cityImage" Stretch="Fill" VerticalAlignment="Top" Width="42" Source="/Settlers;component/Images/city.png" MouseLeftButtonDown="cityImage_MouseLeftButtonDown" MouseMove="cityImage_MouseMove" MouseLeftButtonUp="cityImage_MouseLeftButtonUp"/>
The code is as follows:
bool isDragging = false; Point initMousePos; private void cityImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
isDragging = true;
initMousePos = e.GetPosition(theGrid); } private void cityImage_MouseMove(object sender, MouseEventArgs e) {
if (isDragging)
{
Image image = sender as Image;
Canvas.SetTop(image, initMousePos.X);
Canvas.SetLeft(image, initMousePos.Y);
image.Visibility = System.Windows.Visibility.Visible;
} }
private void cityImage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
isDragging = false; }

What I do to accomplish what you want is to use
System.Windows.Controls.Primitives.Thumb
as the Root of a UserControl and set the ControlTemplate to display an image (within a border but it should work without as well), something like:
<Thumb Name="myRoot" DragDelta="MyRootDragDelta">
<Thumb.Template>
<ControlTemplate>
<Image ... >
... see below ...
</Image>
</ControlTemplate>
</Thumb.Template>
</Thumb>
Also, I bind the Source of the Image to a property of the class:
<Image.Source>
<Binding Path="ImageSource" RelativeSource=
{RelativeSource FindAncestor,
AncestorType=my:MyImageControl, AncestorLevel=1}" />
</Image.Source>
The UserControl has a named TranslateTransform (let's say translateTransform) whose properties X and Y are to be set in the DragDelta event handler:
private void MyRootDragDelta(object sender, DragDeltaEventArgs e)
{
translateTransform.X += e.HorizontalChange;
translateTransform.Y += e.VerticalChange;
}
Don't forget to add:
public ImageSource ImageSource { get; set; }
Hope this helps. If anything's unclear feel free to ask further.

You want to set the Left and Top properties of the Canvas to something other than the initial position. In the MouseMove handler you have to get the position relative to the parent. Also; make sure the parent element is a canvas and not a grid. You have a pretty big left and top margin on the image, aswell as a control with the variable name "theGrid".

Related

Let image ManipulationMode capture pointer

In my app, a user can select an Image and drag it onto a Grid, to play with it. I do this by handling the PointerEntered event of the Grid. Here I detect if the user had an image selected and if the user is holding the mouse button.
Now I want to place the Image on the grid, and pass on the (still pressed down) pointer to my Image, so the Image uses its own ManipulationStarted, ManipulationDelta and ManipulationCompleted events. This should let the user drag the image in one smooth movement from the list of images to the Grid, instead of having to release and click on the element.
I have tried releasing the pointer from the sender in PointerEntered, and capturing it using CapturePointer, but that doesn't seem to work, even though the CapturePointer returns true.
Here is the code I use for the PointerEntered event:
private void DrawingArea_OnPointerEntered(object sender, PointerRoutedEventArgs e)
{
// If we enter the grid while dragging and we have an image that was dragged
if (e.Pointer.IsInContact && CurrentDraggedImage != null)
{
DrawingArea.Children.Add(CurrentDraggedImage);
// Move it to the location we're currently at
var transform = (CurrentDraggedImage.RenderTransform as CompositeTransform);
transform.TranslateX += e.GetCurrentPoint(DrawingArea).RawPosition.X - DrawingArea.ActualWidth / 2;
transform.TranslateY += e.GetCurrentPoint(DrawingArea).RawPosition.Y - DrawingArea.ActualHeight/2;
// This works (I think)
(sender as UIElement).ReleasePointerCaptures();
// This doesn't work (or it isn't what I need), but returns true
CurrentDraggedImage.CapturePointer(e.Pointer);
// Get ready for a new image
CurrentDraggedImage = null;
}
}
My manipulation code is in this answer:
https://stackoverflow.com/a/32230733/1009013
Why don't you just use drag-n-drop? Create a grid containing your toolbar (e.g. a list of images to drag) and a target grid that responds to dragdrop commands:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListBox Background="AliceBlue" MouseMove="OnMouseMove">
<ListBox.Resources>
<Style TargetType="{x:Type Image}">
<Setter Property="Width" Value="64" />
<Setter Property="Height" Value="64" />
</Style>
</ListBox.Resources>
<Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_pawn_T.png" />
<Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_rook_T.png" />
<Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_knight_T.png" />
<Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_bishop_T.png" />
<Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_queen_T.png" />
<Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_king_T.png" />
<Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_pawn_T.png" />
<Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_rook_T.png" />
<Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_knight_T.png" />
<Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_bishop_T.png" />
<Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_queen_T.png" />
<Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_king_T.png" />
</ListBox>
<GridSplitter Grid.Column="1" Width="5" Background="LightGray" />
<Grid x:Name="targetGrid" Grid.Column="2" AllowDrop="True" DragEnter="OnDragEnter" DragOver="OnDragMove" DragLeave="OnDragLeave" Drop="OnDrop" Background="Transparent"/>
</Grid>
Your listbox needs a MouseMove handler to detect when an image is being dragged and your command handlers simply respond to the various events as required, cloning the require image and dragging them across the face of the grid accordingly:
public partial class MainWindow : Window
{
private Image DragImage = null;
public MainWindow()
{
InitializeComponent();
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
// make sure we have an image
var image = e.OriginalSource as Image;
if (image == null)
return;
// make sure we've started dragging
if (e.LeftButton != MouseButtonState.Pressed)
return;
DragDrop.DoDragDrop(image, image, DragDropEffects.Copy);
}
private void OnDragEnter(object sender, DragEventArgs e)
{
// make sure we have an image
if (!e.Data.GetDataPresent(typeof(Image)))
{
e.Effects = DragDropEffects.None;
return;
}
// clone the image
var image = e.Data.GetData(typeof(Image)) as Image;
e.Effects = DragDropEffects.Copy;
this.DragImage = new Image { Source = image.Source, Width=64, Height=64 };
var position = e.GetPosition(this.targetGrid);
this.DragImage.SetValue(Grid.MarginProperty, new Thickness(position.X-32, position.Y-32, 0, 0));
this.DragImage.SetValue(Grid.HorizontalAlignmentProperty, HorizontalAlignment.Left);
this.DragImage.SetValue(Grid.VerticalAlignmentProperty, VerticalAlignment.Top);
this.DragImage.IsHitTestVisible = false; // so we don't try and drop it on itself
// add it to the target grid
targetGrid.Children.Add(this.DragImage);
}
private void OnDragMove(object sender, DragEventArgs e)
{
var position = e.GetPosition(this.targetGrid);
this.DragImage.SetValue(Grid.MarginProperty, new Thickness(position.X - 32, position.Y - 32, 0, 0));
}
private void OnDragLeave(object sender, DragEventArgs e)
{
targetGrid.Children.Remove(this.DragImage);
this.DragImage = null;
}
private void OnDrop(object sender, DragEventArgs e)
{
this.DragImage.IsHitTestVisible = true;
this.DragImage = null;
}
}
Result:
I've done things the horrible and ugly WPF way here instead of clean and elegant MVVM, but you get the idea. I also don't get why you want to drag things around a Grid instead of a Canvas?

How to map tapped event for a single object in a canvas control in XAML?

I am working on a windows phone RT XAML c# project. I have a canvas filled with several dynamically added balls(ellipse) as ContentControl. I want that, when a ball is tapped, only that
particular ball should animate. I have used storyboard animation in the animating function animateBall() and the animation function works fine. But, I am not able to map the tapped
event of a single contentcontrol to the animating function animateBall().
I used foreach loop, but it is applying the animation to all the balls.
XAML:
<Canvas x:Name="playArea" HorizontalAlignment="Left" Height="624" Margin="10,10,0,0" VerticalAlignment="Top" Width="430">
<ContentControl x:Name="ball" Canvas.Left="167" Canvas.Top="482" Height="105" Tapped="ball_Tapped">
<Ellipse x:Name="ellipse" Fill="#FFE81B1B" Height="100" Stroke="Black" Width="100"/>
</ContentControl>
</Canvas>
c#
bool tapped;
foreach (UIElement ball in playArea.Children)
{
ball.Tapped += ball_Tapped;
double top= Canvas.GetTop(ball);
if (tapped)
{
animateBall(ball, top, playArea.ActualHeight, "(Canvas.Top)");
}
}
void ball_Tapped(object sender, TappedRoutedEventArgs e)
{
tapped = true;
}
I think you should try something like this:
foreach (UIElement ball in playArea.Children)
{
if(ball is ContentControl)
ball.Tapped += ball_Tapped;
}
}
void ball_Tapped(object sender, TappedRoutedEventArgs e)
{
ContentControl ball = sender as ContentControl;
double top = Canvas.GetTop(ball);
animateBall(ball, top, playArea.ActualHeight, "(Canvas.Top)");
}

Drag and drop Elements on a Canvas

This may be a very basic question. I have a canvas, which I put different Elements on in runtime (TextBoxes, Shapes, Buttons). Now I want to be able to drag and drop those elements to another location on the same canvas.
Can anyone provide me with some code, how to implement something like onDrag in runtime?
You can use for example the MouseDown, MouseMove and MouseUp events and then the MouseEventArgs's GetPosition(element) that returns you the coordinates relative to element (there are more events that expose this method).
Also, take advantage of the RoutedEvent's OriginalSource to check which element inside the canvas was clicked (in this case it's only the rectangle).
Here's an example:
<Grid>
<Canvas Name="MyCanvas" PreviewMouseLeftButtonDown="OnMouseDown" MouseMove="OnMouseMove">
<Rectangle Width="20" Height="20" Canvas.Left="10" Canvas.Top="10" Fill="Blue"/>
<Rectangle Width="20" Height="20" Canvas.Left="50" Canvas.Top="10" Fill="Red"/>
</Canvas>
</Grid>
Code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AddHandler(Mouse.MouseUpEvent, new MouseButtonEventHandler(OnMouseUp), true);
}
private bool _isBeingDragged;
public void OnMouseDown(object sender, MouseEventArgs args)
{
if (!(args.OriginalSource is Canvas))
{
_isBeingDragged = true;
}
}
public void OnMouseMove(object sender, MouseEventArgs args)
{
if (_isBeingDragged)
{
var elementBeingDragged = (FrameworkElement) args.OriginalSource;
var position = args.GetPosition(MyCanvas);
Canvas.SetLeft(elementBeingDragged, position.X - elementBeingDragged.ActualWidth / 2);
Canvas.SetTop(elementBeingDragged, position.Y - elementBeingDragged.ActualHeight / 2);
}
}
public void OnMouseUp(object sender, MouseEventArgs args)
{
_isBeingDragged = false;
}
}
I had to use the UIElement's AddHandler method to register MouseUp because somehow it has being marked as Handled (the AddHandler method allows you to register an event handler for events that have been handled previously, i.e. e.Handled = true)

How to detect if the scroll viewer reaches bottom in winrt

I'm wondering what's the best approach to detect if a ScrollViewer reaches the bottom, right etc.
I think I can achieve that by using both PointerWheelChanged for mouse and ManipulationDelta for touch. In these event handlers, I can record the HorizontalOffset to find out when will the scroller reach the end. But I think there could be a better way to do it.
I've found this article. But the compression visual states seem not working in winrt. The CurrentStateChanging event method is not getting called.
I also checked another article. But it just works for scroll bar, not a generic approach.
Anyone knows what's the best way to solve this problem?
XAML:
<ScrollViewer
x:Name="sv"
ViewChanged="OnScrollViewerViewChanged">
<Rectangle
x:Name="rect"
Width="2000"
Height="2000"
Fill="Yellow"
Margin="10" />
</ScrollViewer>
Code behind:
private void OnScrollViewerViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
var verticalOffset = sv.VerticalOffset;
var maxVerticalOffset = sv.ScrollableHeight; //sv.ExtentHeight - sv.ViewportHeight;
if (maxVerticalOffset < 0 ||
verticalOffset == maxVerticalOffset)
{
// Scrolled to bottom
rect.Fill = new SolidColorBrush(Colors.Red);
}
else
{
// Not scrolled to bottom
rect.Fill = new SolidColorBrush(Colors.Yellow);
}
}
For UWP I got it like this
<ScrollViewer Name="scroll" ViewChanged="scroll_ViewChanged">
<ListView />
</ScrollViewer>
private void scroll_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
var scrollViewer = (ScrollViewer)sender;
if (scrollViewer.VerticalOffset == scrollViewer.ScrollableHeight)
btnNewUpdates.Visibility = Visibility.Visible;
}
private void btnNewUpdates_Click(object sender, RoutedEventArgs e)
{
itemGridView.ScrollIntoView(itemGridView.Items[0]);
btnNewUpdates.Visibility = Visibility.Collapsed;
}

Changing an image location in WPF

I am trying to have the location of an image change when I mouseover. I have:
<Image
Name="cat"
Source="CatRun.bmp"
Visibility="Hidden"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="100"
Height="100"
UIElement.MouseEnter="cat_MouseEnter"/>
in XAML and:
private void cat_MouseEnter(object sender, MouseEventArgs e)
{
}
in C#.
How can I set the location specifically on the canvas?
Here's an example:
<Canvas x:Name="canvas">
<Rectangle x:Name="rect" Width="20" Height="20" Canvas.Left="10" Canvas.Top="10" Fill="Blue" MouseEnter="RectangleMouseEnter" />
</Canvas>
You need to set the attached properties top, left (or bottom, right)
private void RectangleMouseEnter(object sender, MouseEventArgs e)
{
Canvas.SetTop(rect, 50);
Canvas.SetLeft(rect, 50);
}
In order to set position of the Image on the Canvas from code-behind you can use something like:
private void cat_MouseEnter(object sender, MouseEventArgs e)
{
Canvas.SetLeft(cat, 100); //set x coordinate of cat Image to 100
Canvas.SetTop(cat, 300); //set y coordinate of cat Image to 300
}
Update:
In some cases you may not be able to access cat object by name from that method. In order to make it work, just use the sender object that should be the Image that caused the event, as H.B. described in his comment.

Categories

Resources