top trim

Drag-and-Drop Verhalten programmieren

Drag-and-drop Verhalten setzt mindestens zwei Objekte vorraus: ein Quellobjekt und ein Zielobjekt.

Zu Anschaulichkeitszwecken zeigen die Bilder unten ein ausgewähltes Quellobjekt das über ein Zielobjekt gezogen wird. Zu beachten ist das Erscheinen der Mauszeigers wenn das Quellobjekt innerhalb des Fensters über das Zielobjekt gezogen wird. Diese visuellen Besonderheiten lassen den Benutzer wissen, dass das Quellobjekt "ziehbar" ist. Sie informieren ihn auch über mögliche Zielobjekte.

Tabelle 5.1. Ziehen und Ablegen eines Objekts

Auswählen und ziehen des Quellobjekts

Ablegen auf dem Zeilobjekt


In den Bildern oben, wenn das Quellobjekt auf dem Ziel abgelegt wird, wird das Quellobjekt aus dem Fenster entfernt. Zusätzlich zeigen die Bilder wie Daten ("I PASSED IN THIS VALUE!") von Quellobjekt an das Zielobjekt übergeben werden. (Wenn ein Quellobjekt gezogen wird, haben wir immer die Option Daten an das Eventobjekt zu übergeben, die abgefragt werden können, wenn der Benutzer die Quelle zieht und auf dem Ziel ablegt. Das kann nützlich sein, wenn es mehrere arten von Quellobjekten gibt, aber nur einen besimmten Typ, der auf ein bestimmtes Ziel abgelegt werden kann.)

Als Entwickler, um diese Art von Drag-and-Drop Verhalten zwischen zwei Objekten in der Anwendung zu aktivieren, müssen wir:

Im Normalfall wird Drag-and-Drop Verhalten durch die Erstellung von JavaScript Funktionen implementiert, die einige oder alle der folgenden Drag-and-Drop Events ausführen:

Die folgenden Code Beispiele und Bilder zeigen die Erstellung einer Anwendung mit zwei Objekten: einem "ziehbaren" Quellobjekt und einem Zielobjekt, auf das das Quellobjekt abgelegt werden kann.

Ein "ziehbares" Quellobjekt erstellen

Um ein "ziehbares" Objekt zu erstellen, müssen wir eine Reaktion zum Objekt hinzufügen, die auf den dragStart Event wartet. (Wie oben erwähnt, wird der dragStart Event ausgelöst, wenn der Benutzer eine Drag-and-Drop Operation einleitet, indem er die linke Maustaste über dem Quellobjekt drückt.)

Der folgende Code demonstriert, wie das zu tun ist:

<gadget id="mySourceObject" language="jscript"
   code="mySourceGadget.js" >
  <parts>
    <box:text  value="Source Object" />
  </parts>
  <behavior>
    <reaction event="initialized" action="gadget:onInitialized();"/> 
    <reaction event="dragStart" action="gadget:onDragStart();"/>
  </behavior>
</gadget>

Als Nächstes müssen wir eine JavaScript Funktion erstellen, die das "move" Verhalten für den Event übernimmt, wie in folgendem Code gezeigt:

// Die onDragStart Funktion initialisiert die move Operation
component.prototype.onDragStart = function ()
{
  var event = shell.currentEvent;
  if (event)
  {
    event.operation = appUtils.TRANSFER_OPERATION_MOVE;
    //  Daten an das Event Objekt übergeben
    event.sourceData.addString("application/x-array",
       "I PASSED IN THIS VALUE!");
    event.stopPropagation();
  }
}

Und so funktioniert die onDragStart Funktion:

Als erstes erhalten wir den aktuellen Event der versandt wurde. Wenn der Event nicht null ist, verwenden wir die appUtils.TRANSFER_OPERATION_MOVE Funktion um das "move" Verhalten für das Event zu übernehmen, durch das Ändern des Mauszeigers während der Zieh Operation. Als letzten schritt, übergeben wir Daten an das Event Objekt. Dieses Objekt (und die Daten die ihm übergeben wurden) ist innerhalb der onDragOver und onDragDrop Funktionen verfügbar, die wir im nächsten Teil erstellen werden, Ein Zielobjekt zum fallenlassen des Objekts erstellen.

