다음을 통해 공유


WP7 ReorderListBox improvements: rearrange animations and more

Update 2013-10-08: Created a ReorderListBox CodePlex project and pushed a ReorderListBox NuGet package. Use either of those to get the latest version of this control.

Update 2011-12-19: Updated the attached demo project to target the WP 7.1 (Mango) SDK. This required a small change to the auto-scroll logic.

The ReorderListBox control I shared last week was moderately popular, and I've received some great feedback. Today I'm sharing an update to that control that polishes some minor issues and adds a cool new feature I call "rearrange animations". This feature allows the application to programmatically move, insert, or delete items in the list while showing a nice visual indication of what is happening. Specifically, the animations are as follows:

  • Inserted items fade in while later items slide down to make space.
  • Removed items fade out while later items slide up to close the gap.
  • Moved items slide from their previous location to their new location.
  • Moved items which move out of or in to the visible area also fade out / fade in while sliding.

The rearrange animations are complementary to the interactive drag-and-drop reordering, and both features can be enabled at the same time. Unfortunately the rearrange animations are not customizable in XAML due to the way they must be dynamically generated. And when using them you'll have to keep performance in mind: if you make a lot of changes to a large list (such as a full shuffle or sort) then the rearrange animations may take some time to prepare, although once the animations start they should be smooth. In my tests on a Samsung Focus, such complex rearrangements took up to about a second to prepare. Simpler operations such as moving, adding, or removing an item or two are much faster.

Updated source is attached at the ReorderListBox CodePlex site. Here's a complete list of what else has changed since last week:

  • When an item is dropped, it now quickly slides into its proper position rather than suddenly appearing there.
  • Control templates are corrected: a few things were missing compared to the standard ListBox control template, most notably the selected-item accent color.
  • The scrollbar is now made visible while drag-scrolling.
  • Fixed a bug that prevented moving a taller item to the top or bottom position when a shorter item was there.
  • Fixed a bug that caused the list items to jump slightly after moving an item down by more than a page then dropping it.

And now, a video preview of the improved ReorderListBox control!

[Video]

