Introduction
How to drag and drop ListBox Items in Windows Presentation Foundation(WPF) within the parent bounds has been a question that's appeared on many websites. So I have decided to tackle the problem here. To demonstrate drag and drop Listbox items within the parent bounds, I've added Telerik Assembly which contains drag-drop-behavior (<telerik: ListBoxDragDropBehavior/>). For dragging and dropping Listbox items and for restricting the dragged items within the parent bounds an adorner layer can be used. You can select multiple Listbox items by holding the Ctrl or Shift key and then dragging one of the selected items.
Generally, the drag-drop operations are executed in a separate window. The Adorner layer can also be used, which will force the drag visual to remain within the boundaries of the current window. Indeed, the <telerik: ListBoxDragDropBehavior/> Telerik behavior also exposes methods for further customization, and the target framework version is Dot-Net Framework - 4.7.2. Assembly Versions used in the sample is 2020.3.1012.40Telerik.Windows.Control and Telerik.Windows.Control.Navigation.
Getting Started
In this walkthrough, you will create a custom WPF UserControl that represents ListBox Items. You will implement functionality on the control to enable data transfer through telerik-drag-and-drop behavior. For example, if you drag one ListBoxItem to another, the data is copied from the source to the target.
- Creating a WPF Project. Open Visual Studio 2017 or higher version.
- Go to File => New => Project.
- Select Window in installed templates.
- Select WPF Application.
- Enter the Name and choose the location.
- Click OK.
- Create a UserControl and add the Following Xaml Code and XAML.cs code-behind.
- Add telerik reference in xaml.
- xmlns:telerik=http://schemas.telerik.com/2008/xaml/presentation
DragDropCustomControl.xaml
<UserControl
x:Class="DragDropWithinParentBoundaries.CustomControl.DragDropCustomControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:behavior="clr-namespace:DragDropWithinParentBoundaries.DragDropBehavior"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DragDropWithinParentBoundaries.Models"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
d:DesignHeight="150"
d:DesignWidth="400"
mc:Ignorable="d">
<UserControl.Resources>
<local:CountryViewModel x:Key="CountryViewMode"/>
<Style x:Key="DraggableListBoxItem" TargetType="telerik:RadListBoxItem">
<Setter Property="telerik:DragDropManager.AllowCapturedDrag" Value="True"/>
</Style>
</UserControl.Resources>
<Grid DataContext="{StaticResource CountryViewMode}">
<telerik:RadListBox
x:Name="radListObj"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AllowDrop="True"
ItemContainerStyle="{StaticResource DraggableListBoxItem}"
ItemsSource="{Binding CountryView}"
SelectionMode="Extended">
<telerik:RadListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<TextBlock FontWeight="Bold" Text="{Binding Name}"/>
<TextBlock Grid.Column="1" Text="{Binding Capital}"/>
</Grid>
</DataTemplate>
</telerik:RadListBox.ItemTemplate>
<telerik:RadListBox.DragVisualProvider>
<telerik:ScreenshotDragVisualProvider />
</telerik:RadListBox.DragVisualProvider>
<telerik:RadListBox.DragDropBehavior>
<telerik:ListBoxDragDropBehavior/>
<!--default telerik behaviour -->
</telerik:RadListBox.DragDropBehavior>
</telerik:RadListBox>
</Grid>
</UserControl>
Code Explanation
- Telerik: ScreenshotDragVisualProvider creates drag visuals containing screenshots of the dragged item containers during drag/drop operations. It enables you to display the default drag cue of Telerik ScreenshotDragVisualProvider during drag operation.
- Telerik: ListBoxDragDropBehavior provides drag-drop capabilities for standard ListBox controls. This behavior involves two operations: a drag source from which the dragged object originates and a drop target which receives the dropped object.
DragDropCustomControl.xaml.cs
public partial class DragDropCustomControl : UserControl
{
public DragDropCustomControl()
{
Telerik.Windows.DragDrop.DragDropManager.UseAdornerLayer = true;
InitializeComponent();
}
}
Code Explanation
- UseAdornerLayer property is set to true to restrict the drag visual within the parent bounds.
- By default, the DragDropManager shows the drag visual in a separate window. Since the 2018 build version, you have the option to set the UseAdornerLayer property of the DragDropManager. After this property is set to True, the drag visual will be shown in the AdornerLayer of the MainWindow.(For Restricting the Visual Within the Parent Bounds).
Attaching DefaultBehavior in Xaml
Models-AutoImplemented properties
Country.cs
using System;
using System.ComponentModel;
using System.Linq.Expressions;
public class Country : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get;
set;
}
public string Capital
{
get;
set;
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, args);
}
}
protected void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
this.OnPropertyChanged(((MemberExpression)propertyExpression.Body).Member.Name);
}
}
CountryViewModel.cs
using System.Collections.ObjectModel;
using Telerik.Windows.Controls;
public class CountryViewModel : ViewModelBase
{
private ObservableCollection<Country> countryView;
public CountryViewModel()
{
this.CountryView = new ObservableCollection<Country>()
{
new Country
{
Name = "India",
Capital = "New Delhi"
},
new Country
{
Name = "United States Amercia",
Capital = "Washington D.C"
},
new Country
{
Name = "United Kingdom",
Capital = "London"
},
new Country
{
Name = "United Arab Emirates",
Capital = "Abu Dhabi"
},
new Country
{
Name = "Australia",
Capital = "Canberra"
}
};
}
/// <Summary>Gets or sets Country and notifies for changes</Summary>
public ObservableCollection<Country> CountryView
{
get
{
return this.countryView;
}
set
{
if (this.countryView != value)
{
this.countryView = value;
this.OnPropertyChanged(() => this.CountryView);
}
}
}
}
Code Explanation
An ObservbleCollection instance is created and the values are assigned to generate the content of the ListBoxItems(ListBox.ItemSources).
Call the UserControl From MainWindow
Finally!!! Call the UserControl from the MainWindow so that the MainWindow acts as a parent and ListBox Control as a Child.
<Window
x:Class="DragDropWithinParentBoundaries.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DragDropWithinParentBoundaries.CustomControl"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="400"
Height="250"
mc:Ignorable="d">
<Grid>
<!--Call the UserControl From MainWindow-->
<local:DragDropCustomControl
Width="300"
Height="150"
Margin="20"/>
</Grid>
</Window>
Run the Application
We must call the UserControl from the MainWindow so that the MainWindow acts as a parent and ListBox Control as Child.
Conclusion
In this article, we discussed how to restrict the drag visual within the parent bounds using Telerik behavior and illustrated this through an example. I hope that this article reveals some of the mystery behind drag and drop.