Wenn wir jetzt unsere Anwendung ausführen, und das Quellobjekt ziehen, wird der Mauszeiger geändert, um anzuzeigen, dass das Objekt bewegt wird, wie in den folgenden Bildern gezeigt.

Tabelle 5.2. Ziehen eines Objekts ändert den Mauszeiger

Auswählen des Quellobjekts

Ziehen des Quellobjekts


Ein Zielobjekt zum fallenlassen des Objekts erstellen

Um ein Zielobjekt zu erstellen, auf das das Quellobjekt abgelegt werden kann, müssen wir Reaktionen zum Zielobjekt hinzufügen, die auf die dragOver, dragDrop, und dragOut Events warten, wie in folgendem Code gezeigt:

<gadget id="myTargetObject" language="jscript"
    code="myTargetGadget.js"  >
  <parts>
    <box:text  value="Target Object" />
    <box:text  id="myPassedData" value="[Passed Message Appears Here]" />
  </parts>
  <behavior>
    <reaction event="initialized" action="gadget:onInitialized();" />  
    <reaction event="dragOver" action="gadget:onDragOver();" />
    <reaction event="dragDrop" action="gadget:onDragDrop();" />
    <reaction event="dragOut" action="gadget:onDragOut();" />
  </behavior>
</gadget>

Als Nächstes müssen wir JavaScript Funktionen erstellen, die die dragOver, dragDrop, und dragOut Events verwalten, wie in folgendem Code gezeigt:

// Die onDragDrop Funktion stellt sicher, dass das Objekt das auf dem 
// Ziel abgelgt wird, die Art von Objekt ist, die das Ziel aufnehmen
// kann.
component.prototype.onDragDrop = function ()
{
  var event = shell.currentEvent;
  if (event)
  {
    if (event.targetBox && event.sourceBox)
    {
      if (event.targetBox != event.sourceBox)
      {
        // erhalte Daten die dem Event übergeben wurden durch: application/x-array
        var myString = event.sourceData.getStringAt(0,"application/x-array");

        // aktualisieren des Wertes des myPassedData Text Labels um die
        // Daten, die dem Event übergeben wurden, anzuzeigen
        this.gadget.getPartById("myPassedData").setAttribute("value", myString);

        // optionales Aufrufen einer Funktion als Reaktion auf den dragDrop Event
        this.gadget.gadgetParent.deleteSourceGadget();
      }
    }
  }
}

// Die onDragOver Funktion bestimmt, ob das Zielobjekt das
// Quellobjekt aufnehmen kann
component.prototype.onDragOver = function ()
{
  var event = shell.currentEvent;
  if (event)
  {
    if (event.targetBox && event.sourceBox)
    {
      event.canDrop = 0;
      if (event.targetBox != event.sourceBox)
      {
        // Ändern des Aussehens des Zielobjekts (mit colorSubtract) um
        // anzuzeigen, dass es am Ziel "ablegbar" ist
        event.targetBox.setStyle("colorSubtract", "#00000ffff");

        var targetParent = event.targetBox.parent;
        var canDrop = 1;

        while (targetParent && canDrop != 0)
        {
          if (targetParent == event.sourceBox)
          {
            canDrop = 0;
          }

          targetParent = targetParent.parent;
        }

        if (canDrop == 1)
        {
          event.operation = appUtils.TRANSFER_OPERATION_MOVE;
          event.canDrop = canDrop;
        }
      }
    }
  }
}

// Die onDragOut Funktion ändert das Aussehen des Zielobjekts
// zurück auf das ursprüngliche Aussehen (bevor das Quellobjekt
// darüber gezogen wurde).
component.prototype.onDragOut = function ()
{
  var event = shell.currentEvent;
  if (event)
  {
    if (event.targetBox && event.sourceBox)
    {
      if (event.targetBox != event.sourceBox)
      {
        // zurückaendern der Farbe
        event.targetBox.setStyle("colorSubtract", "#000000000");
      }
    }
  }
}

Und so funktioniert der obige Code:

