|
using ClearStyle.ViewModel;
using System;
using System.Diagnostics;
using Windows.UI.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using System.Linq;
using ClearStyle;
using LinqToVisualTree;
using Windows.UI.Xaml.Controls;
namespace ClearStyle.Interactions
{
/// <summary>
/// Adds the ability to be able to drag items within the list
/// </summary>
public class DragReOrderInteraction : InteractionBase
{
private static readonly int AutoScrollHitRegionSize = 80;
private DispatcherTimer _autoScrollTimer;
private FrameworkElement _dragItem;
private int _initialDragIndex;
// private SoundEffect _moveSound;
public DragReOrderInteraction()
{
// a timer which is used to periodically detect the position of the
// item being dragged in order to allow auto-scroll behaviour
_autoScrollTimer = new DispatcherTimer();
_autoScrollTimer.Interval = TimeSpan.FromMilliseconds(50);
_autoScrollTimer.Tick += (s, e) =>
{
AutoScrollList();
ShuffleItemsOnDrag();
};
// _moveSound = SoundEffect.FromStream(TitleContainer.OpenStream("Sounds/Windows XP Menu Command.wav"));
}
public override void AddElement(FrameworkElement manipulationElement, FrameworkElement transformElement)
{
manipulationElement.ManipulationStarted += Element_ManipulationStarted;
manipulationElement.ManipulationDelta += Element_ManipulationDelta;
manipulationElement.ManipulationCompleted += Element_ManipulationCompleted;
}
private void Element_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
{
if (IsEnabled == false)
return;
if (Math.Abs(e.Cumulative.Translation.Y) > Math.Abs(e.Cumulative.Translation.X))
{
IsActive = true;
// locate the element being dragged
_dragItem = (sender as FrameworkElement).Ancestors<Border>().OfType<Border>().First();
_dragItem.SetVerticalOffset(0);
ToDoItem draggedToDoItem = ((ToDoItem)_dragItem.DataContext);
_initialDragIndex = _todoItems.IndexOf(draggedToDoItem);
// fade out the items in the list, other than the dragged one
foreach (var item in _todoList.GetItemsInView()
.Where(i => i.DataContext != draggedToDoItem))
{
item.Animate(1.0, 0.7, "Opacity", 300, 0);
}
_autoScrollTimer.Start();
}
}
private void Element_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
{
if (!IsActive)
return;
IsActive = false;
_autoScrollTimer.Stop();
int dragIndex = GetDragIndex();
// fade in the list
_todoList.Animate(null, 1.0, "Opacity", 200, 0);
// animated the dragged item into location
double targetLocation = dragIndex * _dragItem.ActualHeight - _initialDragIndex * _dragItem.Height;
var trans = _dragItem.GetVerticalOffset().Transform;
trans.Animate(null, targetLocation, "Y", 200, 0, null,
() =>
{
// move the dragged item
var draggedItem = _todoItems[_initialDragIndex];
_todoItems.Remove(draggedItem);
_todoItems.Insert(dragIndex, draggedItem);
// re-populate our ObservableCollection
RefreshView();
// fade out the dragged image and collapse on completion
_dragItem.Animate(null, 0.0, "Opacity", 1000, 0, null, ()
=> _dragItem.Visibility = Visibility.Collapsed);
});
}
private void Element_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
Debug.WriteLine("ManipulationDelta : " + e.Cumulative.Translation.Y.ToString());
if (!IsActive)
return;
// set the event to handled in order to avoid scrolling the ScrollViewer
e.Handled = true;
// move our 'drag image'.
_dragItem.SetVerticalOffset(_dragItem.GetVerticalOffset().Value + e.Delta.Translation.Y);
}
// Determines the index that the dragged item would occupy when dropped
private int GetDragIndex()
{
double dragLocation = _dragItem.GetRelativePosition(_todoList).Y +
ScrollViewer.VerticalOffset +
_dragItem.ActualHeight / 2;
int dragIndex = (int)(dragLocation / _dragItem.ActualHeight);
dragIndex = Math.Min(_todoItems.Count - 1, dragIndex);
return dragIndex;
}
private void ShuffleItemsOnDrag()
{
// find its current index
int dragIndex = GetDragIndex();
// iterate over the items in the list and offset as required
double offset = _dragItem.ActualHeight;
for (int i = 0; i < _todoItems.Count; i++)
{
FrameworkElement item = _todoList.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
// determine which direction to offset this item by
if (i <= dragIndex && i > _initialDragIndex)
{
OffsetItem(-offset, item);
}
else if (i >= dragIndex && i < _initialDragIndex)
{
OffsetItem(offset, item);
}
else
{
OffsetItem(0, item);
}
}
}
private void OffsetItem(double offset, FrameworkElement item)
{
double targetLocation = item.Tag != null ? (double)item.Tag : 0;
if (targetLocation != offset)
{
var trans = item.GetVerticalOffset().Transform;
trans.Animate(null, offset, "Y", 500, 0);
item.Tag = offset;
// _moveSound.Play();
}
}
// checks the current location of the item being dragged, and scrolls if it is
// close to the top or the bottom
private void AutoScrollList()
{
// where is the dragged item relative to the list bounds?
double draglocation = _dragItem.GetRelativePosition(_todoList).Y + _dragItem.ActualHeight / 2;
if (draglocation < AutoScrollHitRegionSize)
{
// if close to the top, scroll up
double velocity = (AutoScrollHitRegionSize - draglocation);
ScrollViewer.ScrollToVerticalOffset(ScrollViewer.VerticalOffset - velocity);
}
else if (draglocation > _todoList.ActualHeight - AutoScrollHitRegionSize)
{
// if close to the bottom, scroll down
double velocity = (AutoScrollHitRegionSize - (_todoList.ActualHeight - draglocation));
ScrollViewer.ScrollToVerticalOffset(ScrollViewer.VerticalOffset + velocity);
}
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.