Click here to Skip to main content
15,888,733 members
Articles / Desktop Programming / WPF
Tip/Trick

WPF Animated Arrange Panel

Rate me:
Please Sign up or sign in to vote.
4.85/5 (7 votes)
13 Apr 2014GPL33 min read 21.8K   964   12   5
Creating custom WPF panel that can be used to Arrange (order) items in MVVM application

Introduction

In this tip, I would like to share my experience on creating ArrangePanel, that allows users to reorder items by dragging them around, supports different layout strategies, fully animated and supports MVVM.

Background

When I started with implementation of reordering functionality, I faced the issue with separation of view and view model. The matter is that View doesn't know anything about View Model and at the same time View Model doesn't know anything about View. Under such conditions, it is hard to notify View Model about changes in items order and not to expose one side to another. And the fact that I expect this panel to be used as layout control in items controls makes reordering synchronization an even harder task.

After thinking a little, I've come up with a small trick: instead of changing children order in child collection, I've introduced an attached property called Order that represents item position in the list. So when user grabs an element and moves it, ArrangePanel will only change Order property of elements that changed their positions.

Another trick is DesiredPosition and Position attached properties. These properties define where panel's child elements will be placed. Both properties are of Rect type, and both define where particular child property should be displayed. Position property controls actual child position inside the panel, while DesiredPosition reflects position that child is going to take. Each time when DesiredPosition changed - it creates a RectAnimation for Position property that will animate child movement from its current position to DesiredPosition. Animation duration depends on distance between these properties.

Using the Code

ArrangePanel can be used just like plain StackPanel or WrapPanel. It can be used as ItemsPanelTemplate in items controls and as stand alone layout panel. Using it allows items reordering by using drag and drop with full animation.

MainWindow.xaml file of demo project contains Grid with three columns. Left and central columns demonstrate ArrangePanel usage as ItemsPanelTemplate and right panel contains it with predefined set of child elements.

Left and central lists are bound to the same data source so that when items from one list are reordered, it is immediately reflected in the other list.

To achieve this functionality, Lancer1WPF:ArrangePanel.Order property is attached to ListBoxItem is bound to the Order property of DataItem.

XML
<ListBox.ItemContainerStyle>
    <Style TargetType="{x:Type ListBoxItem}">
        <Setter Property="Lancer1WPF:ArrangePanel.Order" Value="{Binding Path=Order, Mode=TwoWay}" />
    </Style>
</ListBox.ItemContainerStyle> 

Right now, only TableLayoutStrategy is implemented as is working as default layout for the panel, but it is fairly easy to add new layout strategies.

ArrangePanel

ArrangePanel does not contain any layout logic in it, instead it is delegated to layout strategy. Layout strategy interface is defined as:

C#
public interface ILayoutStrategy
{
    Size ResultSize { get; }
    void Calculate(Size availableSize, Size[] sizes);
    Rect GetPosition(int index);
    int GetIndex(Point position);
}

Layout has three responsibilities:

  1. To determine position of the element. To accomplish this, Calculate and GetPosition methods are used. Calculate method receives the array of children sizes, and available size of the panel where all elements should be placed. While GetPosition returns position of each element.
  2. To determine child index by given position. GetIndex is used in reordering functionality to determine the place where to put dragged element.
  3. To determine resulting panel size.
ArrangePanel itself could be divided into two main parts: reordering functionality and arrangement functionality.

StartReordering, DoReordering and StopReordering methods perform all the job. StartReordering brings dragging element to front by setting ZIndex property. This value will be cleared in StopReordering method. DoReordering is called from mouse move handler and its main responsibility to determine new order index of dragged element.

As ArrangePanel uses attached property Order to store child's index order, it will always look after Order's values and it will assign sequential numbers for each child starting from 0 value for first child. It is done in InitializeEmptyOrder and ReorderOthers methods.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Team Leader EPAM Systems
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionDifferent sized items don't layout correctly Pin
jbolstad30-Jul-16 22:28
jbolstad30-Jul-16 22:28 
QuestionA good article. Just a stupid question from my part Pin
comiscience14-Apr-14 0:52
comiscience14-Apr-14 0:52 
What triggered DesiredPositionProperty's change?
I cannot find it in your code. It's a part of origin interface or UIElement?

THanks Smile | :)
AnswerRe: A good article. Just a stupid question from my part Pin
Oleg Oshkoderov20-May-14 6:17
professionalOleg Oshkoderov20-May-14 6:17 
GeneralRe: A good article. Just a stupid question from my part Pin
comiscience23-May-14 4:08
comiscience23-May-14 4:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.