I would like to create a visual effect when an item in a listbox is double clicked. So far I have drag and drop functionality where the item is visually attached to the mouse and can be moved to a drop target. From that functionality I am able to animate the item using the same logic of getting the item container, however I am not able to leave the items control. Is there any way to remove the item from the ListBox and visually animate it to another location? Basically the main list box is a hand of cards. When a card is double-clicked I want it to visually move from the hand listbox to a discard listbox. As of now, the logic for moving the item from one collection to another is no problem, however I would really like an animated visual representation of this event. Any ideas or references on how to do something like this would be appreciated.
Thanks,
Brandon
Further Details on what I have attempted:
There are some concepts that I do not yet have a strong grasp of yet, which has led me to run face first into this wall. I have a method that I pass in(some may be unnecessary) the ListBox as an ItemsControl, a FrameworkElement that is the listbox item, and the data object associated with the ListBox item. What I attempted to do was FindVisualChild of the ListBoxItem that is a canvas. I can do that. In my mind I was wanting to somehow clone the canvas either as a canvas or as a bitmap, add it to the children of the child of the page in the same location, remove the ListBoxItem from the ListBox, and animate the clone to the discard pile. When the animation completes the clone will be removed or hidden, and as that object is added to the discard pile collection it will effectively take the replace the clone.
My problem with this, is that I feel like there really is a simpler way of doing this using the adorner layer or something. I also, don't know how I would position the clone at the exact same position in the element further up the visual tree. I am going to keep working on it and researching other approaches, and I will just hope that someone will share some insight on this.
Here's some code I worked up to draw a visual to a bitmap. You may be able to adapt this to your needs, and draw the bitmap by adorning the UIElement that represents a common ancestor of the two list views. Note the use of FrameworkElement.TransformToAncestor to get the coordinates of a nested element in terms of an ancestor element.
public static BitmapSource CreateBitmapFromElement(FrameworkElement element, Double dpiX, Double dpiY)
{
Size elementSize = new Size(element.ActualWidth, element.ActualHeight);
Visual root = GetAdornerDecoratorAncestor(element);
Rect elementBounds = element.TransformToAncestor(root).TransformBounds(new Rect(elementSize));
RenderTargetBitmap rtb = new RenderTargetBitmap((Int32)(elementBounds.Size.Width * dpiX / 96.0),
(Int32)(elementBounds.Size.Height * dpiY / 96.0),
dpiX,
dpiY,
PixelFormats.Pbgra32);
DrawingVisual dv = new DrawingVisual();
using (DrawingContext dc = dv.RenderOpen())
{
VisualBrush vb = new VisualBrush(root);
vb.ViewboxUnits = BrushMappingMode.Absolute;
vb.Stretch = Stretch.None;
vb.Viewbox = elementBounds;
dc.DrawRectangle(vb, null, new Rect(new Point(), elementBounds.Size));
}
rtb.Render(dv);
return rtb;
}
public static Visual GetAdornerDecoratorAncestor(DependencyObject obj)
{
while(obj != null && !(obj is AdornerDecorator))
{
obj = VisualTreeHelper.GetParent(obj);
}
return obj as AdornerDecorator;
}
OK, you could try taking a Visual element and setting it's background to a visualbrush of your ListItem and animate that to the other list box. You can wait for the storyboard completed event to actually do the switch. If this were me, I would animate from one box only to the edge of the other. If the switch happens fast enough it should look pretty seamless to the user. Finding the exact position of where the item is supposed to go into the list box would be pretty complex based on any sorting/filtering rules you have.
If the two listboxes are always in the same position, you could try animating the double-clicked item to a predetermined spot, let's say half-way between the old list and the new list. Then execute the code to move the item to the new list, but use a style that immediately starts an animation on that item starting it from that predetermined location and animating to its location in the new list. You'd probably have to tweak the initial offset of the new item at runtime based on where it's inserted in the list.
Related
I have a simple Silverlight (v5) Grid with a fixed number of rows and columns. On this grid, I'm positioning several UserControls (called myControl in this example) at specific "grid coordinates" e.g. row 2, column 1.
Occasionally, I need to move a myControl to a new grid position; I currently do this by
Grid.SetColumn(myControl, newColumn);
Grid.SetRow(myControl, newRow);
.. and this all works fine, myControl is moved to the new coordinates.
Just purely for eye candy, I'd like to animate myControl when it moves, so that it "slides" from the old grid position to the new one rather than just appearing. Is there an easy/quick way to do this?
For handling layout changes within an element to display a smooth transition FluidMoveBehavior is generally the option you would be looking for. While it won't animate things like size or visibility, it is however specifically for animating the offset of a child element moved around a parent container.
Glad you found your answer. Cheers
I'm trying to determine the physical coordinates (specifically I'm interested in the TOP and BOTTOM) of the container which represents the ListViewItem.
I have a reference to the ListViewItem via:
ListViewItem item = ((ListView)AdornedElement).ItemContainerGenerator.ContainerFromIndex(idx) as ListViewItem;
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(item);
I now need to take the item and figure out what it's top and bottom values are relative to the entire listview. This is for showing a dragline indicator during a drag drop.
Can anyone offer any insight?
Thanks
For someone who this may help in the future. I figured this out (literally right after I posted)
Point pointTransformToVisual = lvItem.TransformToVisual((Visual)theListView.Parent).Transform(new Point());
Rect boundsRect = VisualTreeHelper.GetDescendantBounds(lvItem);
boundsRect.Offset(transform.X, transform.Y);
In my apps I use CircularProgressBar from http://sachabarber.net/?p=639.
In one app I place it on AdornerLayer I get from Image element and it works as expected (i.e. the element is animated by rotation driven by a timer).
In another app I place it on AdornerLayer I get from ListBox element and it's shown but not animated. It does get animated while I resize the window which I think means that for some reason AdornerLayer is not being redrawn when the element changes (unless I force a refresh e.g. by resizing a window).
It is also properly animated when I hold Alt key (I have no theory to explain this).
In both cases I use this code to create an adorner:
var adornerLayer = AdornerLayer.GetAdornerLayer(ListBoxDirOrImageElement);
if (null == adornerLayer)
return;
var vb = new Viewbox();
vb.Width = 16;
vb.Height = 16;
vb.Child = new CircularProgressBar();
var adorner = new UpperRightAdorner(ListBoxDir, vb);
adornerLayer.Add(adorner);
The only difference I've noticed so far between the 2 programs is the element I'm adorning (works on Image, doesn't work on ListBox).
Any thoughts why that might be and how to fix that or some other explanation why adorner on ListBox is not being repainted/animated?
I have stack panel with custom controls in it. I attach standard MouseDragElementBehavior to each item.
When I drag to the right the item moves underneath the other items.
What would be a viable solution to create better user experience - to show better visual cue - how the item moves and where is it going to be dropped.
After a bit of tinkering I realised that nothing can be dragged within stack panel to the right not being coverd by other elements .. unless you drag the very right item..
What I did to resolve it:
Created a visual cue (half transparent shape of a generic item to represnt it during the drag operation)
Made the cue invisible (width=0) and keep it always as the very last element of the stack panel children
Subscribed the stack panel to mouse left button up, down, move
Emulated drag/drop with code
Once the drag initated I turn the cue to visible and set its translate transform to the current mouse coordinates
Adjust translate transform on every mouse move event
On drop I hide the cue again and rearrange items in a way I want.
To stress again - whatever way you do - you have to manipulate with the last element in StackPanel.Children collection....
If the MouseDragElementBehavior doesn't work the way you need it to, you could always descend from the class and customize it to your needs. For example:
public class DragBehvaior : MouseDragElementBehavior
{
public DragBehvaior()
{
this.DragBegun += new MouseEventHandler(DragBehvaior_DragBegun);
}
void DragBehvaior_DragBegun(object sender, MouseEventArgs e)
{
var element = this.AssociatedObject as UIElement;
// Calculate the correct ZIndex here.
Canvas.SetZIndex(element, 100);
}
}
I'm rendering a WPF grid with multiple elements (buttons, textbox, ...) to a bitmap which is then used as a texture for a 3D surface in a Direct3D scene. For user interaction I create a 3D ray from the 2D mouse cursor position into the 3D scene finding the intersection point with the gui surface. So I know where the user has clicked on the WPF grid, but from there I'm stuck:
How can I simulate mouse events on the WPF elements while they are not actually shown in an open window but rendered off-screen?
Recently, I was looking into UIAutomation and RaiseEvent but these are used to send events to individual elements, not the whole visual tree. Traversing the tree manually and looking for elements at the cursor position would be an option but I haven't found a way to do this accurately. VisualTreeHelper.HitTest is a good start but instead of finding TextBox it finds TextBoxView and instead of ListBox it finds a Border.
EDIT: returning HitTestResultBehavior.Continue in the result callback of HitTest lets me walk through all elements at a given point. I can now send mouse events to all these elements but the values of the MouseEventArgs object are those of the real mouse. So I have to create a custom MouseDevice which apparently is impossible.
PointHitTestParameters p = new PointHitTestParameters(new Point(
((Vector2)hit).X * sourceElement.ActualWidth,
(1 - ((Vector2)hit).Y) * sourceElement.ActualHeight));
VisualTreeHelper.HitTest(sourceElement,
new HitTestFilterCallback(delegate(DependencyObject o)
{
return HitTestFilterBehavior.Continue;
}),
new HitTestResultCallback(delegate(HitTestResult r)
{
UIElement el = r.VisualHit as UIElement;
if (el != null)
{
MouseButtonEventArgs e = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left);
if (leftMouseDown) e.RoutedEvent = Mouse.MouseDownEvent;
else e.RoutedEvent = Mouse.MouseUpEvent;
el.RaiseEvent(e);
}
return HitTestResultBehavior.Continue;
}), p);
You might be able to send windows messages (like WM_MOUSEMOVE) to the WPF window's HWND, via the PostMessage(..) win32 method. I believe these messages would be read by WPF and executed as if it came from a user.
If you are feeling really brave, you can try my hack to get access to the WPF IDirect3DDevice9Ex. From there you can copy the backbuffer in the swapshchain to your d3d scene. If you are using 9Ex (Vista D3D9), you can take advantage of the shared resources (share surfaces/textures/etc between devices and processes) feature to help with performance.
You may still have to do some trickery with windows messages for interactivity.