使用OLE拖放列表框项

介绍 奥立已经做了拖放一块蛋糕。它允许两个不相关的应用程序在剪贴板格式的帮助下以双方都能理解的格式交换数据。使用OLE进行拖放是一项相对简单的任务。使用MFC,除了创建适当的对象并调用它们适当的方法外,实际上没有什么可做的。拖放操作涉及到三个MFC类。这些类是COleDataSource、COleDropSource和COleDropTarget。 背景 下面简要说明这些类的作用。COleDataSource是保存将从源传输到目标的数据的类。COleDropSource是一个相对较小的类,它在拖放操作期间提供可视化反馈。最后,COleDropTarget是处理目标端的所有东西的类。实际上,大多数人只会使用COleDataSource和COleDropTarget,因为大多数人不需要改变COleDropSource的行为,它是由COleDataSource自动创建的。 拖放操作由源对象创建COleDataSource、向其附加一些数据以及调用COleDataSource对象的DoDragDrop方法组成。目标对象必须实现一个COleDropTarget类。COleDropTarget类有几个虚函数,它们在操作期间被调用:OnDragEnter、OnDragOver、OnDragLeave和OnDrop是最重要的。必须重写这些方法,这样我们才能告诉系统要做什么。 让我们看一下来源。在这里,我们将创建一个COleDataSource并将一些文本附加到其中。隐藏,复制Code

void CMyDragAndDropWnd::StartDrag()
{
   //create the COleDataSource, and attach the data to it
   COleDataSource DataSource;
 
   //create a chunck of memory that will hold "This is a test" 15 chars
   HGLOBAL hData = GlobalAlloc(GMEM_MOVEABLE,15);
   char *pChar = (char *)GlobalLock(hData);
   strcpy(pChar,"This is a test");
   GlobalUnlock(hData);
 
   if (hData)
   {
      //attach the data to the COleDataSource Object
      DataSource.CacheGlobalData(CF_TEXT,hData);
 
      //allow the user to drag it.
      DROPEFFECT DropEffect = 
         DataSource.DoDragDrop(GetDragItemEffects(m_DraggedIndex));
 
      //Once DoDragDrop returns we can check the return value stored in DropEffect
      //to see what kind of dropping happened. Like move, copy or shortcut
   }
}

“掉落”的部分比拖拽的部分要复杂得多。我们需要做的是从COleDropTarget继承并覆盖一些方法。希望接收drop的窗口需要有一个继承类的对象,并使用register方法向它注册自己。让我们看看从COleDropTarget继承的简单类是什么样子的。隐藏,收缩,复制Code

//this method is called when the Drag operation first enters the window
//attached to this COleDropTarget object
DROPEFFECT CMyDropTarget::OnDragEnter(CWnd* pWnd, 
           COleDataObject* pDataObject, 
           DWORD dwKeyState,CPoint point)
{
   //if the data is the kind we want
   if (pDataObject->IsDataAvailable(CF_TEXT))
   {
      //here we can take bring window to top
      //since what is attached to the COleDropTarget object is
      //most likely a child window, you would probably have to 
      //send it a message so that it can activate it's parent 
      //or something.
      SendMessage(m_hWnd,...,...,...);

      //we can handle copy and move of this data
      return DROPEFFECT_COPY|DROPEFFECT_MOVE;
   }
   //we can't handle this type of data
   return DROPEFFECT_NONE;
}

DROPEFFECT CMyDropTarget::OnDragOver(CWnd* pWnd, 
           COleDataObject* pDataObject, 
           DWORD dwKeyState,CPoint point)
{
   //if the data is the kind we want
   if (pDataObject->IsDataAvailable(CF_TEXT))
   {
      //we can handle copy and move of this data
      return DROPEFFECT_COPY|DROPEFFECT_MOVE;
   }
   
   //we can't handle this type of data
   return DROPEFFECT_NONE;
}

void CMyDropTarget::OnDragLeave(CWnd* pWnd)
{
   //we can use this method to do any kind of clean up
   //in this case we don't have anything
}

