WPF Drag and Drop Between ItemsControls

Posted by: Clarity Blogs: ASP.NET, on 07 Apr 2009 | View original | Bookmarked: 0 time(s)

Last week I blogged about a generic Drag and Drop framework. That post focused on the framework and gave some simple implementations of it, but didnt get into more practical examples. So, in honor of Opening Day, heres a quick example of how to use the framework to create the Cincinnati Reds batting order by dragging from a list of players to another list.

Setup

The first thing we need to do is get all of our resources set up. I included the existing DragDropAdornerBase and DragDropHelper classes (discussed in the framework post) into my new project. Next, I set up the images of the Reds starting roster as application level resources.

Model

Now that our resources are in place, lets make a quick object to represent a player. I named this class Player (surprise!). Player.cs has simple public properties to hold the data well show on the screen: Picture, Name and Position.

namespace Clarity.Demo.ListDragDrop
{
    public class Player
    {
        public string Name { get; set; }
        public Position Position { get; set; }
        public BitmapImage Img { get; set; }

        public Player() { }

        public Player(string name, Position position, BitmapImage img)
        {
            Name = name;
            Position = position;
            Img = img;
        }
    }

    public enum Position
    {
        Pitcher = 1,
        Catcher = 2,
        First = 3,
        Second = 4,
        ShortStop = 5,
        Third = 6,
        Outfield = 7
    }
}

View

Well also need a UserControl to show the player information. PlayerControl inherits from UserControl and has no code behind. In order to represent the player data visually without code behind PlayerControl takes advantage of DataBinding to the Player model object.

<Canvas Background="Red">
   <Image x:Name="playerImage" Source="{Binding Img}" Margin="5 5 0 5" MaxHeight="90" MaxWidth="90"/>
   <TextBlock x:Name="name" Text="{Binding Name}" Background="Transparent" Margin="100 15 5 0"/>
   <TextBlock x:Name="position" Text="{Binding Position}" Background="Transparent" Margin="100 60 5 0"/>
</Canvas>

And now we have to build the window. Lets make this as simple as possible, so well just put 2 scrollable ItemsControls that use the PlayerControl as a DataTemplate.

<Window x:Class="Clarity.Demo.ListDragDrop.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Clarity.Demo.ListDragDrop"
    Title="Window1" Height="600" Width="800" Loaded="Window_Loaded">
    <Canvas>
        <UniformGrid Rows="1" Columns="2" Height="550">
            <ScrollViewer Margin="15 15 30 15" >
                <ItemsControl x:Name="playerList">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <local:PlayerControl Margin="5"/>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </ScrollViewer>
            <ScrollViewer Grid.Column="1" Margin="30 15 15 15">
                <ItemsControl x:Name="lineup">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <local:PlayerControl Margin="5"/>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </ScrollViewer>
        </UniformGrid>
    </Canvas>
</Window>

And now well populate the playerList ItemsControl in the Window codebehind:

public partial class Window1 : Window
{
    ObservableCollection<Player> _players = new ObservableCollection<Player>();
    ObservableCollection<Player> _lineup = new ObservableCollection<Player>();

    public Window1()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        LoadPlayers();
        playerList.ItemsSource = _players;
        lineup.ItemsSource = _lineup;
    }

    private void LoadPlayers()
    {
        _players.Add(new Player("Aaron Harang", Position.Pitcher, 
            (BitmapImage)App.Current.TryFindResource("harang")));
        _players.Add(new Player("Alex Gonzalez", Position.ShortStop,
            (BitmapImage)App.Current.TryFindResource("gonzalez")));
        _players.Add(new Player("Brandon Phillips", Position.Second,
            (BitmapImage)App.Current.TryFindResource("phillips")));
        _players.Add(new Player("Chris Dickerson", Position.Outfield,
            (BitmapImage)App.Current.TryFindResource("dickerson")));
        _players.Add(new Player("Edwin Encarnacion", Position.Third,
            (BitmapImage)App.Current.TryFindResource("encarnacion")));
        _players.Add(new Player("Jay Bruce", Position.Outfield,
            (BitmapImage)App.Current.TryFindResource("bruce")));
        _players.Add(new Player("Joey Votto", Position.First,
            (BitmapImage)App.Current.TryFindResource("votto")));
        _players.Add(new Player("Ramon Hernandez", Position.Catcher,
            (BitmapImage)App.Current.TryFindResource("hernandez")));
        _players.Add(new Player("Willy Taveras", Position.Outfield,
            (BitmapImage)App.Current.TryFindResource("taveras")));
    }
}

