Technology Experience
.NET World

Gestire lo swipe nelle quattro direzioni su un qualsiasi elemento della UI

Vi piacerebbe poter gestire lo swipe verso sinistra/destra/alto/basso, su qualsiasi elemento della UI, e sfruttando ovviamente Model-View-ViewModel, per gli amici MVVM? Ovvero, poter scrivere un blocco di XAML così?

<Border Background="Cyan" Width="500" Height="200"
        helper:SwipeCommandHelper.Up="{Binding IncrementCommand}"
        helper:SwipeCommandHelper.Down="{Binding DecrementCommand}"
        helper:SwipeCommandHelper.Right="{Binding MultiplyCommand}"
        helper:SwipeCommandHelper.Left="{Binding DivideCommand}" />

Al Border qui sopra ho dato la possibilità di eseguire un command differente in base a quale tipo di swipe l’utente fa sopra il Border stesso. Se scorro il dito verso l’alto, eseguo l’IncrementCommand. Se lo scorro verso destra, eseguo il MultiplyCommand. E così via. La cosa bella è che la classe SwipeCommandHelper può agganciarsi a qualsiasi oggetto FrameworkElement.

Il codice è abbastanza semplice.

public static class SwipeCommandHelper
{
    public static ICommand GetUp(DependencyObject obj)
    {
        return (ICommand)obj.GetValue(UpProperty);
    }

    public static void SetUp(DependencyObject obj,
        ICommand value)
    {
        obj.SetValue(UpProperty, value);
    }

    public static readonly DependencyProperty UpProperty =
        DependencyProperty.RegisterAttached("Up",
        typeof(ICommand),
        typeof(SwipeCommandHelper),
        new PropertyMetadata(null,
        new PropertyChangedCallback(Setup)));
}

Il codice qui sopra è una semplificazione, e riporta solo la dependency property per gestire lo swipe verso l’alto. La dependency property è UpProperty, ed è di tipo ICommand. Il valore predefinito è null, e quando il suo valore viene modificato viene invocato il metodo Setup(). Questo metodo fa un po’ di code, ma il succo del discorso è che si aggancia all’evento ManipulationStarted del controllo, in modo tale che quando l’utente farà qualche gesture noi potremo intercettare questo evento ed eseguire il command associato. Il codice del metodo Setup() è il seguente:

private static void Setup(DependencyObject obj,
            DependencyPropertyChangedEventArgs e)
{
    FrameworkElement ctl = obj as FrameworkElement;

    if (ctl != null)
    {
        ICommand oldValue = (ICommand)e.OldValue;
        ICommand newValue = (ICommand)e.NewValue;

        if (oldValue == null && newValue != null)
        {
            {
                if (GetAttached(ctl) == false)
                {
                    ctl.ManipulationMode = ManipulationModes.TranslateX |
                        ManipulationModes.TranslateY;
                    SetAttached(ctl, true);
                    ctl.ManipulationCompleted += ctl_ManipulationCompleted;
                }
            }
        }
        else if (oldValue != null && newValue == null)
        {
            ctl.ManipulationMode = ManipulationModes.None;
            ctl.SetValue(AttachedProperty, false);
            ctl.ManipulationCompleted -= ctl_ManipulationCompleted;
        }
    }
}

Aggiungo due note importanti: per default, in Windows 8 su un oggetto sulla UI non si possono intercettare gesture. Con Windows Phone, per esempio, è sufficiente sottoscrivere l’evento ManipulationCompleted ed il gioco è fatto. Con Windows 8, invece, prima dovete impostare il ManipulationMode, poi sottoscrivete l’evento. Per default, ManipulationMode è None, e quindi anche se impazzite a fare gesture sopra l’oggetto, nel codice non accadrà nulla. Ma non abbiate paura: ci pensa il mio codice a fare tutto ciò: non appena associate un command ad uno dei quattro tipi di swipe, vado automaticamente ad impostare il ManipulationMode su TranslateX | TranslateY.

A questo punto non ci resta che scrivere il codice dell’evento ManipulationCompleted:

static void ctl_ManipulationCompleted(object sender,
            ManipulationCompletedRoutedEventArgs e)
{
    var element = sender as FrameworkElement;
    ICommand command = null;

    // x < 0 --> verso sinistra
    // x > 0 --> verso destra
    var x = e.Cumulative.Translation.X;

    // y < 0 --> verso l'alto
    // y > 0 --> verso il basso
    var y = e.Cumulative.Translation.Y;

    if (y < 0 && Math.Abs(y) > Math.Abs(x))
    {
        command = GetUp(element);
    }
    else if (y > 0 && Math.Abs(y) > Math.Abs(x))
    {
        command = GetDown(element);
    }
    else if (x > 0 && Math.Abs(x) > Math.Abs(y))
    {
        command = GetRight(element);
    }
    else if (x < 0 && Math.Abs(x) > Math.Abs(y))
    {
        command = GetLeft(element);
    }

    if (command != null)
    {
        if (command.CanExecute(null))
        {
            command.Execute(null);
        }
    }
}

All’interno dell’evento posso capire quale tipo di gesture ha fatto l’utente sul controllo. Avendo impostato il ManipulationMode solamente alle traslazioni X e Y, sappiamo per forza di cose che arriveremo qui solo in presenza di questa gesture. Capisco la direzione del movimento seguendo due fattori:

  • il valore di x e y, ovviamente
  • quale dei due valori x e y ha più rilevanza l’uno con l’altro (spostando il dito verso l’alto, probabilmente lo sposterete anche a sinistra o a destra – grazie a Math.Abs() capisco quale asse ha avuto più rilevanza nella gesture)

I base alla direzione, ottengo l’ICommand associato. Ovviamente può anche essere null, nel caso in cui non abbia bindato alcun command. Se invece c’è, invoco il CanExecute, ed in caso di esito positivo, eseguo finalmente il comando associato.

Si potrebbe pensare di aggiungere il CommandParameter per ciascuna delle quattro direzioni, ma a me non serve, per cui lo lascio fare voi!

Il codice completo della classe pronta all’uso è qui. E’ stata scritta in circa 30 minuti, ci sarà sicuramente qualche bug: avvisatemi e correggiamola/miglioriamola insieme!

Send to Kindle

Igor Damiani

La sua passione per l'informatica nasce nella prima metà degli anni '80, quando suo padre acquistò un Texas Instruments TI-99. Da allora ha continuato a seguire l'evoluzione sia hardware che software avvenuta nel corso degli anni. E' un utente, un videogiocatore ed uno sviluppatore software a tempo pieno. Igor ha lavorato e lavora anche oggi con le più moderne tecnologie Microsoft per lo sviluppo di applicazioni: .NET Framework, XAML, Universal Windows Platform, su diverse piattaforme, tra cui spiccano Windows 10 piattaforme mobile. Numerose sono le app che Igor ha creato e pubblicato sul marketplace sotto il nome VivendoByte, suo personale marchio di fabbrica. Adora mantenere i contatti attraverso Twitter e soprattutto attraverso gli eventi delle community .NET.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.