BOOL CMyDropTarget::OnDrop(CWnd* pWnd, 
                    COleDataObject* pDataObject, 
                    DROPEFFECT dropEffect,CPoint point)
{
   BOOL Ret = FALSE;
   
   //if it is a format that we handle
   if (pDataObject->IsDataAvailable(CF_TEXT))
   {
      //grab the data 
      HGLOBAL hGlobal = pDataObject->GetGlobalData(m_cfFormat);
   
      //Do something with the data
      //here you would normally send a message back to the 
      //window registered to the COleDropTarget to tell it
      //to do something with the data
      SendMessage(m_hWnd,WM_DOSOMETHING,hGlobal,0);
   
      Ret = TRUE;
   }
   
   return Ret;
}

这就是它的全部。现在,让我们看一下拖放列表框的例子。 它是如何工作的 这个listbox类演示了如何重新排列列表框项,以及如何使用OLE的拖放功能在两个列表框之间拖放项目。概念非常简单:获取用户想要拖动的项,创建一个COleDataSource对象,将数据附加到该对象上,并调用COleDataSource. dodragdrop()。在接收端,显示下降将是当用户拖拽项目,通过覆盖COleDropTarget OnDragOver方法,最后插入新位置的物品一旦用户释放鼠标按钮调用COleDropTarget OnDrop方法。 为了完成这个任务,我创建了一个新的类COleDragAndDropListBox,它继承了CListBox和COleDropTarget。与containment方法相比,我更喜欢这个方法,因为通过这种方式,COleDropTarget派生类不必向CListBox派生类发送消息来获取信息并通知drop操作。(多继承部分是不允许在vc++ 6.0及以上版本,所以只有在vc++ 7.0编译器编译时才可以工作。) 让我们从任务的拖动部分开始。我们需要为你的listbox窗口捕捉三条消息来做这个,WM_LBUTTONDOWN, WM_MOUSEMOVE,和WM_LBUTTONUP。 WM_LBUTTONDOWN处理程序方法仅确定用户选择了哪一项。隐藏,复制Code

void COLEDragAndDropListBox::OnLButtonDown(UINT nFlags, CPoint point)
{
   __super::OnLButtonDown(nFlags, point);

   //keep track of the item that was clicked on
   //WM_MOUSEMOVE message is going to use that to 
   //create the COleDataSource
   m_Interval = 0;
   m_DropIndex = LB_ERR;
   m_DraggedIndex = LB_ERR;

   BOOL Outside;
   int Index = ItemFromPoint(point,Outside);
   if (Index != LB_ERR && !Outside)
   {
      m_DraggedIndex = Index;
      SetCurSel(Index);
   }
}

WM_WMMOUSEMOVE处理程序的唯一任务是创建一个COleDataSource,将数据附加到它,并调用DoDragDrop。因为不是每个人都会简单地将文本从一个列表框传输到另一个列表框,所以CDragAndDropListBox类定义了一些虚函数,用于获取可用的删除模式、获取数据和删除项。当事件发生需要某物时,使用这些虚拟方法中的一个来获取它需要的信息。OnMouseMove是第一个使用这些虚拟的。它调用GetData()来获取应该附加到COleDataSource的数据,然后在调用DoDragDrop()之后,如果操作是一个移动操作,它调用RemoveItem()来删除该项。隐藏,收缩,复制Code

void COLEDragAndDropListBox::OnMouseMove(UINT nFlags, CPoint point)
{
   if (m_DraggedIndex != LB_ERR && (nFlags & MK_LBUTTON))
   {
      //create the COleDataSource, and attach the data to it
      COleDataSource DataSource;
      HGLOBAL hData = GetData(m_DraggedIndex);
      if (hData)
      {
         //attach the data
         DataSource.CacheGlobalData(m_cfFormat,hData);
   
         //allow the user to drag it.
         DROPEFFECT DropEffect = 
                DataSource.DoDragDrop(GetDragItemEffects(m_DraggedIndex));
   
         //if the user wanted to move the item then delete it
         //Only do this if it was dragged to another window
         //OnDrop handles deleting a moved item within the same window
         if (DropEffect & DROPEFFECT_MOVE && m_DraggedIndex != LB_ERR)
         {
            RemoveItem(m_DraggedIndex);
         }
         m_DraggedIndex = LB_ERR;
         GetParent()->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), 
                                  LBN_SELCHANGE),(LPARAM)CListBox::m_hWnd);
      }
   }
   
   __super::OnMouseMove(nFlags, point);
}

