3

I've implemented a little WPF app to drag an animal from the shop in to my barn. ;-)

I'm using:

  • MVVMlight and its EventToCommand behaviors
  • The animal is a simple Border listening on MouseDown
  • The barn is an ItemsControl bound to an ObservableCollection of the ViewModel. It listens to 3 events:
    • DragEnter (adds a dummy animal into the ObservableCollection to preview where the animal is placed in the barn)
    • DragLeave (removes the dummy animal if the animal isn't dropped)
    • Drop (sets the animal to the designated place in the barn).

Sorry. I don't have the reputation to upload images. So I try some ascii arts (try to image dragging the donkey into the barn):

Shop   | Barn
-------------------------
Mouse  | [Mouse] [Dog] 
Cat    | [.]
Dog    |  ^
Donkey-|--|

Here's the problem:

If I drag an animal into the barn onto it's designated space (the preview dummy):

  1. DragLeave removes the dummy item and immediately the
  2. DragEnter will be fired to redraw the dummy item. Goto step 1.

An inifite loop. The flickering is up and running!

Does anyone know a nice little workaround?

So here's the code of my ViewModel:

private string _dummy;
public ObservableCollection<string> Animals { get; private set; }

private void StartDrag(FrameworkElement element)
{
    var animal = element.DataContext as string;
    if (animal == null) return;
    System.Windows.DragDrop.DoDragDrop(element, animal, DragDropEffects.Copy);
}

private void PreviewDrop(DragEventArgs args)
{
    if (args.Data.GetDataPresent(typeof (string)))
        AddDummy();
}

private void StopDrop(DragEventArgs args)
{
    RemoveDummy();
}

private void Drop(DragEventArgs args)
{
    var animal = args.Data.GetData(typeof (string)) as string;
    if (animal == null) return;

    RemoveDummy();
    Animals.Add(animal);
}

private void RemoveDummy()
{
    if (_dummy == null) return;
    Animals.Remove(_dummy);
    _dummy = null;
}

private void AddDummy()
{
    if (_dummy != null) return;
    _dummy = ".";
    Animals.Add(_dummy);
}

Thanks for your help

Marcel

Marcel
  • 1,002
  • 2
  • 16
  • 37
  • possible duplicate of [DragDrop - DragEnter/DragLeave Events keep firing](http://stackoverflow.com/questions/2632821/dragdrop-dragenter-dragleave-events-keep-firing) – G.Y Aug 11 '15 at 19:30

2 Answers2

3

Ok, I got it. IsHitTestVisible does the trick! Here are 2 solutions:

First approach, the quick way: Just set all animals in the barn (by modifing the ItemTemplate in the target ItemsControl) to IsHitTestVisible="False":

<ItemsControl.ItemTemplate>
    <DataTemplate DataType="{x:Type system:String}">
        <Border Background="Gray" IsHitTestVisible="False">
            <TextBlock Text="{Binding}" Foreground="White" />
        </Border>
    </DataTemplate>
</ItemsControl.ItemTemplate>

So each item is still visible but no event is fired by the items nor the ItemsControl. But wait! What if I want to replace an animal by dragging a new one onto the old one??? I will need at least a proper Drop event?

Second approach, the better way: I set IsHitTestVisible="False" only to the dummy animal (here I compare the string content against "." to check if it's a dummy).

<ItemsControl.ItemTemplate>
    <DataTemplate DataType="{x:Type system:String}">
        <Border Name="AnimalPlace" Background="Gray">
            <TextBlock Text="{Binding}" Foreground="White" />
        </Border>
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding}" Value=".">
                <Setter TargetName="AnimalPlace" Property="IsHitTestVisible" Value="False" />
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>
</ItemsControl.ItemTemplate>

The last approach is suitable for the MVVM aspects and fits my needs perfectly. Not to mention that it can be adapted easily for more complex objects (more complex than a string to represent an animal).

I'am happy now :-)

Marcel
  • 1,002
  • 2
  • 16
  • 37
  • Thanks for pointing that out - I've just wasted hours trying to work around the DragLeave fired by my Canvas every time I mouse over my "drop point marker". Now I've set IsHitTestVisible to false on my marker control and there's no longer a DragLeave event to work around! – Jon Aug 10 '14 at 11:38
0

Simple solution, you might just need to use flags to control the DragLeave and DragEnter code execution. Check Here to understand the sequence of events

Community
  • 1
  • 1
sayed saad
  • 672
  • 7
  • 12
  • I know the article. But the problem is: `DragLeave` is fired before `DragEnter`. Even if I would set flags inside `DragEnter` there aren't available in `DragLeave`. So I cannot distinguish if `DragLeave` is fired due to the mouse leaves the `ItemsControl` or enters one of its child items. – Marcel Nov 11 '13 at 18:35