Die onDragDrop Funktion, die ausgelöst wird, wenn der Benutzer die Maustaste über einem gültigen Ziel loslässt, erhält zunächst den aktuellen Event der übergeben wird. Wenn der Event nicht null ist, stellt sie sicher dass die event.targetBox (die Box die das Ziel der Drag-and-Drop Operation ist) und die event.sourceBox (die Box die in dieser Drag-and-Drop Operation gezogen wird) nicht das gleiche Objekt sind. Vorrausgesetzt Quell- und Zielobjekt sind unterschiedlich, erhält sie die Daten, die dem Event Objekt übergeben wurden und zeigt die Daten im myPassedData Part des Zielobjekts an. Sie löst dann die Funktion aus, um das Quellobjekt aus der Szene zu entfernen.

Die onDragOver Funktion, die ausgelöst wird, wenn die Maus in die Begrenzung einer Box während der Drad-and-Drop Operation eintritt, stell visuelle Effekte zur Verfügung, um anzuzeigen ob das Ziel zum Ablegen ist. Sie erhält zunächst den aktuellen Event der übergeben wird und wenn der Event nicht null ist, legt sie die event.canDrop Eigenschaft auf null fest. Das aendert den Mauszeiger zu einem Kreis mit einem Strich hindurch, um anzuzeigen, dass das Ziel nicht zum Ablegen ist. Dann, wenn die event.targetBox und die event.sourceBox unterschiedlich sind, ändert sie das Aussehen des Zielobjekts legt die event.canDrap Eigenschaft auf eins oder true fest, was den Mauszeiger zu einer offenen Box ändert, um anzuzeigen, dass das Ziel zum Ablegen ist.

Wichtig

Während das nicht in diesem Beispiel gezeigt wurde, kann die onDragOver Funktion des Drag Events sourceData Eigenschaft verwenden, um die an das Drag Event Objekt übergebenen Daten abzufragen, wie in der onDragDrop Funktion gezeigt. In beiden Fällen können die übergebenen Daten verwendet werden um festzustellen ob die Quelle über das Ziel gezogen/auf ihm abgelegt werden kann.

Die onDragOut Funktion, die ausgelöst wird, wenn die Maus die Grenzen einer Box während einer Drag-and Drop Operation verlässt, erhält den übergebenen Event, und ändert, wenn der Event nicht null ist, das Aussehen der event.targetBox zurück zur ursprünglichen Farbe.

Wenn wir jetzt die Anwendung ausführen und das Quellobjekt über das Zielobjekt ziehen und dort ablegen, werden die betreffenden Funktionen ausgelöst.

Tabelle 5.3. Ziehen und ablegen eines Objekts führt eine Funktion aus

Auswählen und Ziehen des Quellobjekts

Auf dem Zielobjekt ablegen


"To-Do List" Übung 5.1: Das noteArea Gadget "ziehbar" machen

Nun, da es und bekannt ist Drag-and-Drop Verhalten einzubauen, werden wir es in der "To-Do List" Anwendung ausprobieren.

In dieser Übung werden wir lernen, Drag-and-Drop Verhalten einzubauen, um Benutzern zu erlauben, einen "to-do" Eintrag auf ein Mülleimer Icon im buttonsArea Gadget zu ziehen. Auf diese Weise, anstelle des Klickens des "Clear To-Do Item" Buttons, kann der Benutzer jedesmal, wenn er-sie einen Eintrag löschen möchte, einfach den "to-do" Eintrag auf den Mülleimer Icon ziehen. Die folgenden Bilder demonstrieren die Drag-and-Drop Operation.

Tabelle 5.4. Eine Notiz durch ziehen und ablegen in den Mülleimer entfernen

Ziehen des "to-do" Eintrags über das Mülleimer Icon.

Wenn er abgelegt wird, wird der Eintrag gelöscht.


Um die noteArea "ziehbar" zu machen, folgen wir folgenden Schritten:

  • Schritt 1: Modifizieren des noteArea Gadgets - Modifizieren der Definition des noteArea Gadgets, um es "ziehbar" zu machen. Da das noteArea Gadget viele Parts beinhaltet, müssen wir eine Box um die Parts hinzufügen, die auf den dragStart Event wartet.

  • Schritt 2: Erstellen von JavaScript Funktionen für das noteArea Gadget - Erstellen einer JavaScript Funktion um auf den dragStart Event zu reagieren.

  • Schritt 3: Testen der Anwendung - Ausfühern der Anwendung un sicherstellen, dass das noteArea Gadget "ziehbar" ist.

  • Schritt 4: Erstellen der deleteCurrentItem Funktion - In der ToDoList.js Datei, erstellen wir eine neue JavaScript Funktion um den aktuellen Eintrag aus der Liste zu löschen. Diese Funktion wird aufgerufen, wenn der dragDrop Event auftritt.