WM_LBUTTONUP消息处理程序的工作非常简单。它只是将所有变量设置为下一次拖动操作的初始状态。隐藏,复制Code

void COLEDragAndDropListBox::OnLButtonUp(UINT nFlags, CPoint point)
{
   //cancel everything
   KillTimer(TID_SCROLLDOWN);
   KillTimer(TID_SCROLLUP);
   m_Interval = 0;
   m_DropIndex = LB_ERR;
   Invalidate();

   __super::OnLButtonUp(nFlags, point);
}

COLEDragAndDropListBox继承的COleDropTarget类可以帮助我们处理掉的事情。这个类有四个我们感兴趣的虚函数:OnDragEnter、OnDragOver、OnDragLeave和OnDrop。OnDragEnter和OnDragOver的实现几乎是相同的。唯一的区别是OnDragEnter给了类一个用c激活窗口的机会启用ActivateWindow虚拟方法。隐藏,收缩,复制Code

DROPEFFECT COLEDragAndDropListBox::OnDragEnter(CWnd* pWnd, 
                COleDataObject* pDataObject,DWORD dwKeyState, CPoint point)
{
   if (DragOriginateInSameWindow() && !GetCanInternalDrop())
   {
      return DROPEFFECT_NONE;
   }
   //if the data is the kind we want
   if (pDataObject->IsDataAvailable(m_cfFormat))
   {
      //bring window to top
      ActivateWindow();
      //draw the line where it would be inserted
      DrawTheLines(GetItemAt(point));
      //scroll if needed
      DoTheScrolling(point);
      //return how the user can drop the item
      return GetDropItemEffects(pDataObject,dwKeyState);
   }
   return DROPEFFECT_NONE;
}

DROPEFFECT COLEDragAndDropListBox::OnDragOver(CWnd* pWnd, 
           COleDataObject* pDataObject,DWORD dwKeyState,CPoint point)
{
   if (DragOriginateInSameWindow() && !GetCanInternalDrop())
   {
      return DROPEFFECT_NONE;
   }
   //if the data is the kind we want
   if (pDataObject->IsDataAvailable(m_cfFormat))
   {
      //draw the line where it would be inserted
      DrawTheLines(GetItemAt(point));
      //scroll if needed
      DoTheScrolling(point);
      //return how the user can drop the item
      return GetDropItemEffects(pDataObject,dwKeyState);
   }
   return DROPEFFECT_NONE;
}

OnDragLeave方法甚至更简单。它所做的只是清理一下。因为OnDragOver方法在列表框上画了一条线来表示插入点,所以当鼠标在拖动操作期间离开窗口时,OnDragLeave将处理删除这条线的处理。隐藏,复制Code

void COLEDragAndDropListBox::OnDragLeave(CWnd* pWnd)
{
   //cancel everything
   KillTimer(TID_SCROLLDOWN);
   KillTimer(TID_SCROLLUP);
   //Clear The line that was drawn
   CDC *pDC = GetDC(); 
   ClearOldLine(pDC,m_DropIndex);
   ReleaseDC(pDC);
   m_Interval = 0;
   m_DropIndex = LB_ERR;
}

OnDrop方法是该类中最广泛的方法。顾名思义,当用户释放窗口上的鼠标按钮时,就会调用它。一般来说,这是非常简单的,但是由于用户可以在发起拖放操作的同一个窗口中进行拖放,所以我们必须处理一些特殊情况。特殊情况是,用户希望将项从一个索引移动到另一个索引。这可能很棘手,因为当我们从列表框中添加或删除项时,索引会发生变化,我们很容易丢失需要插入或删除项的位置的索引。隐藏,收缩,复制Code

