Drag and drop in your controls¶
The drag and drop of the designer and the field list is built on reusable attached properties (namespace EasyBI.Wpf.DragAndDrop,
XAML prefix bi:), usable on any ItemsControl of an application. Being attached properties, they can be set
from a style:
<!-- Items can be dragged: a preview follows the pointer, a moved item is dimmed -->
<ListBox ItemsSource="{Binding Fields}" bi:DragSource.IsEnabled="True" bi:DragSource.AllowedEffects="Copy" />
<!-- Drops are decided by an IDropHandler (usually the view-model); an insertion line shows where the item goes -->
<ItemsControl ItemsSource="{Binding Columns}" bi:DropTarget.Handler="{Binding}" bi:DropTarget.Key="Columns" />
public sealed class MyViewModel : IDropHandler
{
public DragDropEffects DragOver(DropInfo drop) => drop.Item is Field ? DragDropEffects.Copy : DragDropEffects.None;
public void Drop(DropInfo drop) => Columns.Insert(drop.Index, ((Field)drop.Item!).ToColumn());
public string? RefusalReason(DropInfo drop) => null;
}
DropInfogives the draggedItem, the target'sKey(one handler for several targets), the insertionIndexamong the target's items and the rawDatafor drags coming from elsewhere.DropTarget.IsDragOveris true while an accepted drag is over the target (style triggers);DropTarget.Orientationswitches the insertion rules to a vertical stack.- A handler may explain a refusal (
IDropHandler.RefusalReason, null to say nothing): the reason is shown under the target and set on it asDropTarget.Refusalwhile the drag is over it. - A scrollable target, or its scroll viewer, scrolls when the pointer nears its edges (
AutoScroller, also usable on its own);InsertionIndexholds the pure insertion rules. - Style keys
DragSource.PreviewStyleKey(aContentControl;DragSource.PreviewTemplatesets the content),DropTarget.InsertionLineStyleKey(aBorder) andDropTarget.RefusalStyleKey(aContentControlholding the reason).
In the designer, fields are dropped between chips, chips are reordered within a shelf, and dimensions move between
Axis and Legend keeping their granularity. A refused field says why (DesignerViewModel.RefusalReason): only measures
in Values, only dimensions elsewhere, a field already on the shelf, a full axis, a field of another model.