Once you've completed exercise 5.1, you can proceed to the next exercise, exercise 5.2, where you will create the target object (the trashcan) on which the source object (the noteArea) may be dropped.

Schritt 1: Hinzufügen von Event Handling zum noteArea gadget

In der noteAreaGadgets.box Datei müssen wir:

  • Eine vertikale Box um alle Parts des Gadgets einfügen und die dragMode Eigenschaft auf "data" festlegen. Diese Box wird die "ziehbare" Einheit.

  • Für jeden enthaltenen Part innerhalb der Box legen wir die ignoreEvents Eigenschaft auf "true" fest. Dies versichert, dass die einzelnen Parts nicht individuell ziehbar sind.

  • Hinzufügen einer Reaktion, um auf den dragStart Event zu warten.

  • Modifizieren der noteArea Gadget Style Eigenschaften, um die vertikale Box unterzubringen.

Der Code, unten fett formatiert, zeigt, wie dies zu tun ist:

<gadget id="noteArea" language="jscript"
    code="noteAreaGadgets.js" >
  <attributes title="" date="" description="" image="" />
  <script id="common" language="jscript"
      href="../../common/common.js" />
  <parts>
     <box:vbox id="itemContainer" dragMode="data"  > 
      <box:label   ignoreEvents="true" id="itemTitle" inherits="value=title" />
      <box:label   ignoreEvents="true" id="itemDate" inherits="value=date" />
      <box:vbox  ignoreEvents="true" id="descriptionContainer"  >
        <box:multilineLabel  ignoreEvents="true" id="itemDescription"
            inherits="value=description" />
      </box:vbox>
      <box:image  ignoreEvents="true" id="itemImage" inherits="src=image" />
     </box:vbox>
  </parts>
  <behavior>
    <reaction event="initialized" action="gadget:onInitialized();" /> 
    <reaction event="dragStart" action="gadget:onDragStart();" />
  </behavior>
</gadget>
<style tag="noteArea" flex="1" orient="vertical" > 
  <part name="itemContainer" hAlign="center" vAlign="top" margin="25 0 0 25" />
  <part name="itemTitle" padding="0" fontFamily="Arial" fontSize="22pt"
      hAlign="center"/>
  <part name="itemDate" padding="0" margin="0" fontFamily="Arial"
      fontSize="12pt"  hAlign="center" />
  <part name="descriptionContainer" overflow="scroll" minHeight="90"
      maxHeight="90" minWidth="280" maxWidth="280" hAlign="center" />
  <part name="itemDescription" width="240" minHeight="80"
      fontFamily="Brush Script" fontSize="14pt"/>
  <part name="itemImage" maxWidth="240" maxHeight="180" margin="0 0 0 15" />
</style>

Schritt 2: Erstellen von JavaScript Funktionen für das noteArea Gadget

Als Nächstes, in der noteAreaGadgets.js Datei, fügen wir folgenden Code, unten fett hervorgehoben, ein um den "dragStart" Event zu verwalten:

// Die onInitialized Funktion wird aufgerufen wenn ein Gadget erstellt
// wird, aber noch nicht dargestellt wird (d.h. sichtbar ist)
component.prototype.onInitialized = function()
{
};
// Boolscher Wert der bestimmt, ob ein Eintrag angezeigt wird
            component.prototype.displayingItem = false;

// Die setDisplay Funktion bevölkert die noteArea Gadget Parts mit
// den Werten im Element des Eintrags Arrays, der der Funktion
// übergeben wird.
component.prototype.setDisplay = function(item)
{
  this._gadget.setAttribute("title", item["title"]);
  this._gadget.setAttribute("date", formatDate(item["date"]));
  this._gadget.setAttribute("description", item["description"]);
  this._gadget.setAttribute("image", item["image"]); 
  this.displayingItem = true;
}

