Quickstart: Dragging content

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

People can drag text, images, or other data to reorder them in a GridView or ListView, or people can drag items onto other kinds of controls.

In a Windows Store app you can use a mouse, touch gesture, or voice to drag items to reorder them in a GridView or ListView, or you can drag an item from a single GridView or ListView onto another XAML control. To drag from a GridView or ListView, set the CanDragItems attribute to True. To enable a control to accept dragged items, set the AllowDrop attribute to True.

You can apply animations to drag operations. Follow the same general guidelines that are given in Guidelines for drag animations, even though that topic is written for JavaScript. The article Animating your UI shows how to apply drag animations in XAML.

Drag between separate apps is not supported, but you can use the sharing APIs or the Clipboard to transfer data in those scenarios. All of the data transfer APIs share some types in common—these include the Windows::ApplicationModel::DataTransfer ::DataPackage class and the Windows::ApplicationModel::DataTransfer ::StandardDataFormats enumeration. In a drag operation, the DataPackage class is used to wrap the data that's being transferred, and the StandardDataFormats enumeration defines formats for which DataPackage provides special Set* methods. These include Text, Bitmap, Html, Rtf, Uri, and collections of StorageItem object. The DataPackageView class provides the asynchronous Get* methods that you use in the Drop event handler to retrieve the data. You can also drag user-defined objects by using the DataPackage::SetData method and DataPackageView::GetDataAsync methods.

To enable the dragging of items in a GridView or ListView

It's trivial to enable dragging of elements in a GridView or ListView. Just set these attributes on the control in the XAML: AllowDrop="True" CanDragItems="True" CanReorderItems="True"

Drag basic steps

No matter what kind of data you are transferring, a drag operation between controls has two basic steps:

  1. Handle the Windows::UI::Xaml::Controls::DragItemsStarting event in the source where the drag gesture begins, which can be either a Windows::UI::Xaml::Controls::GridView or Windows::UI::Xaml::Controls::ListView. The DragItemsStartingEventArgs parameter contains a reference to the item or items that are being dragged. The parameter also contains a Data property that is the DataPackage that will be passed to the drop target. In the event handler, get the data that you want to pass from the DragItemsStartingEventArgs::Items collection, and then use that data to populate the Data property in whatever way is appropriate for your scenario.

  2. Handle the Windows::UI::Xaml::Controls::Drop event in the drop target. Retrieve the DataPackage object that is passed in, call GetView to get its DataPackageView property, and use its Get* methods to access the data that you previously stored in the DragItemsStarting event. A drop target can be any XAML control that supports the "Drop" event.

Additional events—DragEnter, DragLeave, and DragOver—are primarily useful for creating animations or other visual effects, but are not essential for the basic data transfer operation itself.

To drag text

In the following example, assume that the drop source is a GridView that contains String items, and that DragItemsStarting_1 is the event handler for the DragItemsStarting event that is fired by the GridView. If the GridView:: SelectionMode property is set to Multiple, then the Items collection may contain more than one element. In this example, assume that the SelectionMode is set to Single, so that it's safe to assume that the item being dragged is at position 0.

Drag:

void DragDrop::DragText::DragTextGridView_DragItemsStarting_1(Platform::Object^ sender, Windows::UI::Xaml::Controls::DragItemsStartingEventArgs^ e)
{    
    auto mytxt = safe_cast<String^>(e->Items->GetAt(0));
    e->Data->SetText(mytxt);
}
private void DragTextGridView_DragItemsStarting_1(object sender, DragItemsStartingEventArgs e)
{
    var mytxt = e.Items[0] as String;
    if (mytxt != null)
    {
        e.Data.SetText(mytxt);
    }
}

Drop:

The following example shows how to handle the Drop event. Assume in this case that the drop target is a XAMLTextBlock element.

void DragDrop::DragText::DropTextHere_Drop_1(Platform::Object^ sender, Windows::UI::Xaml::DragEventArgs^ e)
{
    using namespace concurrency;
    using namespace Windows::ApplicationModel::DataTransfer;

    DataPackageView^ dataView = e->Data->GetView();
    create_task(dataView->GetTextAsync())
    .then([this, sender](String^ txt)
    {
        TextBlock^ tb = safe_cast<TextBlock^>(sender);
        tb->Text = txt;
    });
}
private async void DropTextHere_Drop_1(object sender, DragEventArgs e)
{
    var dataView = e.Data.GetView();
    var tb = sender as TextBlock;
    if (sender != null)
    {
        tb.Text = await dataView.GetTextAsync();
    }
}

To drag bitmaps

If a BitmapImage is obtained from a file, then use the DataPackage::SetStorageItems and DataPackageView::GetStorageItemsAsync methods to drag it as an IStorageItem. Furthermore, if you have to create a GridView or ListView of draggable file-based BitmapImage objects, the best approach is to create a bindable wrapper object that contains as public properties the BitmapImage and the original StorageFile, and perhaps other information such as the file name or attributes. Because an IStorageItem object itself is not bindable, it cannot be displayed easily in a GridView.

Important  Although you can use the FileInformation class to bind virtualized file objects to a XAML control, the virtualized objects are not easily draggable.

 

In the following example, assume that the drag source is a ListView that is data-bound to a list of user-defined PhotoWrapper objects. Each PhotoWrapper contains a BitmapImage and the StorageFile object that was obtained when the file was originally opened. The DataTemplate on the ListView binds to the ImageFile property.

    [Windows::UI::Xaml::Data::Bindable]
    public ref class PhotoWrapper sealed
    {
    public:
        property Windows::UI::Xaml::Media::Imaging::BitmapImage^ Thumbnail;
        property Windows::Storage::StorageFile^ ImageFile;
    };
    public sealed class PhotoWrapper
    {
        public BitmapImage Thumbnail { get; set; }
        public StorageFile ImageFile { get; set; }
    }

