#46 - Permettere il Drag-and-drop solo su zone predefinite con 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.

Nota: Questo script contiene un allegato.

IL CONTENUTO
SCRIPT VIA E-MAIL

Iscriviti alle nostre newsletter unoscript@lgiorno e Xcript per ricevere gli script via e-mail.

MEDIA
IN EVIDENZA
MISC