Silverlight cookbook: drag & drop recipe

    In this article I will analyze the work with drag & drop using an example of such an application:

    image

    The idea is that from the list of all users we can personally (using drag & drop) sort users into two groups. Moreover, users with negative karma cannot be added to the white list.

    But closer to the point. I ask for cat.


    So, the introduction.


    Out of the box, we cannot immediately take and start using the drag & drop functionality (we have to implement everything ourselves), but to make our life easier, the specially trained guys from Silverlight Team at Microsoft have already implemented everything in the Silverlight Toolkit .
    This functionality is presented in the form of wrappers for some basic controls (User Controls) with the name of the control + DragDropTarget (for a ListBox, this is ListBoxDragDropTarget ). It is on the example of a ListBox, or rather several ListBoxes, that drag & drop will be demonstrated.

    Let's create a new Silverlight application.


    image

    I called it DragAndDropTestApp, which, in fact, does not matter at all.
    In the next dialog box, leave everything as is and click OK.

    Immediately add a link to the Toolkit:

    image

    It is assumed that it has already been downloaded and installed.

    Add the following namespaces to MainPage.xaml:

    xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
    xmlns:my="clr-namespace:DragAndDropTestApp"
    * All source code (under and below) was highlighted with Source Code Highlighter.

    On the Toolkit. And to the namespace in which our application lies.

    Sketch xaml.
    Each of the three lists is represented by the following markup:
          Text="Users:"
          Style="{StaticResource Header}"/>
               Grid.Row="1"
          HorizontalContentAlignment="Stretch"
          VerticalContentAlignment="Stretch">
          
            
              
                
              

            

          

        

    TextBox with a title for a list of elements (with all the styles I dabbled in App.xaml). And, actually, the ListBox itself, wrapped in a ListBoxDragDropTarget. ItemTemplate is redefined to display users. UCUser is my own control. There is nothing supernatural inside, see for yourself.
    image
    1.     x:Name="LayoutRoot"
    2.     Background="Transparent">
    3.     
    4.       
    5.       
    6.       
    7.     
    8.     
    9.       
    10.       
    11.     
    12.     
    13.       Source="../Images/gnome_face_devilish.png"
    14.       Width="48"
    15.       Height="48"
    16.       Margin="15, 0, 20, 0"
    17.       Grid.RowSpan="2"/>
    18.     
    19.       Text="Nickname:"
    20.       Grid.Column="1"/>
    21.     
    22.       Text="{Binding Path=NickName, Mode=OneWay}"
    23.       Grid.Column="2"
    24.       FontWeight="Bold"/>
    25.     
    26.       Text="Karma:"
    27.       Grid.Row="1"
    28.       Grid.Column="1" />
    29.     
    30.       Text="{Binding Path=Karma, Mode=OneWay}"
    31.       Grid.Row="1"
    32.       Grid.Column="2"
    33.       FontWeight="Bold" />
    34.   

    The Source is set for the picture and the data binding (Binding) is registered.
    By the way, there is a separate class for the user. Here is his view:
    public class User
      {
        public string NickName { get; set; }
        public int Karma { get; set; }
      }

    Now create a collection for each list and simulate user loading:
    1. public partial class MainPage : UserControl
    2.   {
    3.     public MainPage()
    4.     {
    5.       InitializeComponent();
    6.       this.DataContext = this;
    7.       InitializeCollections();
    8.       LoadUsers();
    9.     }
    10.  
    11.     //Объявление коллекций
    12.     public ObservableCollection Users { get; set; }
    13.     public ObservableCollection WhiteList { get; set; }
    14.     public ObservableCollection BlackList { get; set; }
    15.  
    16.     //Инициализация коллекций
    17.     private void InitializeCollections()
    18.     {
    19.       Users = new ObservableCollection();
    20.       WhiteList = new ObservableCollection();
    21.       BlackList = new ObservableCollection();
    22.     }
    23.  
    24.     //Метод, имитирующий загрузку данных в коллекцию Users
    25.     private void LoadUsers()
    26.     {
    27.       var r = new Random();
    28.  
    29.       //Добавление 20 пользователей со случайным значением кармы.
    30.       for (int i = 1; i < 21; i++)
    31.       {
    32.         var newUser = new User
    33.         {
    34.           NickName = "Username" + i,
    35.           Karma = r.Next(200) - 100
    36.         };
    37.  
    38.         Users.Add(newUser);
    39.       }
    40.     }
    41.   }

    As the DataContext for MainPage, install MainPage itself . I did this so that I could easily attach to the data. It looks like this:

    The last touch is missing for Drag & Drop to work:
    AllowDrop="True"

    By setting this property to true, we allow the element to accept data using drag & drop. Moreover, you can install it both in ListBox and ListBoxDragDropTarget.

    Here's what happened:

    image

    Now you can drag a user with negative karma to any of the lists.
    According to the plan, you must prohibit dragging one to the White List.

    First, let's look at the events that are generated when Drag-n-Drop'e:

    image

    DragEnter occurs when the dragged container crosses the border of the destination element (fires before the DragOver event).
    DragLeave - the dragged container was pulled from the source element.
    DragOver - fires when the dragged container moves over the destination element.
    Drop - the dragged container was “dropped” to the destination element.

    Add a DragEnter event handler for the whitelist.
    1.     
    2.       AllowDrop="True"
    3.       Grid.Row="1"
    4.       Grid.Column="1"
    5.       HorizontalContentAlignment="Stretch"
    6.       VerticalContentAlignment="Stretch"
    7.       DragEnter="ListBoxDragDropTarget_DragEnter">
    8.       
    9.         ItemsSource="{Binding Path=WhiteList, Mode=TwoWay}"
    10.         x:Name="lbWhiteList">
    11.         
    12.           
    13.             
    14.           
    15.         
    16.       
    17.     

    And give ListBox a name, it will come in handy.

    The handler itself:
    1. private void ListBoxDragDropTarget_DragEnter(object sender, Microsoft.Windows.DragEventArgs e)
    2.     {
    3.       //Узнали формат
    4.       var dataFormat = e.Data.GetFormats()[0];
    5.  
    6.       //Получили объект в формате ItemDragEventArgs
    7.       var dragEventArgs = e.Data.GetData(dataFormat) as ItemDragEventArgs;
    8.  
    9.       //Получили коллекцию перетаскиваемых элементов
    10.       SelectionCollection sc = dragEventArgs.Data as SelectionCollection;
    11.       
    12.       //Проверка каждого элемента коллекции
    13.       foreach (var item in sc)
    14.       {
    15.         //Если хоть один из перетаскиваемых элементов (пользователей) имеет отрицательную карму
    16.         //запрещаем добавлять эти элементы в белый список.
    17.         if ((item.Item as User).Karma < 0)
    18.         {
    19.           lbWhiteList.AllowDrop = false;
    20.           break;
    21.         }
    22.       }
    23.     }

    DragEventArgs.Data returns an IDataObject that contains the data associated with the corresponding drag event. But the data cannot be accessed directly, they are marked as Non-Public:

    image

    To get this data, the GetData () method is used , which returns the data in the format passed as a parameter (the format can be specified as a string or type). And you can find out the format using the GetFormats () method .

    The result is an instance of the ItemDragEventArgs class . This class contains information describing a drag event on a UIElement. It has the Data property , which is already directly related to the draggable container. We bring this object toSelectionCollection .
    The collection is used here because we can drag several elements at once
    (this can be checked very simply, for the ListBox, set the SelectionMode property to Multiple ).
    It remains to run through the entire collection and check whether there are guys with negative karma among the dragged users. If there is, then simply prohibit them "drop" in the white list.

    You can return AllowDrop to its previous value (in True) in the MouseLeave event handler .

    And lastly, to realize the ability to sort items within a single list using drag-n-drop, simply redefine the template for ItemsPanel:

      
        
      



    You can download the project here .

    Also popular now: