I'm new to your DragAndDropManager class and have been reading up on it lately. I'm able to get the samples working without any problem and have been trying to adjust some of the sample code to meet a need I have. The need is this. I have a ListBox (A) with Bird objects within it and a ListBox (B) with Dinosaurs in it. Both list's items can be dragged within their same list to reorder them, however I want to prevent items from ListBox (A) being dropped onto ListBox (B). Now, I can determine the type of object in the destination list during the DropQuery event handler, but I can't seem to figure out how to prevent the OnDropInfo event from firing.
Additionally, I can prevent the Item from ListBox (A) from being added to ListBox (B) in the OnDropInfo event (based on some typeof() logic), however at this point the Item from ListBox (A) has already been removed from ListBox (A).
I would, ideally, like to be able to set the status of the DragDropQuery event args to DropImpossible when I try to drag an Item from a ListBox whose Items are of a different than the destination.
Does this make sense?
Thanks,
- Scott
7 Answers, 1 is accepted
Hello Scott McEachern,
You can find a wide arrange of topics on Dran and Drop on our online documentation page. For that particular problem, you could use the OnDropQuery method and cancel the drop operation is the drop is invalid. For example:
// ListBox (a) OnDropQueryprivatevoidOnDropQuery(objectsender, DragDropQueryEventArgs e ){var payload = e.Options.PayloadasBird;if(payload ==null) {e.QueryResult =false;}else{e.QueryResult =true;}}
Greetings,
Milan
the Telerik team
Thanks for the quick response. I have added the logic that checks the data type of the payload object against the data type of the objects in the destination list within the OnDropQuery event, however the OnDropInfo still fires. I have ensured that e.QueryResult get's set to false in this condition.
I guess I'm confused about the purpose of the Handled property and the QueryResult property on the DragDropQueryEventArgs object. What behavior is expected when their values change?
Setting the QueryResult property (in OnDropQuery) to false doesn't prevent the OnDropInfo method from firing when the user releases the mouse when trying to drop a Bird object (A) into ListBox of Dinosaurs (B).
Q: Is it necessary to place this same conditional logic within the OnDropInfo event to prevent the item from being added to the list? If that is true, then I would argue that it is pointless to check for this condition in the OnDropQuery method, per your suggestion.
I would like to know if it is possible to deny the Drop (and NOT remove the object from List (A)) during a failed Drop AND prevent the OnDropInfo event from firing when the object and its destination list object are not the same object. Does this make sense?
I know this is possible, but I'm just not sure how Telerik would have intended for it to happen.
Here's my XAML:
<UserControl x:Class="DragDropListBoxPrototype.MainPage3" xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows" xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls" xmlns:telerikDragDrop="clr-namespace:Telerik.Windows.Controls.DragDrop;assembly=Telerik.Windows.Controls" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <UserControl.Resources> <Style TargetType="Control" x:Key="DraggableItem"> <Setter Property="telerikDragDrop:RadDragAndDropManager.AllowDrag" Value="True" /> <Setter Property="telerikDragDrop:RadDragAndDropManager.AllowDrop" Value="True" /> </Style> <Style TargetType="ListBox" x:Key="DraggableListBox"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListBox"> <Border x:Name="ListBoxBorder" CornerRadius="2" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <vsm:VisualStateManager.VisualStateGroups> <vsm:VisualStateGroup x:Name="DragCue"> <vsm:VisualState x:Name="NoDrop" /> <vsm:VisualState x:Name="DropPossible"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="DropCueElement" Storyboard.TargetProperty="(UIElement.Visibility)"> <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Visible" /> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ListBoxBorder" Storyboard.TargetProperty="(Border.BorderBrush)"> <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Orange" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </vsm:VisualState> <vsm:VisualState x:Name="DropImpossible" /> </vsm:VisualStateGroup> </vsm:VisualStateManager.VisualStateGroups> <Grid> <ScrollViewer x:Name="ScrollViewer" Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" BorderBrush="Transparent" BorderThickness="0" telerik:ScrollViewerExtensions.EnableMouseWheel="True"> <ItemsPresenter x:Name="ItemsPresenterElement" /> </ScrollViewer> <Grid x:Name="DropCueElement" HorizontalAlignment="Stretch" VerticalAlignment="Top" IsHitTestVisible="False" telerikDragDrop:RadDragAndDropManager.AllowDrop="True" Visibility="Collapsed" Margin="-2."> <Ellipse Width="5" Height="5" Fill="DarkSlateGray" HorizontalAlignment="Left" IsHitTestVisible="False" /> <Ellipse Width="5" Height="5" Fill="DarkSlateGray" HorizontalAlignment="Right" /> <Rectangle Height="3" VerticalAlignment="Center" IsHitTestVisible="False" Fill="DarkSlateGray" /> </Grid> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> <Setter Property="telerikDragDrop:RadDragAndDropManager.AllowDrop" Value="True" /> <Setter Property="ItemContainerStyle" Value="{StaticResource DraggableItem}" /> </Style> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White"> <ListBox x:Name="birdsListBox" Margin="8,8,0,8" DisplayMemberPath="Name" Style="{StaticResource DraggableListBox}" HorizontalAlignment="Left" Width="400"/> <ListBox x:Name="dinoListBox" Margin="0,8,8,8" DisplayMemberPath="Name" Style="{StaticResource DraggableListBox}" HorizontalAlignment="Right" Width="400"/> </Grid></UserControl>Here's my code. Its pretty straight forward.
public partial class MainPage3 : UserControl { public class Bird { public string Name { get; set; } } public class Dinosaur { public string Name { get; set; } } public MainPage3() { InitializeComponent(); birdsListBox.ItemsSource = GetBirds(); dinoListBox.ItemsSource = GetDinos(); //- Wire up drag drop for birds RadDragAndDropManager.AddDropQueryHandler(birdsListBox, new EventHandler<DragDropQueryEventArgs>(OnDropQuery)); RadDragAndDropManager.AddDragQueryHandler(birdsListBox, new EventHandler<DragDropQueryEventArgs>(OnDragQuery)); RadDragAndDropManager.AddDropInfoHandler(birdsListBox, new EventHandler<DragDropEventArgs>(OnDropInfo)); RadDragAndDropManager.AddDragInfoHandler(birdsListBox, new EventHandler<DragDropEventArgs>(OnDragInfo)); //- Wire up drag drop for dinos RadDragAndDropManager.AddDropQueryHandler(dinoListBox, new EventHandler<DragDropQueryEventArgs>(OnDropQuery)); RadDragAndDropManager.AddDragQueryHandler(dinoListBox, new EventHandler<DragDropQueryEventArgs>(OnDragQuery)); RadDragAndDropManager.AddDropInfoHandler(dinoListBox, new EventHandler<DragDropEventArgs>(OnDropInfo)); RadDragAndDropManager.AddDragInfoHandler(dinoListBox, new EventHandler<DragDropEventArgs>(OnDragInfo)); } // OnDragQuery event handler private void OnDragQuery(object sender, DragDropQueryEventArgs e) { var listBoxItem = e.Options.Source as ContentControl; if (e.Options.Status == DragStatus.DragQuery && listBoxItem != null) { var sourceControl = e.Options.Source; var dragCue = RadDragAndDropManager.GenerateVisualCue(sourceControl); dragCue.HorizontalAlignment = HorizontalAlignment.Left; dragCue.Content = sourceControl.DataContext; dragCue.ContentTemplate = listBoxItem.ContentTemplate; e.Options.DragCue = dragCue; e.Options.Payload = new DragDropOperation() { Payload = sourceControl.DataContext }; } e.QueryResult = true; e.Handled = true; } // OnDragInfo event handler private void OnDragInfo(object sender, DragDropEventArgs e) { var listBox = e.Options.Source.FindItemsControlParent() as ItemsControl; var operation = e.Options.Payload as DragDropOperation; if (e.Options.Status == DragStatus.DragComplete) { listBox = e.Options.Source.FindItemsControlParent() as ItemsControl; var itemsSource = listBox.ItemsSource as IList; operation = e.Options.Payload as DragDropOperation; itemsSource.Remove(operation.Payload); } } // OnDropQuery event handler private void OnDropQuery(object sender, DragDropQueryEventArgs e) { var destination = e.Options.Destination; var operation = e.Options.Payload as DragDropOperation; if (e.Options.Status == DragStatus.DropDestinationQuery && destination is ListBoxItem && operation != null) { var listBox = destination.FindItemsControlParent() as ListBox; // Cannot place an item relative to itself: if (e.Options.Source == e.Options.Destination) { return; } // Get the spatial relation between the destination item and the vis. root: var destinationTopLeft = destination.TransformToVisual(null).Transform(new Point()); // Should the new Item be moved before or after the destination item?: bool placeBefore = (e.Options.CurrentDragPoint.Y - destinationTopLeft.Y) < destination.ActualHeight / 2; operation.DropPosition = placeBefore ? DropPosition.Before : DropPosition.After; e.QueryResult = true; e.Handled = true; } if (e.Options.Status == DragStatus.DropDestinationQuery && destination is ListBox && operation != null) { var listBox = destination as ListBox; var itemsCollection = listBox.ItemsSource.Cast<object>(); // Check to see if the object being dragged matches the type of objects in the destination list Type type = listBox.ItemsSource.GetType().GetProperty("Item").PropertyType; if (type != operation.Payload.GetType()) { // Deny drag drop here some how? e.QueryResult = false; e.Handled = true; return; } // Cannot drop the last or only item of the list box within the same list box: if (listBox.ItemsSource != null && (!itemsCollection.Any() || itemsCollection.Last() != operation.Payload)) { e.QueryResult = true; e.Handled = true; } else { e.QueryResult = false; e.Handled = true; } } } // OnDropInfo event handler private void OnDropInfo(object sender, DragDropEventArgs e) { var destination = e.Options.Destination; if (e.Options.Status == DragStatus.DropPossible && destination is ListBoxItem) { var listBox = destination.FindItemsControlParent() as ListBox; VisualStateManager.GoToState(listBox, "DropPossible", false); // Get the DropCueElement: var dropCueElement = (VisualTreeHelper.GetChild(listBox, 0) as FrameworkElement).FindName("DropCueElement") as FrameworkElement; var operation = e.Options.Payload as DragDropOperation; // Get the parent of the destination: var visParent = VisualTreeHelper.GetParent(destination) as UIElement; // Get the spatial relation between the destination and its parent: var destinationStackTopLeft = destination.TransformToVisual(visParent).Transform(new Point()); var yTranslateValue = operation.DropPosition == DropPosition.Before ? destinationStackTopLeft.Y : destinationStackTopLeft.Y + destination.ActualHeight; dropCueElement.RenderTransform = new TranslateTransform() { Y = yTranslateValue }; e.Handled = true; } if (e.Options.Status == DragStatus.DropPossible && destination is ListBox) { var listBox = destination as ListBox; VisualStateManager.GoToState(listBox, "DropPossible", false); // Get the DropCueElement: var dropCueElement = (VisualTreeHelper.GetChild(listBox, 0) as FrameworkElement).FindName("DropCueElement") as FrameworkElement; var operation = e.Options.Payload as DragDropOperation; // Get the size of the items: var itemsPresenter = listBox.GetTemplateChild<FrameworkElement>("ItemsPresenterElement"); var panel = VisualTreeHelper.GetChild(itemsPresenter, 0) as Panel; if (panel != null) { var yTranslateValue = panel.ActualHeight; dropCueElement.RenderTransform = new TranslateTransform() { Y = yTranslateValue }; } } // Hide the DropCue: if (e.Options.Status == DragStatus.DropImpossible || e.Options.Status == DragStatus.DropCancel || e.Options.Status == DragStatus.DropComplete) { var listBox = destination as ListBox; if (listBox == null) { listBox = e.Options.Destination.FindItemsControlParent() as ListBox; } VisualStateManager.GoToState(listBox, "DropImpossible", false); } // Place the item: if (e.Options.Status == DragStatus.DropComplete && destination is ListBoxItem) { var listBox = e.Options.Destination.FindItemsControlParent() as ListBox; var itemsSource = listBox.ItemsSource as IList; var destinationIndex = itemsSource.IndexOf(e.Options.Destination.DataContext); var operation = e.Options.Payload as DragDropOperation; var insertIndex = operation.DropPosition == DropPosition.Before ? destinationIndex : destinationIndex + 1; itemsSource.Insert(insertIndex, operation.Payload); listBox.Dispatcher.BeginInvoke(() => { listBox.SelectedIndex = insertIndex; }); } if (e.Options.Status == DragStatus.DropComplete && destination is ListBox) { var listBox = destination as ListBox; var itemsSource = listBox.ItemsSource as IList; var operation = e.Options.Payload as DragDropOperation; itemsSource.Add(operation.Payload); listBox.Dispatcher.BeginInvoke(() => { listBox.SelectedIndex = itemsSource.Count-1; }); } } public static ObservableCollection<Bird> GetBirds() { ObservableCollection<Bird> items = new ObservableCollection<Bird>(); items.Add(new Bird() { Name = "Meadow Lark"}); items.Add(new Bird() { Name = "Crow"}); items.Add(new Bird() { Name = "Sparrow"}); items.Add(new Bird() { Name = "Canary"}); items.Add(new Bird() { Name = "Robin"}); items.Add(new Bird() { Name = "Oriole"}); items.Add(new Bird() { Name = "Pheasant"}); items.Add(new Bird() { Name = "Magpie"}); items.Add(new Bird() { Name = "Cardinal"}); items.Add(new Bird() { Name = "Parrot"}); items.Add(new Bird() { Name = "Finch"}); items.Add(new Bird() { Name = "Raven"}); items.Add(new Bird() { Name = "French Hen"}); items.Add(new Bird() { Name = "Duck"}); items.Add(new Bird() { Name = "Cornish Game Hen"}); items.Add(new Bird() { Name = "Chicken"}); items.Add(new Bird() { Name = "Ostrich"}); items.Add(new Bird() { Name = "Roadrunner" }); return items; } public static ObservableCollection<Dinosaur> GetDinos() { ObservableCollection<Dinosaur> items = new ObservableCollection<Dinosaur>(); items.Add(new Dinosaur() { Name = "T-Rex" }); items.Add(new Dinosaur() { Name = "Triceritops"}); items.Add(new Dinosaur() { Name = "Brontosarus"}); items.Add(new Dinosaur() { Name = "Velociraptor"}); items.Add(new Dinosaur() { Name = "Alligator"}); items.Add(new Dinosaur() { Name = "Brachiasaurus"}); items.Add(new Dinosaur() { Name = "Stegasauraus"}); items.Add(new Dinosaur() { Name = "Pteridactyl"}); return items; }In this event handler I can access the destination of the Drop (meaning, the place where the user intends to drop their object). I can then check to see if this destination object and the payload object are the same object and can then deny the drag / drop process from here. However, the DragDropEventArgs object doesn't contain a QueryResult object that can be set to "false" (I'm still not sure what this does anyway), and I can't explicitly set the DragStatus to DropImpossible as I would like to.
// OnDragInfo event handlerprivate void OnDragInfo( object sender, DragDropEventArgs e ){ // if we are dropping on the appropriate listbox, then remove the item from the first listbox. if ( e.Options.Status == DragStatus.DragComplete ) { var itemsControl = e.Options.Source.FindItemsConrolParent() as ItemsControl; var itemsSource = itemsControl.ItemsSource as IList; itemsSource.Remove( e.Options.Payload ); }}I know that I am probably going about this in a manner that wasn't intended, but the object structure and the pattern employed are confusing to me. The documentation doesn't offer much, either in the way of explaining the nature of the properties of the EventArgs objects.
Please let me know if what I am asking for is prohibited and if there is a proper way of going about this.
Thanks
DragCancel - the source is notified that the DragDrop has been cancelled. For example, the user may press Esc while dragging to cancel the event or the object may be released over a target that does not accept it.
I should add that I'd like to know I can have the target list "not accept" an object of another type. I think this might be the best approach. I see no documentation explaining how or when this two-way handshake process occurrs.
Thanks,
- Scott
I have checked the code sent and found that indeed the drop operation is executed when dragging between the two listboxes. The reason for that is the code in the DropQuery - it check for payload type only when destination is Listbox, but not when destination is Listboxitem. Adding the code in this case result in correct behavior.
About the other questions I will try to summarize the drag drop mechanism in few words:
- dragquery and dropquery are raised before draginfo and dropinfo to check whether the current operation is allowed or not. This is done by using QueryResult. If the result is false the corresponding info method is not called.
- The progress of the drag operations can be tracked through Options.Status property. It cannot be changed from outside, since it reflects the final result from the mouse position, state and query results in the drag/drop query methods. As an example, I have added status textblock that displays the dragstatus in DragInfo method. it will display DropPossible,DropImpossible,DragComplete,DragCancel depending on current drag state.
More information about the events and statuses can be found in these help articles:
http://www.telerik.com/help/silverlight/raddraganddrop-events-overview.html
http://www.telerik.com/help/silverlight/raddragdrop-events-eventargs.html
Also, you can check the attached project using the code posted earlier in the thread.
Let us know if you have any further questions.
Tsvyatko
the Telerik team
That adjustment made a big difference. I didn't realize that I had to check for both ListBox and ListBoxItems. It seems as though checking for a ListBox only should be sufficient, but now it seems that ListBoxItems are almost always found to be the drop destination in this example. In what cases would the ListBox be found? When there are no items in the list?
Thanks again, this is working now.
Thanks,
- Scott
I am glad that all is fine now. You are correct, the ListBox will be found when you are dropping on empty area.
Regards,
Milan
the Telerik team