Comments

  • Anonymous
    January 17, 2011
    Fantastic! Just one quick... I have a strange behavior if I disable the scrollviewer inside.. and have one surrounding it...

  • Anonymous
    January 17, 2011
    The drag-and-drop reorder behavior integrates with the ListBox's internal ScrollViewer for several reasons, including auto-scrolling when you drag near the top or bottom edge of the ListBox. I'm not surprised that it doesn't work with an external ScrollViewer. It should be possible to fix the code to handle that, but I have not tried.

  • Anonymous
    January 25, 2011
    Just trying this out. After 5min copying and pasting, it worked instantly. Just adding some tweaks to the template now. Really great work! Thanks!

  • Anonymous
    January 31, 2011
    The comment has been removed

  • Anonymous
    January 31, 2011
    @cohoman, You don't need to copy that DesginData.xml file into your project. It is only needed for the demo application. (To actually fix the error, I think you need to set the Build Action to "DesignData" in the project item properties.)

  • Anonymous
    February 12, 2011
    Any suggestions on how to make this work with the MVVM pattern? The reorder listbox moves items within its source collection (a collection of viewmodels), but the source often is readonly. Wouldn't it be better to raise an event to request to move an item so it can be handled by the model, which is then reflected in the viewmodel collection? In fact, I've tried the latter but it doesn't seem to work because some methods aren't called. Bert.

  • Anonymous
    March 20, 2011
    The comment has been removed

  • Anonymous
    March 21, 2011
    @Neel, you need to use an ObservableCollection<Quote> instead of a List<Quote>.

  • Anonymous
    March 21, 2011
    Love your control.  I am trying to use it with a Context Menu attached to it.  I am running into an issue with getting the underlying item that brought up the Context Menu. When using a normal Listbox control, the below works great: ListBoxItem selectedListBoxItem = this.mylistbox.ItemContainerGenerator.ContainerFromItem((sender as MenuItem).DataContext) as ListBoxItem; When using the Reorderlistbox control, the above code will not return the correct entry if the listbox item was 'reordered'. Any ideas?

  • Anonymous
    March 21, 2011
    Thanks Jason :) After posting this post y'day , I did the same changes which you have suggested and wolha!!! it worked :)

  • Anonymous
    April 27, 2011
    Great control you have here. Before digging in too deep, do you think this control is portable to WPF? I get a lot of discrepancies in terms of RootVisual, WritableBitmap and FindElementsInHostCoordinates that need to be addressed.

  • Anonymous
    April 29, 2011
    @Perry, Sorry I don't know what the problem could be there, I would expect that to work. @AvSomeren, It has been a while since I've done much WPF programming, so I don't think I can give a good answer. I would expect you could find WPF equivalents for the things you mention, although you may encounter other problems if the WPF ListBox control happens to work differently than it does in Silverlight.

  • Anonymous
    May 01, 2011
    Hi Jason, great work! I have one question/suggestion: would you be able to change the code so that it will still performs animations if we change the ListBox ItemsPanelTemplate? For instance: <rlb:ReorderListBox.ItemsPanel>                        <ItemsPanelTemplate>                            <toolkit:WrapPanel Name="_wrapPanel"  Orientation="Horizontal" Width="800"/>                        </ItemsPanelTemplate> <rlb:ReorderListBox.ItemsPanel> If i do this the animations won't be what one would like to see.... Would you consider adapting the code to account for other ItemsPanelTemplates? Or maybe this would be too complex? Regards, Eduardo Serrano

  • Anonymous
    May 01, 2011
    @Eduardo, That should be possible to do, by making the animation-generation code more generic based on the before and after positions of each item and the visible area of the items panel. But don't wait for me, I suggest you try it out yourself. :)

  • Anonymous
    May 01, 2011
    Thank you for your quick reply Jason. I'll definitely give it a try. I'm trying to finish something first and then i'll work on it. If i get it to work (or hit a wall) i'll tell you something =)

  • Anonymous
    May 10, 2011
    Thank you for posting this example, but i have a question.  I would like to use some of your ideas in my app however after reading the Microsoft Public License (MS-PL) included in your code I am not sure I can. I read the agreement to mean that if I use any portion of your code my app must be open source as well since your code will be distributed along with mine to a users phone.  Is this correct? Link: go.microsoft.com/fwlink

  • Anonymous
    May 10, 2011
    @jperry, the MS-PL does not require you to open-source any part of your app. Lots of closed-source apps are built with MS-PL code, for example the popular Silverlight Toolkit: silverlight.codeplex.com/license

  • Anonymous
    May 11, 2011
    thank you for the reply jason, i was just concerned and i did not want to break any rules

  • Anonymous
    June 21, 2011
    The comment has been removed

  • Anonymous
    June 22, 2011
    The comment has been removed

  • Anonymous
    June 26, 2011
    First of all thanks for this great peace of code I need to upgrade the xml (since the order of the items change) that I used to populate the listbox Where is the best place to insert my code in the ReorderListBox Class. I have been trying in the move Method but the drop of the item been moved kind of frees for a fraction of time while my code executed

  • Anonymous
    June 27, 2011
    @Hidroilio, You shouldn't need to modify the ReorderListBox class to do that. Just use an ObservableCollection<T> as the data source, and handle the CollectionChanged event for the collection. In that event-handler you'll have all the information you need to update the XML To avoid freezing the UI thread, you should do any time-consuming work on a background thread. See this link for some discussion of how to do that: wildermuth.com/.../Architecting_WP7_-_Part_9_of_10_Threading

  • Anonymous
    June 27, 2011
    Thanks one more time These tip help me a lot.

  • Anonymous
    July 28, 2011
    Hello Jason, AutoScrolling doesn´t work for me when i drag near the top or bottom of the list, including the Sample Project. Any Ideas? Best Regards, Stephan

  • Anonymous
    July 29, 2011
    @Stephan, you must be using the Mango SDK. In Mango there is a breaking change to the ScrollViewer behavior. Try setting ScrollViewer.ManipulationMode="Control" to restore the old behavior.

  • Anonymous
    July 29, 2011
    Hi Jason, I want to change the background under the up/down arrows. In the Generic.xaml I modified the <Style TargetType="rlb:ReorderListBoxItem">        <Setter Property="Background"  Value="Transparent" /> to: <Style TargetType="rlb:ReorderListBoxItem">        <Setter Property="Background"  Value="{StaticResource ListItemBackground}" /> Each list item should have this background, that I defined in the begining of the Generic.xaml and its xaml: <LinearGradientBrush x:Key="ListItemBackground" EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#FFDDDDDD" Offset="1"/> <GradientStop Color="White"/> </LinearGradientBrush> After the change it looks fine in Blend and at the begining of the application start. But if I want to reorder an item, the application will stop. Do you have any ida what am I doing wrong and how could I fix it? Regards, Péter

  • Anonymous
    August 30, 2011
    Hi, i found a bug in the reordered listbox class. if anybody uses two applicationbar buttons to sort a list ascending and descending and press one button twice and then the other one, you will get a ordering issue with the rearrangeCanvas (this holds the items in the background). For fixing this, you must add in the "else" tree on line 826 following code: this.rearrangeCanvas.Children.Clear(); here the complete fixed function AnimateRearrangeInternal: private void AnimateRearrangeInternal(Action rearrangeAction, Duration animationDuration)        {            // Find the indices of items in the view. Animations are optimzed to only include what is visible.            int viewFirstIndex, viewLastIndex;            this.GetViewIndexRange(true, out viewFirstIndex, out viewLastIndex);            // Collect information about items and their positions before any changes are made.            RearrangeItemInfo[] rearrangeMap = this.BuildRearrangeMap(viewFirstIndex, viewLastIndex);            // Call the rearrange action callback which actually makes the changes to the source list.            // Assuming the source list is properly bound, the base class will pick up the changes.            rearrangeAction();            this.rearrangeCanvas.Visibility = Visibility.Visible;            // Update the layout (positions of all items) based on the changes that were just made.            this.UpdateLayout();            // Find the NEW last-index in view, which may have changed if the items are not constant heights            // or if the view includes the end of the list.            viewLastIndex = this.FindViewLastIndex(viewFirstIndex);            // Collect information about the NEW items and their NEW positions, linking up to information            // about items which existed before.            RearrangeItemInfo[] rearrangeMap2 = this.BuildRearrangeMap2(rearrangeMap,                viewFirstIndex, viewLastIndex);            // Find all the movements that need to be animated.            IEnumerable<RearrangeItemInfo> movesWithinView = rearrangeMap                .Where(rii => !Double.IsNaN(rii.FromY) && !Double.IsNaN(rii.ToY));            IEnumerable<RearrangeItemInfo> movesOutOfView = rearrangeMap                .Where(rii => !Double.IsNaN(rii.FromY) && Double.IsNaN(rii.ToY));            IEnumerable<RearrangeItemInfo> movesInToView = rearrangeMap2                .Where(rii => Double.IsNaN(rii.FromY) && !Double.IsNaN(rii.ToY));            IEnumerable<RearrangeItemInfo> visibleMoves =                movesWithinView.Concat(movesOutOfView).Concat(movesInToView);            // Set a clip rect so the animations don't go outside the listbox.            this.rearrangeCanvas.Clip = new RectangleGeometry() { Rect = new Rect(new Point(0, 0), this.rearrangeCanvas.RenderSize) };            // Create the animation storyboard.            Storyboard rearrangeStoryboard = this.CreateRearrangeStoryboard(visibleMoves, animationDuration);            if (rearrangeStoryboard.Children.Count > 0)            {                // The storyboard uses an overlay canvas with item snapshots.                // While that is playing, hide the real items.                this.scrollViewer.Visibility = Visibility.Collapsed;                rearrangeStoryboard.Completed += delegate                {                    rearrangeStoryboard.Stop();                    this.rearrangeCanvas.Children.Clear();                    this.rearrangeCanvas.Visibility = Visibility.Collapsed;                    this.scrollViewer.Visibility = Visibility.Visible;                    this.AnimateNextRearrange();                };                this.Dispatcher.BeginInvoke(rearrangeStoryboard.Begin);            }            else            {                this.rearrangeCanvas.Children.Clear(); //remove item layer if no items are rearranged                this.rearrangeCanvas.Visibility = Visibility.Collapsed;                this.AnimateNextRearrange();            }        }

  • Anonymous
    August 30, 2011
    i forgot.... Best regards Enno :)

  • Anonymous
    October 23, 2011
    Hi Jason, great work! but can you help a little, how I can put wrap panel to items panel? What I need to change? Thanks

  • Anonymous
    October 24, 2011
    @Serg, I think it would be difficult to change this to use a WrapPanel. Currently the drag-reordering and rearrange-animation logic is all done with the assumption that items are moving only up and down. At a minimum you'd have to re-write a lot of that code, which is moderately complex now and would be much more complex when items can move both horizontally and vertically. I'm not sure what else would have to change.

  • Anonymous
    October 26, 2011
    "you must be using the Mango SDK. In Mango there is a breaking change to the ScrollViewer behavior. Try setting ScrollViewer.ManipulationMode="Control" to restore the old behavior." doesn t work. any other idea? thanks, olaf

  • Anonymous
    November 10, 2011
    The comment has been removed

  • Anonymous
    December 18, 2011
    Hi, Thanks for this great control.  I am using VB.NET for my app, and it works perfectly when referencing the C# phone application project that you created.  However, when installing on a phone, both applications are installed and that is not the desired behaviour. So, I created a C# class library (Silverlight for Windows Phone) and put the Themes/Generic.xaml + both the ReorderListBox and the ReorderListBoxItem classes in there.  At runtime, I keep getting the "ReorderListBoxItem must have a DragHandle ContentPresenter part." error.  Any idea how to solve this? Thanks, Joris

  • Anonymous
    February 04, 2012
    This is awesome.... thanks for sharing. You should probably put this up on Codeplex or something. Great work!

  • Anonymous
    March 01, 2012
    Amazing work! Thanks for sharing.

  • Anonymous
    April 13, 2012
    It's awesome! Thanks for such a great work! One question, how should I move the draggable area to the front(left). I tried to switch the column position of ContentContainer and HandleContainer, I also change the column definition, but it seems does not work. Could you teach me how to change the draggable area position? Thank you very much! Once again, excellent work!

  • Anonymous
    April 15, 2012
    I found out it's because i forgot to change the position of the DragInterceptor. Please ignore my question :P Thank you

  • Anonymous
    May 05, 2012
    Great work that I am thankfully re-using. I found an issue when using it in my application where the selected item would often not be shown in the accent color. A normal listbox does not have this issue. After a lot of experimentation I found out that this issue can be resolved by changing the type of the DragHandle in Generic.xaml to ContentControl i.s.o. ContentContainer. The same type change must be done in file ReorderListBoxItem.cs in lines 21, 224 and 237. Thanks again for a great control.

  • Anonymous
    May 07, 2012
    hi, i'm facing some problems with the dropping part. There's no error in the codes but when i drag the item, it will jump back to its original position. How can this be resolved? Thanks for sharing this code.

  • Anonymous
    June 22, 2012
    Thanks so much. It works like a charm with a list has >300 items :)

  • Anonymous
    November 23, 2012
    No we need this control as an "ReorderListView" for Windows 8! :)

  • Anonymous
    December 04, 2012
    Hi Jason, I use your control in my todo-list app 2Day (www.2day-app.com). However, the control does not work when I upgrade the project to WP8. In this case, the dragged item does not move but all the other items are dragged. Do you have any idea ?

  • Anonymous
    December 26, 2012
    I've just tried with WP8 and it works fine.

  • Anonymous
    January 15, 2013
    Hi Jason Thanks so much ! I aslo use your control in WP8 The problem is the focusd item dosn't move but the whole list is moving i don't know why ,it's worked in WP7

  • Anonymous
    February 18, 2013
    OK, here is fix for WP8: In function dragInterceptor_ManipulationStarted() on the first row add: scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled; Then in function dragInterceptor_ManipulationCompleted() on the last row add: scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;

  • Anonymous
    March 05, 2013
    working good but how can i get the index of the dragged item and the index of the dropped position? thanks

  • Anonymous
    March 06, 2013
    Index of position to drop: dropTargetIndex

  • Anonymous
    April 12, 2013
    Lookig at the code I cannot figure out how to programatically move an item from position X to position Y. Is it possible?

  • Anonymous
    September 01, 2013
    The comment has been removed

  • Anonymous
    September 01, 2013
    The comment has been removed

  • Anonymous
    September 17, 2013
    Hi Jason Ginchereau, In your ReorderListBox, how can i disable top item? Don't alow move and show drag icon? Thank you in advance.

  • Anonymous
    October 24, 2013
    Hi Jason Ginchereau, How I can handle event reorder action? thanks

  • Anonymous
    October 27, 2013
    Hi Jason, excellent work! But in my app i need to use StackPanel instead of VirtualizingStackPanel and it fails in AnimateDrop() because itemContainer.RenderSize is zero. Can you help me with this fix? Thanks.

  • Anonymous
    July 09, 2014
    Hi Jason, This is fantastic feature. I'm very excited to add this feature to my application. But I got following error when I set ItemSource from the IQueryable list, as below:            var Query = from it in AppDB.TableName                        select it;            ReorderListBox.ItemsSource = Query.ToList(); here's the details of my error. "InvalidOperationException", Additional information: Operation not supported on read-only collection.

  • Anonymous
    July 09, 2014
    Mohamed, that error message is pretty self-explanatory: you cannot reorder a read-only collection. The Enumerable.ToList() method returns a read-only list. Try wrapping your query results in a new List<T> or ObservableCollection<T> before using it as the ItemsSource.

  • Anonymous
    March 11, 2015
    Thanks for the code :) It really helped me a lot. But I have one doubt,that is their a way to hide the re-order image and make the entire row to get selected and dragged?

  • Anonymous
    March 16, 2015
    is there a version for wp8.1(window runtime)?