component.prototype.blankDisplay = function()
{
  this._gadget.setAttribute("title", "[no note to display]");
  this._gadget.setAttribute("date", "");
  this._gadget.setAttribute("description",
      "Instructions: Add a note with the button 'Create To-Do Item' below.");
  this._gadget.setAttribute("image", gDefaultImage); 
  this.displayingItem = false;
}
 
component.prototype.onDragStart = function ()
{
  if (!this.displayingItem)
  {
    return;
  }

  var event = shell.currentEvent;

  if (event)
  {
    event.operation = appUtils.TRANSFER_OPERATION_MOVE;

    //  Daten an das Eventobjekt übergeben
    var myPassedObject = Object();
    myPassedObject["some key"] = "some value";
    event.sourceData.addString("application/x-array", myPassedObject);

    event.stopPropagation();
  }
}

Und so funktioniert der obige Code:

In den setDisplay und blankDisplay Funktionen, wird die this.displayingItem Eigenschaft passend auf true oder false festgelegt.

Die onDragStart Funktion bestimmt den Wert, der in der this.displayingItem Eigenschaft gespeichert ist. Ist er true, wird der aktuelle Event eine "move" Operation, und übernimmt das Bewegeverhalten an das Gadget.

Schritt 3: Testen der Anwendung

Wenn wir jetzt unsere Anwendung ausführen, einen neuen "to-do" Eintrag erstellen, und dann den Notizbereich ziehen, erscheint es als ob sie sich bewegt. WEnn wir versuchen den standard "to-do" Eintrag zu verschieben, wenn kein Eintrag vorhanden ist, wird der Notizbereich nicht verschoben.

Schritt 4: Erstellen der deleteCurrentItem Funktion

In der ToDoList.js Datei müssen wir eine neue Funktions namens deleteCurrentItem erstellen, die aufgerufen wird, wenn der dragDrop Event auftritt. Zusätzlich müssen wir die deleteItem Funktion modifizieren, um den Code einzufügen, der unten fett gezeigt ist.

 component.prototype.deleteCurrentItem = function ()
{
  this.deleteItem(this.currentItem);
}

component.prototype.deleteItem = function (index)
{
  if (this.items == null || this.items[index] == null) { return; }
  this.items.splice(index, 1); 
  this.displayCurrentItem();
}

"To-Do List" Übung 5.2: Ein Zielobjekt erstellen

In dieser Übung werden wir ein Zielobjekt für die Drag-and-Drop Operation erstellen, indem wir folgendee Schritte folgen:

  • Schritt 1: Erstellen des Zielobjekts - Im buttonsArea Gadget, erstellen wir eine Bildbox, die ein Mülleimer Icon enthält. Sie wird als Zielobjekt dienen. Wir fügen Event Handler zum Zielobjekt hinzu, die auf die dragDrop, dragOver, und dragOut Events warten.

  • Schritt 2: Erstellen von JavaScript Funktionen für das buttonsArea Gadget - Erstellen der JavaScript Funktionen, die nötig sind, um auf die drapDrop, dragOver, und dragOut Events zu reagieren.

  • Schritt 3: Testen der Anwendung - Ausführen der Anwendung und anschließendem Ziehen des noteArea Gadgets auf das Mülleimer Icon. Wenn es fallengelassen wird ist sicherzustellen, dass die betreffende Funktion (deleteItem) ausgelöst wird.

Schritt 1: Erstellen des Zielobjekts

In der buttonsAreaGadgets.box Datei fügen wir eine Bildbox in das buttonsArea Gadget ein, die die trashCan.png Bilddatei lädt. Within the image box, verwenden wir den on Namespace um auf die dragDrop, dragOver, und dragOut Events zu warten, wie unten fett hervorgehoben.

