Permettere il Drag-and-drop solo su zone predefinite con Silverlight 2.0

di Marco Leoncini, in Silverlight 2.0,

Nello script #43 abbiamo visto come implementare un semplice sistema di Drag-and-drop mediante l'uso delle AttachedProperties.

Nello script di oggi vedremo come risolvere due dei limiti più grossi del precedente sistema.

Il primo assolutamente non trascurabile è che tale sistema funziona solo se utilizziamo dei Canvas per realizzare il layout della nostra applicazione, affidandoci alle proprietà Canvas.Left e Canvas.Top per la disposizione degli oggetti; infine è possibile trascinare l'elemento e rilasciarlo in qualsiasi parte dell'interfaccia, caratteristica che magari non è sempre desiderabile.

Per ovviare al primo inconveniente dobbiamo ripensare la filosofia del DragManager e applicare una TranslateTransform, invece che modificare la posizione rispetto al Canvas.

Per prima cosa aggiungiamo un nuovo metodo, GetTranslateTransform, che accetta come parametro un oggetto del tipo UIElement e restituisce se esistente l'attuale TranslateTransform, altrimenti ne crea una nuova e l'applica all'elemento.

private static TranslateTransform GetTranslateTransform(UIElement uielement)
{
   if (uielement == null) throw new ArgumentNullException("uielement");
   TranslateTransform _translateTransform = null;
   if (uielement.RenderTransform is TranslateTransform)
   {
      _translateTransform = (TranslateTransform)uielement.RenderTransform;
   }
   else if (uielement.RenderTransform is TransformGroup)
   {
      TransformGroup _transformGroup = (TransformGroup)uielement.RenderTransform;

      foreach (Transform item in _transformGroup.Children)
      {
         if (item is TranslateTransform)
         {
            _translateTransform = (TranslateTransform)item;
         }
      }
   }
   else
   {
      _translateTransform = new TranslateTransform();
      uielement.RenderTransform = _translateTransform;
   }
   return _translateTransform;
}

È con quest'oggetto TranslateTransform che modificheremo nell'eventhandler element_MouseMove al fine di muovere l'oggetto per lo schermo.

static void element_MouseMove(object sender, MouseEventArgs e)
{
   UIElement _element = (UIElement)sender;
   if (_isMouseCapured)
   {
      TranslateTransform _translateTranform = GetTranslateTransform(_element);

      //modifica la trasformazione per muovere l'oggetto
      _translateTranform.Y += e.GetPosition(null).Y - _mouseLastPosition.Y;
      _translateTranform.X += e.GetPosition(null).X - _mouseLastPosition.X;

      //aggiorna le variabili 
      _mouseLastPosition.X = e.GetPosition(null).X;
      _mouseLastPosition.Y = e.GetPosition(null).Y;

      //trovo le coordinare correnti dell'oggetto
      GeneralTransform _objGeneralTransform = _element.TransformToVisual(Application.Current.RootVisual as UIElement);
      Point _point = _objGeneralTransform.Transform(new Point(0, 0));

      //controllo che tra l'oggetti con cui collido si trovi un "dockItem"
      UIElement u = (from ui in VisualTreeHelper.FindElementsInHostCoordinates(
                  new Rect(_point.X,
                           _point.Y,
                          ((FrameworkElement)_element).ActualWidth,
                          ((FrameworkElement)_element).ActualHeight),
                          (UIElement)VisualTreeHelper.GetParent(_element))
                     where (bool)ui.GetValue(IsDragDockProperty) == true
                     select ui).FirstOrDefault();

      if (u != null)
      {
         if (_currentDockItem != u)
         {
            _currentDockItem = u;
         }
      }
      else
      {
         _currentDockItem = null;
      }
   }
}

La parte più importante del codice è quella che verifica le collisioni dell'oggetto trascinato con gli altri elementi dell'interfaccia, il risultato viene filtrato in base al valore dell'AttachedProperty IsDragDockProperty e poi salvato nel campo _currentDockItem.

IsDragDockProperty è un'AttachedProperties definita dal DragManager che indica su quali aree è possibile rilasciare l'elemento che stiamo trascinando.

Il valore del campo _currentDockItem è valutato al rilascio del mouse e nel caso questo sia nullo, l'oggetto trascinato è riportato nella sua posizione originale.
Lo XAML seguente mostra la creazione di due regioni (Border) nelle quali è possibile rilasciare l'oggetto che stiamo trascinando.

Rilasciandolo invece fuori da queste aree farà si che sia ripristinata la sua precedente posizione.

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:my="clr-namespace:DragDropSilverlightApplication"
             xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             x:Class="DragDropSilverlightApplication.Page"
             Width="400"
             Height="300"
             mc:Ignorable="d">
    <Canvas x:Name="LayoutRoot"
            Background="White">
        <Border my:DragManager.IsDragDock="true"
                Height="100"
                Width="100"
                Canvas.Left="29"
                Canvas.Top="135"
                BorderBrush="#FF000000"
                BorderThickness="1,1,1,1"
                Background="#00FFFFFF" />
        <Border my:DragManager.IsDragDock="true"
                Height="100"
                Width="100"
                BorderBrush="#FF000000"
                BorderThickness="1,1,1,1"
                HorizontalAlignment="Right"
                Canvas.Top="29"
                Canvas.Left="29" />
        <Rectangle my:DragManager.IsDraggable="true"
                   Fill="Red"
                   Height="50"
                   Width="50"
                   Canvas.Top="135"
                   Canvas.Left="201"
                   RenderTransformOrigin="0.5,0.5" />
        <Rectangle my:DragManager.IsDraggable="true"
                   Fill="Red"
                   Height="50"
                   Width="50"
                   RenderTransformOrigin="0.5,0.5"
                   Canvas.Top="29"
                   Canvas.Left="201" />
    </Canvas>
</UserControl>

Allo script è allegata la soluzione completa con il codice interamente commentato.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

Nessuna risorsa collegata

I più letti di oggi