Drag:

The following DragItemsStarting event handler shows how to create a list of one or more IStorageItem objects in the Drag operation and insert them into the DataPackage.

void DragDrop::DragImage::ListView_DragItemsStarting_1(Platform::Object^ sender, Windows::UI::Xaml::Controls::DragItemsStartingEventArgs^ e)
{
    IVector<IStorageItem^>^ selectedFiles = ref new Vector<IStorageItem^>();
    for (unsigned int i = 0; i < e->Items->Size; i++)
    {
        PhotoWrapper^ wrapper = safe_cast<PhotoWrapper^>(e->Items->GetAt(i));
        selectedFiles->Append(wrapper->ImageFile);
    }

    e->Data->SetStorageItems(selectedFiles);
}
        void ListView_DragItemsStarting_1(object sender, DragItemsStartingEventArgs e)
        {
            IList<IStorageItem> selectedFiles = new List<IStorageItem>();
            foreach (var item in e.Items)
            {
                var wrapper = item as PhotoWrapper;
                selectedFiles.Add(wrapper.ImageFile);
            }

            e.Data.SetStorageItems(selectedFiles);
        }

Drop:

In the Drop event, get the read-only DataPackageView property and call the OpenReadAsync method to retrieve the stream and then create a BitmapImage from it. After that operation completes, set the Source property on the Image control, and then remove the corresponding PhotoWrapper object from the source list.

void DragDrop::DragImage::ListView_Drop_1(Platform::Object^ sender, Windows::UI::Xaml::DragEventArgs^ e)
{
    DataPackageView^ dpView = e->Data->GetView();

    create_task(dpView->GetStorageItemsAsync())
    .then([this](IVectorView<IStorageItem^>^ images)
    {
        for (unsigned int i = 0; i < images->Size; i++)
        {
            create_task([images, i, this]()
            {
                // Get a stream from the file object.
                IStorageFile^ file = safe_cast<IStorageFile^>(images->GetAt(i));
                return file->OpenReadAsync();
            }).then([this](IRandomAccessStreamWithContentType^ s)
            {
                // Set the stream as the bitmap source
                BitmapImage^ bi = ref new BitmapImage();
                bi->SetSourceAsync(s);
                return bi;
            }).then([this](BitmapImage^ bi)
            {
                // Add the BitmapImage to the source list.
                // The update will be reflected in the ListView.
                m_targetImages->Append(bi);
            });
        }        
    });
}
        private async void ListView_Drop_1(object sender, DragEventArgs e)
        {
            var dpView = e.Data.GetView();
            var images = await dpView.GetStorageItemsAsync();

            foreach (var image in images)
            {
                // Get a stream from the file object.
                IStorageFile file = image as StorageFile;
                var randomStream = await file.OpenReadAsync();

                // Set the stream as the bitmap source
                BitmapImage bi = new BitmapImage();
                await bi.SetSourceAsync(randomStream);

                // Add the BitmapImage to the source list.
                // The update will be reflected in the ListView.
                m_targetImages.Add(bi);
            }
        }
  

To drag user-defined objects

You can drag custom objects by inserting them into the DataPackage::Properties associative container. In this case, because the DataPackage has no knowledge of the data, you must provide a string key that describes the data format. In a drag operation, the format string is private to your app. Therefore, you don't have to choose a universally understood name. Just provide that same string when you retrieve the data. In this example, the same key is provided for all items, but you could also provide each item with an individual key.

In the following example, assume we have a bindable class City and want to drag one or more City objects from a GridView to a ListView.

    [Windows::UI::Xaml::Data::Bindable]
    public ref class City sealed
    {
    public:
        City(Platform::String^ name, int pop)
        {
            this->Name = name;
            this->Population = pop;
        }
        property Platform::String^ Name;
        property int Population;
    };
    public sealed class City
    {
        public City(String name, int pop)
        {
            this.Name = name;
            this.Population = pop;
        }
        public String Name { get; set; }
        public int Population { get; set; }
    }

Drag:

void DragDrop::MainPage::GridView_DragItemsStarting_1(Platform::Object^ sender, Windows::UI::Xaml::Controls::DragItemsStartingEventArgs^ e)
{
    for (auto item : e->Items)
    {    
        // If you want to drop only a subset of the dragged items,
        // then make each key unique.    
        e->Data->Properties->Insert("MyApp.MyCity", item);
    }
}
void GridView_DragItemsStarting_1(Object sender, DragItemsStartingEventArgs e)
{
    foreach(var item in e.Items)
    {
        // If you want to drop only a subset of the dragged items,
        // then make each key unique. 
        e.Data.Properties.Add("MyApp.MyCity", item);
     }
}

Drop:

In the drop operation in C++, the items are retrieved from the DataPackage, cast back to their original type of City, and copied into the data source for the drop target.

void DragDrop::MainPage::ListViewDropTarget_Drop_1(Platform::Object^ sender, Windows::UI::Xaml::DragEventArgs^ e)
{    
    DataPackageView^ dpView = e->Data->GetView();
    for (auto prop : dpView->Properties)
    {
        auto city = safe_cast<City^>(prop->Value);
        m_dropTargetItems->Append(city);
    }  
}
void ListViewDropTarget_Drop_1(Object sender, DragEventArgs e)
{
    DataPackageView dpView = e.Data.GetView();
    foreach (var prop in dpView.Properties)
    {
        var city = prop.Value as City;
        m_dropTargetItems.Add(city);
    }
}