<gadget id="buttonsArea" language="jscript"
    code="buttonsAreaGadgets.js" >
  <parts>
    <box:hbox id="prevNextButtons">
        <box:textButton on:command="gadget:onPreviousItem();"
            label="&#171; Prev Note" tooltip="Go to your previous Note."/>
        <box:hbox s:flex="1"/> 
        <box:image on:dragDrop="gadget:onDragDrop();"
          on:dragOver="gadget:onDragOver();" on:dragOut="gadget:onDragOut();"
          src="../../theme/images/trashCan.png"/>
        <box:hbox s:flex="1"/>
        <box:textButton on:command="gadget:onNextItem();"
            label="Next Note &#187;" tooltip="Go to your next Note."/>
    </box:hbox>
    <box:button on:command="gadget:onCreateItem();" s:flex="1"
        defaultButton="true" icon="../../theme/images/iconWrite.png"
        label="Create To-Do Item" tooltip="Create new To-Do Note." />
    <box:hbox>
      <box:button on:command="gadget:onClearItem();" s:flex="1"
        icon="../../theme/images/iconClearItem.png"
        label="Clear To-Do Item" tooltip="Remove this Note from your list."/>
      <box:button on:command="gadget:onClearAllItems();" s:flex="1"
        icon="../../theme/images/iconClearList.png"
        label="Clear To-Do List" tooltip="Remove all Notes from your list."/>
    </box:hbox>
  </parts>
  <behavior>
    <reaction event="initialized" action="gadget:onInitialized();" />
  </behavior>
</gadget>

Schritt 2: Erstellen von JavaScript Funktionen für das buttonsArea Gadget

In der buttonsAreaGadgets.js Datei fügen wir folgende Funktionen hinzu, um die dragDrop, dragOver, und dragOut Events zu verwalten.

// Die onDragDrop Funktion wird ausgelöst, wenn der onDragDrop Event auftritt.
// Diese Funktion bestimmt ob Quellobjekt ein gültiges Objekt für den
// Drop Event ist.  Falls dem so ist, ruft sie die Funktion auf, die
// den Drop Event verwaltet (in diesem Fall die deleteCurrentItem Funktion)
component.prototype.onDragDrop = function ()
{
  var event = shell.currentEvent;
  if (event)
  {
    // find the correct box to drop on top of
    if (event.targetBox && event.sourceBox)
    {
      if (event.targetBox != event.sourceBox)
      {
        this.parent.deleteCurrentItem();
      }
    }
  }
}

// Die onDragOver Funktion bestimmt ob die Box über der wir uns
// gerade befinden (das Zielobjekt) das Quellobjekt aufnehmen kann
component.prototype.onDragOver = function ()
{
  var event = shell.currentEvent;
  if (event)
  {
    if (event.targetBox && event.sourceBox)
    {
      // zuerst sicherstellen, dass die Box nicht darauf
      // abgelegt werden kann
      event.canDrop = 0;

      if (event.targetBox != event.sourceBox)
      {
        // Ändern des Aussehens des Zielobjekts (mit colorSubtract) um
        // anzuzeigen, dass es am Ziel "ablegbar" ist
        event.targetBox.setStyle("colorSubtract", "#00000ffff");

        // Sicherstellen, dass das Quellobjekt nicht das Parent
        // des Zielobjekts ist
        var targetParent = event.targetBox.parent;
        var canDrop = 1;

        while (targetParent && canDrop != 0)
        {
          if (targetParent == event.sourceBox)
          {
            canDrop = 0;
          }

          targetParent = targetParent.parent;
        }

        if (canDrop == 1)
        {
          event.operation = appUtils.TRANSFER_OPERATION_MOVE;
          event.canDrop = canDrop;
        }
      }
    }
  }
}

// Die onDragOut Funktion ändert das Aussehen des Zielobjekts
// zurück auf das ursprüngliche Aussehen (bevor das Quellobjekt
// darüber gezogen wurde).
component.prototype.onDragOut = function ()
{
  var event = shell.currentEvent;
  if (event)
  {
    if (event.targetBox && event.sourceBox)
    {
      if (event.targetBox != event.sourceBox)
      {
        // change the color back
        event.targetBox.setStyle("colorSubtract", "#000000000");
      }
    }
  }
}

Schritt 3: Testen der Anwendung

Jetzt sollte es uns möglich sein, Notizen in unserer Anwendung auf das Mülleimer Icon zu ziehen, wie in folgenden Grafiken gezeigt. Wir müssen sicherstellen, zuerst einen Eintrag zu erstellen:

Tabelle 5.5. Jetzt wird eine Notiz durch das Ziehen in den Mülleimer entfernt

Ziehen des "to-do" Eintrags über das Mülleimer Icon.

Wenn er fallengelassen wird, wird der Eintrag gelöscht.


bottom trim