// TODO: changed //protected void OnIsReorderEnabledChanged(DependencyPropertyChangedEventArgs e) //{ // string visualState = (bool)e.NewValue ? // ReorderListBoxItem.ReorderEnabledState : ReorderListBoxItem.ReorderDisabledState; // VisualStateManager.GoToState(this, visualState, true); //} private static void OnIsReorderEnabledChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { if (o is ReorderListBoxItem) { ReorderListBoxItem oLBI = o as ReorderListBoxItem; string visualState = (bool)e.NewValue ? ReorderListBoxItem.ReorderEnabledState : ReorderListBoxItem.ReorderDisabledState; VisualStateManager.GoToState(oLBI, visualState, true); } }
/// <summary> /// Updates the drop-indicator height value for visual state and transition animations. /// </summary> /// <remarks> /// This is a workaround for the inability of visual states and transitions to do template binding /// in Silverlight 3. In SL4, they could bind directly to the DropIndicatorHeight property instead. /// </remarks> protected void OnDropIndicatorHeightChanged(DependencyPropertyChangedEventArgs e) { Panel rootPanel = (Panel)VisualTreeHelper.GetChild(this, 0); VisualStateGroup vsg = ReorderListBoxItem.GetVisualStateGroup( rootPanel, ReorderListBoxItem.DropIndicatorStateGroup); if (vsg != null) { foreach (VisualState vs in vsg.States) { foreach (Timeline animation in vs.Storyboard.Children) { this.UpdateDropIndicatorAnimationHeight((double)e.NewValue, animation); } } foreach (VisualTransition vt in vsg.Transitions) { foreach (Timeline animation in vt.Storyboard.Children) { this.UpdateDropIndicatorAnimationHeight((double)e.NewValue, animation); } } } }
/// <summary> /// Updates the targeted index -- that is the index where the item will be moved to if dropped at this point. /// </summary> private void UpdateDropTargetIndex(ReorderListBoxItem targetItemContainer, bool after) { int dragItemIndex = this.Items.IndexOf(this.dragItem); int targetItemIndex = this.Items.IndexOf(targetItemContainer.Content); int newDropTargetIndex; if (targetItemIndex == dragItemIndex) { newDropTargetIndex = dragItemIndex; } else { newDropTargetIndex = targetItemIndex + (after ? 1 : 0) - (targetItemIndex >= dragItemIndex ? 1 : 0); } if (newDropTargetIndex != this.dropTargetIndex) { this.dropTargetIndex = newDropTargetIndex; } }
/// <summary> /// Slides the drag indicator (item snapshot) to the location of the dropped item, /// then performs the visibility swap and removes the dragging visual state. /// </summary> private void AnimateDrop(ReorderListBoxItem itemContainer) { GeneralTransform itemTransform = itemContainer.TransformToVisual(this.dragInterceptor); Rect itemRect = itemTransform.TransformBounds(new Rect(new Point(0, 0), itemContainer.RenderSize)); double delta = Math.Abs(itemRect.Y - Canvas.GetTop(this.dragIndicator) - ((TranslateTransform)this.dragIndicator.RenderTransform).Y); // Added the && case because the itemContainer was being null'd somewhere in the code before this finished // TODO: find the place where itemContainer is being set to null if (delta > 0)// && itemContainer != null) { // TODO: reenable the time scaling // Adjust the duration based on the distance, so the speed will be constant. //TimeSpan duration = TimeSpan.FromSeconds(0.25 * delta / itemRect.Height); TimeSpan duration = TimeSpan.FromMilliseconds(250); Storyboard dropStoryboard = new Storyboard(); DoubleAnimation moveToDropAnimation = new DoubleAnimation(); Storyboard.SetTarget(moveToDropAnimation, this.dragIndicator.RenderTransform); // TODO: changed // Storyboard.SetTargetProperty(moveToDropAnimation, new PropertyPath(TranslateTransform.YProperty)); Storyboard.SetTargetProperty(moveToDropAnimation, TranslateTransform.YProperty.ToString()); moveToDropAnimation.To = itemRect.Y - Canvas.GetTop(this.dragIndicator); moveToDropAnimation.Duration = duration; dropStoryboard.Children.Add(moveToDropAnimation); // TODO: put at the end of the storyboard again //dropStoryboard.Completed += delegate //{ this.dragItem = null; itemContainer.Opacity = 1; this.dragIndicator.Visibility = Visibility.Collapsed; this.dragIndicator.Source = null; ((TranslateTransform)this.dragIndicator.RenderTransform).Y = 0; VisualStateManager.GoToState(itemContainer, ReorderListBoxItem.NotDraggingState, true); //}; // TODO: renable late //dropStoryboard.Begin(); } else { // There was no need for an animation, so do the visibility swap right now. this.dragItem = null; itemContainer.Opacity = 1; this.dragIndicator.Visibility = Visibility.Collapsed; this.dragIndicator.Source = null; VisualStateManager.GoToState(itemContainer, ReorderListBoxItem.NotDraggingState, true); } // reset ability to call RenderAsync drawn = false; }
/// <summary> /// Called when the user releases a drag. Moves the item within the source list and then resets everything. /// </summary> private void dragInterceptor_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e) { if (this.dragItem == null) { return; } if (this.dropTargetIndex >= 0) { this.MoveItem(this.dragItem, this.dropTargetIndex); } if (this.dragItemContainer != null) { this.dragItemContainer.Visibility = Visibility.Visible; this.dragItemContainer.Opacity = 0; this.AnimateDrop(this.dragItemContainer); this.dragItemContainer = null; } this.dragScrollDelta = 0; this.dropTargetIndex = -1; this.ClearDropTarget(); }
/// <summary> /// Called when the user presses down on the transparent drag-interceptor. Identifies the targed /// drag handle and list item and prepares for a drag operation. /// </summary> private void dragInterceptor_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) { if (this.dragItem != null) { return; } // Access the Panel containing all of the draggable ReorderListBoxItems if (this.itemsPanel == null) { ItemsPresenter scrollItemsPresenter = (ItemsPresenter)this.scrollViewer.Content; this.itemsPanel = Utilities.FindChild<StackPanel>(Window.Current.Content, ItemsPanelPart); //this.itemsPanel = (Panel)VisualTreeHelper.GetChild(scrollItemsPresenter, 0); } // Figure out the Point where the user put their finger down GeneralTransform interceptorTransform = this.dragInterceptor.TransformToVisual(Window.Current.Content); // TODO: replace this line - // Point targetPoint = interceptorTransform.Transform(e.ManipulationOrigin); Point targetPoint = interceptorTransform.TransformPoint(e.Position); targetPoint = ReorderListBox.GetHostCoordinates(targetPoint); // Get reference to all items at the Point where the finger is down List<UIElement> targetElements = VisualTreeHelper.FindElementsInHostCoordinates( targetPoint, this.itemsPanel).ToList(); // Get the first eligible ReorderListBoxItem ReorderListBoxItem targetItemContainer = targetElements.OfType<ReorderListBoxItem>().FirstOrDefault(); if (targetItemContainer != null && targetElements.Contains(targetItemContainer.DragHandle)) { // Transition to the VisualState for dragging (default is light grey underlay with lower opacity) VisualStateManager.GoToState(targetItemContainer, ReorderListBoxItem.DraggingState, true); // Position and resize the proxy image "dragIndicator" GeneralTransform targetItemTransform = targetItemContainer.TransformToVisual(this.dragInterceptor); Point targetItemOrigin = targetItemTransform.TransformPoint(new Point(0, 0)); Canvas.SetLeft(this.dragIndicator, targetItemOrigin.X); Canvas.SetTop(this.dragIndicator, targetItemOrigin.Y); this.dragIndicator.Width = targetItemContainer.RenderSize.Width; this.dragIndicator.Height = targetItemContainer.RenderSize.Height; // Store references to the object being dragged this.dragItemContainer = targetItemContainer; this.dragItem = this.dragItemContainer.Content; this.isDragItemSelected = this.dragItemContainer.IsSelected; this.dragInterceptorRect = interceptorTransform.TransformBounds( new Rect(new Point(0, 0), this.dragInterceptor.RenderSize)); this.dropTargetIndex = -1; } }
/// <summary> /// Ensures that a possibly-recycled item container (ReorderListBoxItem) is ready to display a list item. /// </summary> protected override async void PrepareContainerForItemOverride(DependencyObject element, object item) { base.PrepareContainerForItemOverride(element, item); ReorderListBoxItem itemContainer = (ReorderListBoxItem)element; itemContainer.ApplyTemplate(); // Loads visual states. // Set this state before binding to avoid showing the visual transition in this case. string reorderState = this.IsReorderEnabled ? ReorderListBoxItem.ReorderEnabledState : ReorderListBoxItem.ReorderDisabledState; VisualStateManager.GoToState(itemContainer, reorderState, false); // Porting issue // Fixed by adding the "Source = this" parameter to the binding so that all ReorderListBoxItems' .IsReorderEnabled DPs are bound to the parent ReorderListBox // TODO: trying to fix this error: //Error: BindingExpression path error: 'IsReorderEnabled' property not found on 'Windows.Foundation.IReference`1<String>'. BindingExpression: Path='IsReorderEnabled' DataItem='Windows.Foundation.IReference`1<String>'; target element is 'Helios.ReorderListBoxItem' (Name='null'); target property is 'IsReorderEnabled' (type 'Boolean') //Binding iREPBinding = new Binding(); //iREPBinding.Path = new PropertyPath(ReorderListBox.IsReorderEnabledPropertyName); //itemContainer.SetBinding(ReorderListBoxItem.IsReorderEnabledProperty, iREPBinding); itemContainer.SetBinding(ReorderListBoxItem.IsReorderEnabledProperty, new Binding { Path = new PropertyPath(ReorderListBox.IsReorderEnabledPropertyName), Source = this }); // TODO: removed this line and replaced with the above (no ability to pass Path directly to Binding in Jupiter) //itemContainer.SetBinding(ReorderListBoxItem.IsReorderEnabledProperty, // new Binding(ReorderListBox.IsReorderEnabledPropertyName) { Source = this }); if (item == this.dragItem) { itemContainer.IsSelected = this.isDragItemSelected; VisualStateManager.GoToState(itemContainer, ReorderListBoxItem.DraggingState, false); if (this.dropTargetIndex >= 0) { // The item's dragIndicator is currently being moved, so the item itself is hidden. itemContainer.Visibility = Visibility.Collapsed; this.dragItemContainer = itemContainer; } else { itemContainer.Opacity = 0; // TODO: replaced this line: // this.Dispatcher.BeginInvoke(() => this.AnimateDrop(itemContainer)); await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync( new CoreDispatcherPriority(), () => this.AnimateDrop(itemContainer)); } } else { VisualStateManager.GoToState(itemContainer, ReorderListBoxItem.NotDraggingState, false); } }
/// <summary> /// Creates a storyboard to animate the visible moves of a rearrange. /// </summary> private Storyboard CreateRearrangeStoryboard(IEnumerable<RearrangeItemInfo> visibleMoves, Duration animationDuration) { Storyboard storyboard = new Storyboard(); ReorderListBoxItem temporaryItemContainer = null; foreach (RearrangeItemInfo move in visibleMoves) { Size itemSize = new Size(this.rearrangeCanvas.RenderSize.Width, move.Height); ReorderListBoxItem itemContainer = null; if (move.ToIndex >= 0) { itemContainer = (ReorderListBoxItem)this.ContainerFromIndex(move.ToIndex); } if (itemContainer == null) { if (temporaryItemContainer == null) { temporaryItemContainer = new ReorderListBoxItem(); } itemContainer = temporaryItemContainer; itemContainer.Width = itemSize.Width; itemContainer.Height = itemSize.Height; this.rearrangeCanvas.Children.Add(itemContainer); this.PrepareContainerForItemOverride(itemContainer, move.Item); itemContainer.UpdateLayout(); } // TODO: had to replace the WriteableBitmap with the itemSnapshot //WriteableBitmap itemSnapshot = new WriteableBitmap((int)itemSize.Width, (int)itemSize.Height); //itemSnapshot.Render(itemContainer, null); //itemSnapshot.Invalidate(); Image itemImage = new Image(); if (itemImage.Source == null) { Task<RenderTargetBitmap> renderTask = RenderImageSource(itemContainer, itemSize); itemImage.Source = renderTask.Result; } //Image itemImage = new Image(); itemImage.Width = itemSize.Width; itemImage.Height = itemSize.Height; //itemImage.Source = renderTask.Result; itemImage.RenderTransform = new TranslateTransform(); this.rearrangeCanvas.Children.Add(itemImage); if (itemContainer == temporaryItemContainer) { this.rearrangeCanvas.Children.Remove(itemContainer); } if (!Double.IsNaN(move.FromY) && !Double.IsNaN(move.ToY)) { Canvas.SetTop(itemImage, move.FromY); if (move.FromY != move.ToY) { DoubleAnimation moveAnimation = new DoubleAnimation(); moveAnimation.Duration = animationDuration; Storyboard.SetTarget(moveAnimation, itemImage.RenderTransform); Storyboard.SetTargetProperty(moveAnimation, TranslateTransform.YProperty.ToString()); moveAnimation.To = move.ToY - move.FromY; storyboard.Children.Add(moveAnimation); } } else if (Double.IsNaN(move.FromY) != Double.IsNaN(move.ToY)) { if (move.FromIndex >= 0 && move.ToIndex >= 0) { DoubleAnimation moveAnimation = new DoubleAnimation(); moveAnimation.Duration = animationDuration; Storyboard.SetTarget(moveAnimation, itemImage.RenderTransform); Storyboard.SetTargetProperty(moveAnimation, TranslateTransform.YProperty.ToString()); const double animationDistance = 200; if (!Double.IsNaN(move.FromY)) { Canvas.SetTop(itemImage, move.FromY); if (move.FromIndex < move.ToIndex) { moveAnimation.To = animationDistance; } else if (move.FromIndex > move.ToIndex) { moveAnimation.To = -animationDistance; } } else { Canvas.SetTop(itemImage, move.ToY); if (move.FromIndex < move.ToIndex) { moveAnimation.From = -animationDistance; } else if (move.FromIndex > move.ToIndex) { moveAnimation.From = animationDistance; } } storyboard.Children.Add(moveAnimation); } DoubleAnimation fadeAnimation = new DoubleAnimation(); fadeAnimation.Duration = animationDuration; Storyboard.SetTarget(fadeAnimation, itemImage); // TODO: this might not get the right property: see commented original code // Storyboard.SetTargetProperty(fadeAnimation, new PropertyPath(UIElement.OpacityProperty)); Storyboard.SetTargetProperty(fadeAnimation, UIElement.OpacityProperty.ToString()); if (Double.IsNaN(move.FromY)) { itemImage.Opacity = 0.0; fadeAnimation.To = 1.0; Canvas.SetTop(itemImage, move.ToY); } else { itemImage.Opacity = 1.0; fadeAnimation.To = 0.0; Canvas.SetTop(itemImage, move.FromY); } storyboard.Children.Add(fadeAnimation); } } return storyboard; }