Drag a control in UWP using Caluburn Micro

In my application, today I’ve the need to drag a control in the universal app that I’m building. I use Caliburn Micro to implement the MVVM pattern. My base implementation is as folow.

First of all you need to install Caliburn Micro in your UWP project. You can follow the step described in this article.

Now you have a new project with a MainView.xaml and a MainViewModel.cs in theis folders.

To have a sample view where operate, open the MainView.xaml and build a grid with one or more Border in the view. To best perform Caliburn Micro framework, is better have an abstract base class to implement in the view model


[code lang=”c-sharp”]
public abstract class ViewModelBase : Screen, IViewAware
{
public readonly INavigationService navigationService;
public static object Parameters { get; private set; }
protected ViewModelBase(INavigationService navigationService)
{
this.navigationService = navigationService;
navigationService.Navigating += NavigationService_Navigating;
}

private void NavigationService_Navigating(object sender, NavigatingCancelEventArgs e)
{
Parameters = e.Parameter;
}

public void GoBack()
{
navigationService.GoBack();
}

public bool CanGoBack
{
get
{
return navigationService.CanGoBack;
}
}
}
[/code]

This abstract class have to be implemented in our ViewModel

[code lang=”c-sharp”]
public class MainViewModel : ViewModelBase
{
#region Properties and Private fields
static WinRTContainer container = (Application.Current as App).container;
private readonly INavigationService _navigationService;
private readonly IEventAggregator _eventAggregator;
#endregion

#region Constructors
public MainViewModel(INavigationService navigationService) : base(navigationService)
{
_eventAggregator = container.GetInstance<IEventAggregator>();
_eventAggregator.Subscribe(this);
_navigationService = navigationService;
}
#endregion
}
[/code]

Now we can begin to build a simple view with some Borders to drag. To have a simple view to manipulate, I’ve add four square Borders, with a textblock inside, to drag.

DragControl
Preview

At this point need to add the event to manipulate.

Pointers

in the UWP there is a new concept linked to the input device ad mouse, touch, controllers (for XBox)… etc. Manage these large quantity of devices, is very complicate. But the new UWP introduced a new concept: the “Pointers”. The pointers manages all kind of devices with simple common interfaces.

But what are the interested events for the drag? here the list with the explains.

  1. PointerMoved: is the event that allows us to get the informations about the movement of the pointer on the control
  2. PointerPressed: is the event that allows us to get the information when the pointer is pressed on the control
  3. PointerReleasedis the event that allows us to get information when the pointer is released on the control

here the code vot a single Border that I’ve used

[code lang=”xml”]<Border x:Name="buttonA" HorizontalAlignment="Left" Margin="86,95,0,0" VerticalAlignment="Top" Height="80" Width="80" cal:Message.Attach="[Event PointerReleased] = [Action PointerReleased()]; [Event PointerMoved] = [Action PointerMoved($source,$eventArgs)]; [Event PointerPressed] = [Action buttonPointerPressed($source,$eventArgs)]" Background="#FFC8C8C8" >
<TextBlock Text="1" HorizontalAlignment="Center" VerticalAlignment="Center" Height="28" Margin="0" Width="20" MinWidth="4" MinHeight="1" SelectionHighlightColor="{x:Null}" />
</Border>[/code]

Take note to the multi caliburn events attached on the single control.

And now in the ViewModel add five variables:

[code lang=”xml”]double xPos = 0;
double yPos = 0;
double xGap = 0;
double yGap = 0;
bool press = false; [/code]

And the callbacks for each events:

[code lang=”c-sharp”]private void PointerReleased()
{
press = false;
}

private void buttonPointerPressed(object sender, PointerRoutedEventArgs e)
{
bool _hasCapture = ((Border)sender).CapturePointer(e.Pointer);
var s = _hasCapture;
var b = (Border)sender;
xPos = b.Margin.Left;
yPos = b.Margin.Right;
press = true;
}

private void PointerMoved(object sender, PointerRoutedEventArgs e)
{
var v = GetView() as MainView;
var b = (Border)sender;
if (press)
{
var p = e.GetCurrentPoint(v.MainGrid);
xPos = p.Position.X – xGap – (b.ActualHeight / 2);
yPos = p.Position.Y – yGap – (b.ActualWidth / 2);
b.Margin = new Thickness(xPos, yPos, b.Margin.Right, b.Margin.Bottom);

}
}
[/code]

Enjoy and stay tuned!