How easy it really is to implement drag&drop in silverlight? - silverlight

I am new to silverlight - I've seen the tutorials and all, what I am looking for are high-level steps required to implement drag&drop and maybe a bit of (pseudo)code just to get an idea.
Any help appreciated!

Could you explain exactly what you would like to achieve with drag and drop in Silverlight? The top answer in the question that you linked to links to this page:
http://www.adefwebserver.com/DotNetNukeHELP/Misc/Silverlight/DragAndDropTest/
This contains a sample project with source that implements drag and drop (coincidentally, based on a sample I made for beta 1 of Silverlight 2 :-) ). What about this code is not suitable for your needs?
EDIT:
The basic skeleton of a drag and drop implementation would look like:
bool isDragging = false;
void elementMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (element.CaptureMouse())
{
isDragging = true;
element.MouseMove += elementMouseMove;
//start drag code goes here
}
}
void elementMouseMove(object sender, MouseEventArgs e)
{
//Drag code goes here
}
void element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (isDragging)
{
element.ReleaseMouseCapture();
isDragging = false;
element.MouseMove -= elementMouseMove;
//Drop code goes here
}
}
Add the MouseLeftButtonUp/Down handlers to the element that you want to drag.
In the MouseMove event handler, add the code that you want to execute during the drag: e.g. change Canvas.Top and Canvas.Left to match the mouse position. You can get the mouse position from the event args. You would probably want to get the position relative to the elements container.
In the MouseLeftButtonUp event handler, add the code that would execute when the "drop" occurs. For example, you might want to implement a "recycle bin" that elements can be dragged to. In that case, you would want to know what elements are underneath the mouse at the point of the drop. You can use VisualTreeHelper.FindElementsAtHostCoordinates, passing the mouse position relative to the application's root (use e.GetPosition(null)). Then if your "recycle bin" element is returned by FindElementsInHostCoordinates you know to execute the appropriate action.
Does this help answer you question?

isn't MouseLeftButtonDown where you are supposed to be starting the capture, not on mouseleftbuttonup?

Related

WPF - Get current cell details

I have a grid which contains 2 rows and 2 columns. Each cell hosting a windows forms host control. I want to capture the selected cell when the user clicks on any of the cell. I have searched and found that there is no 'Click' event but I can make use of 'MouseDown' event with similar results. But now I am stuck because you would think there must be an easier way like 'GetCurentRow' and 'GetCurrentColumn' to capture the selected cell but there isn't.
What I want further to do is to get the child element in that particular cell of the grid.
I tried the following code but no matter which cell I click, I always get 0 for the row and column:
void InnerGridToContainWindowsFormsHost_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var element = (UIElement)e.Source;
int c = Grid.GetColumn(element);
int r = Grid.GetRow(element);
}
Is there any way to get the selected/clicked cell details or the children inside the cell?
I think that you may be going down the wrong route here... if you just want to know what control was clicked on, try using the VisualTreeHelper.HitTest method. You can find information on the VisualTreeHelper.HitTest Method page on MSDN. Basically, you would do something like:
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// Get the position of the mouse pointer relative to the grid
Point clickPoint = e.GetPosition(grid);
HitTestResult result = VisualTreeHelper.HitTest(grid, clickPoint);
if (result != null)
{
// Do something here
}
}
You may also need some kind of basic recursive function to drill down through the Visual objects until you get to the one you want.

Create my own Slider for multitouch application

The Slider control in WPF doesn't work properly for what I'm looking for.
I need to slide 2 different controls (Slider) at the same time (with one finger each).
When I touch the first Slider, it gets all the focus and I cannot touch anything else with my second touch device.
So I need to create my own Slider (MySlider) that inherit from Slider.
I've made 4 methods:
protected override void OnTouchDown(TouchEventArgs e)
protected override void OnTouchUp(TouchEventArgs e)
protected override void OnTouchLeave(TouchEventArgs e)
protected override void OnTouchMove(TouchEventArgs e)
But is there a way to move the Slider exactly like with the mouse? Or I need to calcule each time my touch device moved something like:
protected override void OnTouchMove(TouchEventArgs e)
{
base.OnTouchMove(e);
if (this.Value <= this.Maximum && this.Value >= this.Minimum)
{
Point newPoint = e.GetTouchPoint(this).Position;
this.Value += (this.lastPoint.Y - newPoint.Y);
lastPoint = newPoint;
}
e.Handled = true;
}
And in this case the movement doesn't move at the same speed as the finger...
You might want to check out the Surface 2.0 SDK as it contains a class called SurfaceSlider, which I believe will allow for two or more sliders to be updated simultaneously. This SDK can be used to target applications built for Windows 7.
I'm not familiar with multi-touch events in WPF so will not be able to help you with that. However, for moving the mouse to the same location as your touching then you can look at this answer here.
Your problem that you're assuming that the width of the control is equivalent to the maximum value. You need to take out the factor the actual width relative to the difference between the max and min values.
This can only be done via events since no routed event or DPs for mouse position.

SL4: need to register for a move (or redraw) event on an Item in an ItemsControl