BOOL COLEDragAndDropListBox::OnDrop(CWnd* pWnd, 
          COleDataObject* pDataObject,DROPEFFECT dropEffect,CPoint point)
{
   BOOL Ret = FALSE;
   if (DragOriginateInSameWindow() && !GetCanInternalDrop())
   {
      return FALSE;
   }
   //if it is a format that we handle
   if (pDataObject->IsDataAvailable(m_cfFormat))
   {
      //get the index of where the user want's to insert item
      int Index = GetItemAt(point);
      HGLOBAL hGlobal = pDataObject->GetGlobalData(m_cfFormat);

      //if the Drag was initiated in the same window as the drop and
      //user wants to move the item then we have to handle the delete here
      //because if the original item was at a higher index than the dropped index
      //we would have to delete the dragged item first before we
      //insert the new item
      if (DragOriginateInSameWindow() && dropEffect == DROPEFFECT_MOVE)
      {
         if (m_DraggedIndex < Index)
         {
            Ret = DroppedAt(Index,hGlobal);
            RemoveItem(m_DraggedIndex);
            m_DraggedIndex = LB_ERR;
            SetCurSel(Index-1);
         }
         else if (m_DraggedIndex > Index)
         {
            RemoveItem(m_DraggedIndex);
            m_DraggedIndex = LB_ERR;
            Ret = DroppedAt(Index,hGlobal);
         }
      }
      else //simply drop the item in the desired index
      {
         Ret = DroppedAt(Index,hGlobal);
         GetParent()->SendMessage(WM_COMMAND, 
               MAKEWPARAM(GetDlgCtrlID(),LBN_SELCHANGE),
               (LPARAM)CListBox::m_hWnd);
      }
   }
   m_DropIndex = LB_ERR;
   return Ret;
}

使用的代码 COLEDragAndDropListBox类可以以两种不同的方式使用。如果您只是想将文本从一个列表框移动到另一个列表框,那么将变量的类型从CListBox更改为COLEDragAndDropListBox。隐藏,复制Code

// CDragDropListBoxSampleDlg dialog
class CDragDropListBoxSampleDlg : public CDialog
{
    ......

    // Implementation
    protected:
       COLEDragAndDropListBox  m_ListBox;
    
    ......
    };

但是,如果您想发送更多的信息而不仅仅是简单的文本,那么您将需要创建一个继承自COLEDragAndDropListBox的类,并覆盖为数不多的虚拟方法来为它提供数据并处理条目的插入和删除。隐藏,收缩,复制Code

// CDragDropListBoxSampleDlg dialog
class CMyDragAndDropListBox : public COLEDragAndDropListBox
{
   ......
protected: //overridables

   //override to supply data to attach to COleDataSource
   virtual HGLOBAL GetData(int ForIndex);

   //override to handle the item insertion
   virtual BOOL DroppedAt(int InsertBefore,HGLOBAL hGlobal);

   //override to so that you can specify 
   //if the DROPPED item can be moved or copied or both
   virtual DROPEFFECT GetDropItemEffects(COleDataObject* 
                          pDataObject,DWORD dwKeyState);

   //override to so that you can specify 
   //if the DRAGGED item can be moved or copied or both
   virtual DROPEFFECT GetDragItemEffects(int Index);

   //override to handle item deleting when an item is moved
   virtual void RemoveItem(int Index);

   //you can override this method to return 
   //the index of the item that is under the point
   //the default implementation depends 
   //on the Outside variable passed to ItemFromPoint
   //but that does not work in all cases
   virtual int GetItemAt(CPoint Point);

   //this function gets called from the OnDragOver method.
   //the default implementation tries to active the parent frame first,
   //if there is no parent frame then it activates the parent window
   virtual void ActivateWindow();

   ......
};

本文转载于:http://www.diyabc.com/frontweb/news348.html

原文地址:https://www.cnblogs.com/Dincat/p/13438245.html