The Problem
Handling just about anything in code-behind is bad (generally speaking) and can very easily create tightly coupled dependencies. Handling events, like drag+drop, in code-behind makes it even easier to produce these dependencies.
So how do you handle drop events if it's not in the code-behind?
The Solution (sample code)
Quick FYI - I just figured this out and wanted to share! There are most likely better ways to handle or go about solving this, so let me know in the comments if you know any.
I find examples to be the best way for me to explain a concept. Let's say you have a UserControl
, we'll call it MyView
, that has the initial drop event defined. We also have the main window class (MainWindow
) that will have MyView
as a child element . In this example the MainWindow
will listen to the Drop
event of MyView
and subsequently call a method of it's ViewModel
(MainWindowViewModel
) to handle the logic.
Keep in mind that the point of this tip/trick is only to show how to enable listening to an event that uses DragEventArgs (like Drop or DragEnter). Where you actually listen from is up to you.
UserControl MyView:
MyView.xaml
<UserControl x:Class="MyProject.Views.MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="uc_View">
<Grid>
<ListBox Name="lbxView" AllowDrop="True" Drop="lbxView_Drop">
</ListBox>
</Grid>
</UserControl>
Key code:
AllowDrop
basically enables the drag/drop.
Drop
is where we give the method name that will be called on a drop event.
MyView.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
namespace MyProject.Views
{
public partial class MyView : UserControl
{
public event EventHandler<DragEventArgs> ObjectDropped;
protected virtual void OnObjectDropped(DragEventArgs e)
{
ObjectDropped?.Invoke(this, e);
}
private void lbxView_Drop(object sender, DragEventArgs e)
{
OnObjectDropped(sender, e);
}
}
}
Key code:
EventHandler<>
we need to use the template version of the EventHandler
in order for this to work. This is also the public handle for other objects to add their listening method.
lbxView_Drop
is fired on the drop event and essentially calls the listening delegates.
Window MainWindow:
MainWindow.xaml
<Window x:Class="MyProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject"
xmlns:localViews="clr-namespace:MyProject.Views"
Title="MyApp" Height="500" Width="720">
<Grid>
<localViews:MyView x:Name="myView" ObjectDropped="MyView_ObjectDropped"/>
</Grid>
</Window>
Key code:
ObjectDropped
is that public event handle I mentioned earlier. This is where you pass in the listening method name.
MainWindow.xaml.cs
using System.Windows;
using MyProject.ViewModels;
namespace MyProject
{
public partial class MainWindow : Window
{
private MainWindowViewModel _viewModel;
private void MyView_ObjectDropped(object sender, DragEventArgs e)
{
_viewModel.ObjectDropped( sender, e );
}
}
}
There's not really any key code here and is mainly here for completeness. Yes, the handling of the ViewModel could be abstracted away, but that's out of the scope of this article.
Window MainWindow (alternate):
I strongly advise against this, but I know that what follows may be the best option for someone else.
The other way you can add the listener is to omit the ObjectDropped="MyView_ObjectDropped"
in MainWindow.xaml and add the listener from the MainWindow
's constructor.
MainWindow.xaml.cs (alternate)
using System.Windows;
namespace MyProject
{
public partial class MainWindow : Window
{
public MainWindow()
{
myView.ObjectDropped += new EventHandler<DragEventArgs>(MyView_ObjectDropped);
}
private void MyView_ObjectDropped(object sender, DragEventArgs e)
{
}
}
}