Now, running the app gives us the following window:

image 

Implementation

OK, so at this point weve got the player model and view in place. Now, lets add the fun stuff. First, lets create an adorner for our drag and drop. Inheriting from the DragDropAdornerBase Ive created a simple adorner that just shows the player picture (hiding the RenderTransforms, Animations and Triggers for simplicity).

<local:DragDropAdornerBase x:Class="Clarity.Demo.ListDragDrop.PlayerAdorner"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Clarity.Demo.ListDragDrop"
    Height="75" Width="100">
    <UserControl.RenderTransform...>
    <UserControl.Resources...>
    <UserControl.Triggers...>
    <Grid x:Name="grid">
        <Grid.RenderTransform...>
        <Image x:Name="playerImage" Source="{Binding Img}" Margin="2"/>
    </Grid>
</local:DragDropAdornerBase>

Now that adorner control is created, that means were ready to add it as a resource to Window1.

<Window.Resources>
    <local:PlayerAdorner x:Key="adorner"/>
</Window.Resources>

Of course, there are three more pieces necessary to implement the drag and drop. 1) Window1 needs to contain an adorner layer, 2) the PlayerControl DataTemplates need to define the DragDropHelper attached properties to be recognized as Drag Sources, and 3) Window1 needs to subscribe to the DragDropHelper.ItemDropped event.

First the adorner layer gets added at the end of the Window1 xaml:

    </UniformGrid>
    <Canvas x:Name="adornLayer" Visibility="Collapsed"/>
</Canvas>

Next the playerList and lineup ItemsControls have their DataTemplates redefined:

<local:PlayerControl Margin="5"
                 local:DragDropHelper.AdornerLayer="adornLayer"
                 local:DragDropHelper.DragDropControl="{StaticResource adorner}"
                 local:DragDropHelper.DropTarget="lineup"
                 local:DragDropHelper.IsDragSource="true"/>
<local:PlayerControl Margin="5"
                 local:DragDropHelper.AdornerLayer="adornLayer"
                 local:DragDropHelper.DragDropControl="{StaticResource adorner}"
                 local:DragDropHelper.DropTarget="playerList"
                 local:DragDropHelper.IsDragSource="true"/>

And finally, Window1 subscribes to the ItemDropped event to add and remove the players accordingly.

void DragDropHelper_ItemDropped(object sender, DragDropEventArgs e)
{
    Player p = e.Content as Player;
    if (p == null) return;

    if (_players.Contains(p))
    {
        _players.Remove(p);
        _lineup.Add(p);
    }
    else if (_lineup.Contains(p))
    {
        _lineup.Remove(p);
        _players.Add(p);
    }
}

And thats all it takes. Now we can drag and drop players from the left list to the right to set the batting order:

 image

And back right to left if you change your mind

image image

Happy drag and dropping, and go Reds!

Next Steps

Ill be adding a link to the source in the morning. Also, stay tuned for Part 2, including some hot-ification of the dropping.

Advertisement
Free Agile Project Management Tool from Telerik
TeamPulse Community Edition helps your team effectively capture requirements, manage project plans, assign and track work, and most importantly, be continually connected with each other.
Category: WPF | Other Posts: View all posts by this blogger | Report as irrelevant | View bloggers stats | Views: 5053 | Hits: 96

Similar Posts

  • Getting and setting max zIndex with jQuery more
  • RadDrag&Drop with a Canvas twist more
  • Winforms Release History : Q2 2009 SP1 (version 2009.2.9.729) more
  • Using the DragDropService in RadDock more
  • Telerik Announces Support for Microsoft Silverlight 3 more
  • WPF Release History : Q2 2009 (version 2009.2.701) more
  • Expression Blend 3 Preview more
  • Generic WPF Drag and Drop Adorner more
  • Silverlight Release History : Q1 2009 (version 2009.1.312) more
  • Drag and Drop with Managed Code more

News Categories

.NET | Agile | Ajax | Architecture | ASP.NET | BizTalk | C# | Certification | Data | DataGrid | DataSet | Debugger | DotNetNuke | Events | GridView | IIS | Indigo | JavaScript | Mobile | Mono | Patterns and Practices | Performance | Podcast | Refactor | Regex | Security | Sharepoint | Silverlight | Smart Client Applications | Software | SQL | VB.NET | Visual Studio | W3 | WCF | WinFx | WPF | WSE | XAML | XLinq | XML | XSD