Not finding a move event or redraw event in the FrameworkElement class. And Google not helping either. So...
I have a custom ItemsControl populated by an observable collection in the VM. The ItemsControl itself leverages the
<i:Interaction.Behaviors>
<ei:MouseDragElementBehavior ConstrainToParentBounds="True"/>
</i:Interaction.Behaviors>
behavior so the user can drag around the whole assembly.
When the user moves the assembly, I want to be notified by each item as the item is repositioned as a result of the assembly moving. So far I have tried registering for
this.myItem.LayoutUpdated += this.OnSomethingNeedsToUpdate;
but it doesn't seem to fire as I drag the assembly around.
Also
this.myItem.MouseMove += this.OnSomethingNeedsToUpdate;
only works if I mouse into the item which is not good enough. Because I am moving the ItemsControl and then have to go mouse into the item to get the event to fire.
Any ideas? Can I look to some ancestor in the visual tree for help in the form of a OneOfMyDecendantsWasRedrawn event or similar? Again I am trying to be notified when an item moves not be notified when the assembly moves.
I would say your best bet would be to add the MouseDragElementBehavior to your custom ItemsControl in code instead of in the Xaml. Here is how this might look (using a Grid since that is easier to demo):
public class DraggableGrid : Grid
{
public DraggableGrid()
{
Loaded += new RoutedEventHandler(DraggableGrid_Loaded);
}
void DraggableGrid_Loaded(object sender, RoutedEventArgs e)
{
MouseDragElementBehavior dragable = new MouseDragElementBehavior();
Interaction.GetBehaviors(this).Add(dragable);
dragable.Dragging += new MouseEventHandler(dragable_Dragging);
}
void dragable_Dragging(object sender, MouseEventArgs e)
{
// Custom Code Here
}
}
In the section that says Custom Code Here you would loop through you Items and notify them that they are being dragged.
I ended up writting another behavior for the individual items I care about and then wrote a LINQ query to search up the visual tree looking for ancestors with the MouseDragElementBehavior attached to them. That query found the ItemsControl since it was an eventual parent of the Item. I was then able to register for the Dragging event as desried.
Thanks again to Bryant for providing the solution over here.

getting postion of a control in mousemove

I have a silverlight control in which i am dynamically creating shapes on button click
These shapes are draggable across the form. Now i want to get top and left postion of a control on dragging (mousemove). Please help
Taking a look at your question history I can think of two approaches. I suspect that the shape is simply being placed on a Canvas and the MouseMove of which you speak refers to an event handler you've attached to the Canvas. On that basis then.
void Canvas_MouseMove(object sender, MouseEventArgs e)
{
Type currentType = e.OriginalSource.GetType();
// Make decisions based on value of currentType here
DependencyObject source = (DependencyObject)e.OriginalSource;
Point p = new Point(Canvas.GetLeft(source), Canvas.GetTop(source));
}
The more general solution is to use the TransformToVisual approach. Something like:-
var transform = ((UIElement)e.OriginalSource).TransformToVisual(MyCanvas);
Point p = transform.Transform(Point(0,0));

How to update the position of a drag adorner during WPF drag-n-drop?

I'm using an adorner to show a 'ghost' of the element being dragged...
var adornerLayer = AdornerLayer.GetAdornerLayer(topLevelGrid);
dragAdorner = new DragAdorner(topLevelGrid, itemToDrag);
adornerLayer.Add(dragAdorner);
dragAdorner.UpdatePosition(e.GetPosition(topLevelGrid));
DragDrop.DoDragDrop(sourceItems, viewModel, DragDropEffects.Move);
adornerLayer.Remove(dragAdorner);
itemToDrag = null;
...but I can't find a nice way to update the position of the adorner during the drag. The closest I've got is by setting AllowDrop="true" on the top level grid and giving it a DragOver handler...
private void TopLevelGrid_OnDragOver(object sender, DragEventArgs e)
{
dragAdorner.UpdatePosition(e.GetPosition(topLevelGrid));
}
But this means I don't get the proper DragDropEffects feedback on the cursor, i.e., it always shows the DragDropEffects.Move cursor instead of DragDropEffects.None until I'm over an actual drop target.
Does anyone know a better way to update the adorner position?
There's this (unfortunately only available as a cached version) pretty old blog post from Bea Stollnitz, which pretty much covers your question. It has a nice implementation of drag n drop with an adorner showing a "ghost image".
Basically drag and drop in WPF is quite the complicate procedure that - if you want some custom DragAdorners - involves adding a bunch of attached dependency properties for handling setup of all the events involved and especially for displaying the adorner in a way that doesn't interfere with the dropping code.
Bea's code works by having a helper class that sets the owning Window's DragOver event handler and AllowDrop right before the actual drag drop operation, that way you can control all the moving in between the actual drag source and the drop target.
So, looking closer at Bea's code that redoced was referring to...
I still set AllowDrop="true" on the top level grid and give it a DragOver handler where I can update the adorner position, but I also set the DragDropEffects to None here. Then I just need to add a DragOver handler to the actual drop target to also update the adorner position...and making sure to set e.Handled = true so that the top level grid's handler doesn't just set the effects back to None when I'm over a drop target...
private void TopLevelGrid_OnDragOver(object sender, DragEventArgs e)
{
UpdateDragAdornerPosition(e.GetPosition(topLevelGrid));
e.Effects = DragDropEffects.None;
e.Handled = true;
}
private void DropTarget_OnDragOver(object sender, DragEventArgs e)
{
UpdateDragAdornerPosition(e.GetPosition(topLevelGrid));
e.Handled = true;
}
I know this is an old question, but I ended up asking the same thing recently and then had to answer it myself. I used hooks via p/invoke to get at the native window messages before they were consumed by the drag and drop operation. This let me track the mouse even during drag and drop and without having to set AllowDrop where I didn't want it.
For the full answer (including most of the code I used) you can check out my question:
WPF - Track mouse during Drag & Drop while AllowDrop = False

Resources