Click here to Skip to main content
15,867,686 members
Articles / Desktop Programming / MFC
Article

Drag & Drop in FlexGrid Using Arbitrary Formats

Rate me:
Please Sign up or sign in to vote.
4.76/5 (12 votes)
2 Jun 2003CPOL3 min read 129.1K   2.4K   30   25
Implementing drag and rop within MS Flexgrid control using any format

Sample Image - FlexGridDragDrop.gif

Introduction

Are you stuck on following corporate 'GUI standards' to use FlexGrid whenever displaying grid data? Stop dreaming, QA wont allow you to use cool MFC grid, or custom CListCtrl. You must use the dreaded FlexGrid control :D ! The good news is, this article is for you!

Few months ago, I was in that dilemma. I have been skewering around for any FlexGrid drag-drop documentation but only found just general, almost useless VB stuff. So here it is - a C++ guide to implementing drag-n-drop in MS FlexGrid.

Implementation

FlexGrid drag-drop is implemented in five parts:

  1. Initiate drag

    At the source grid context, detect mouse movement. Call FlexGrid::OLEDrag() if dragging is appropriate.

    // Event Sink Map
    BEGIN_EVENTSINK_MAP(CSourceGrid, CMSFlexGrid)
        ON_EVENT_REFLECT(CSourceGrid, -606 /* MouseMove */, 
        OnMouseMove, VTS_I2 VTS_I2 VTS_I4 VTS_I4)
        ... other events...
    END_EVENTSINK_MAP()
    
    
    // Handle mouse move event and initiate OLE drag if appropriate
    BOOL CSourceGrid::OnMouseMove(short Button, 
                     short Shift, long x, long y) 
    {
        if (Button&0x1 && !m_dragging) // 1 - vbLeftButton 
        {
            if (!m_resourcesAssigned[index])
            {
                m_dragging = TRUE;
                OLEDrag(); // OnOLEStartDrag will be called
            }
        }
    
        return m_dragging;
    }

    Of course, you can also implement the above in the Flexgrid container window (e.g. the dialog). The dialog's event sink would look like the following.

    C++
    // Dialog Event Sink Map
    BEGIN_EVENTSINK_MAP(CFlexGridDragDropDlg, CDialog)
        ON_EVENT(CFlexGridDragDropDlg, IDC_GRID_SOURCE, 
            -606 /* MouseMove */, OnGridSourceMouseMove, 
            VTS_I2 VTS_I2 VTS_I4 VTS_I4)
        ... other events...
    END_EVENTSINK_MAP()
  2. Handle OLEStartDrag event

    OLEDrag() above triggers a OLEStartDrag event. Handle this event in the source grid context to create a DataObject to be passed around the drag-drop operation.

    If you are going to pass your own proprietary format, make sure you prepare a struct or a class that you can comfortably dump and restore as a byte array. Don't forget that you might need to add filler to align your struct to your memory model.

    C++
    // Event Sink Map
    BEGIN_EVENTSINK_MAP(CSourceGrid, CMSFlexGrid)
        ON_EVENT_REFLECT(CSourceGrid, 1550 /* OLEStartDrag */, 
        OnOLEStartDrag, VTS_PDISPATCH VTS_PI4)
        ... other events...
    END_EVENTSINK_MAP()
    
    
    // Handle OLEStartDrag, and create the data object to pass around
    BOOL CSourceGrid::OnOLEStartDrag(LPDISPATCH FAR* Data, 
                                     long FAR* AllowedEffects) 
    {
        int i_row = GetMouseRow();
        int i_col = GetMouseCol();
    
        // OLE gave us object where to put our data in
        CMSFlexGridDataObject dataObject(*Data);
        dataObject.Clear();
    
        // Indicate the expected drop effect
        // Use AllowedEffects to tell OLE whether the
        // object can be drag-drop or not, or whether
        // we want:
        // DROPEFFECT_COPY - we intend to keep the original data
        // DROPEFFECT_MOVE - we will delete the original 
        // data after successful drop operation
    
        if (_example_only_IsCellDraggable(i_row, i_col))
        {
            // Dont want to drag 
            *AllowedEffects = DROPEFFECT_NONE;
        }
        else
        {
            // OK to copy 
            *AllowedEffects = DROPEFFECT_COPY;
    
            if (_example_only_IsDragTypeText(i_row, i_col)
            {
                // Drag a text
                ////////////////////////
                CString str_text = GetTextMatrix(i_row, i_col);
                // Setup data to pass around
                _variant_t dragDropData(_bstr_t((LPCTSTR)str_text));
                _variant_t l_format(1L);  //cfText
                dataObject.SetData(dragDropData, l_format);
            }
            else
            {
                // Drag arbitrary format
                ////////////////////////
                // In this example, let's drag our own
                // format - from an ordinary C++ class
                CellData myData;
                // sample only
                m_currentCellData.i_resourceNumber = 0; 
                // Our sample code needs this 
                //to 'know' where the object came from
                m_currentCellData.p_gridSource = this;    
    
    
                // Now stuff our data into a variant byte array
                _variant_t dragDropData;
                <A href="#ByteArrayGet">ByteArrayFill</A>(&dragDropData, 
                      reinterpret_cast<BYTE *>(&m_currentCellData), 
                      sizeof(m_currentCellData));
    
                // And tag it with our own format type
                _variant_t l_noformat(ARBITRARY_FORMAT1); 
                dataObject.SetData(dragDropData, l_noformat);
    
                m_colDragging = col;
                m_rowDragging = row;
            }
        }
    
        // Done. Let it linger in the drag drop operation
        dataObject.DetachDispatch();
        
        // Give chance to other handlers
        return FALSE;
    }
  3. Handle OLEDragOver event

    Handle the OLEDragOver in the target grid context to examine the data object being dragged over. Tell OLE whether we can accept the object or not by specifying the Effect.

    C++
    // Event Sink Map
    BEGIN_EVENTSINK_MAP(CTargetGrid, CMSFlexGrid)
        ON_EVENT_REFLECT(CTargetGrid, 1554 /* OLEDragOver */, 
         OnOLEDragOver, 
         VTS_PDISPATCH VTS_PI4 VTS_PI2 VTS_PI2 VTS_PR4 VTS_PR4 VTS_PI2)
        ... other events...
    END_EVENTSINK_MAP()
    
    
    // Handle OLEDragOver, indicate whether we
    // can accept the object or not
    BOOL CTargetGrid::OnOLEDragOver(LPDISPATCH FAR* Data, 
        long FAR* Effect, short FAR* Button, 
        short FAR* Shift, float FAR* x, 
        float FAR* y, short FAR* State) 
    {
        int row = GetMouseRow();
        int col = GetMouseCol();
    
        // Get access to the object being dragged over
        CMSFlexGridDataObject dataObject(*Data);
    
        // Don't allow drop on the same cell or in fixed cells
        if (_example_only_CanDropInCell(row, col))
        {
            *Effect = *Effect&DROPEFFECT_NONE;
        }
    
        // Check the format if we can accept it or not
        
        // Our own format1: dragged from CSourceGrid
        // Our own format2: dragged from within this control
        if (dataObject.GetFormat(ARBITRARY_FORMAT1) 
            || dataObject.GetFormat(ARBITRARY_FORMAT2)) 
        {
            // Yes we want it. No need to modify the Effect
        }
        else if (dataObject.GetFormat(1))  // VbCFText
        {
            // Simple text. Yes we want it.
            // No need to modify the Effect
        }
        else
        {
            // Format not supported. Don't want it
            *Effect = *Effect&DROPEFFECT_NONE;
        }
    
        // We're done here
        dataObject.DetachDispatch();
    
        // Give chance to other handlers
        return FALSE;
    }
  4. Handle OLEDragDrop event

    Handle the OLEDragDrop in the target grid context to get the object being dropped. Update the cell accordingly.

    C++
    // Event Sink Map
    BEGIN_EVENTSINK_MAP(CTargetGrid, CMSFlexGrid)
        ON_EVENT_REFLECT(CTargetGrid, 
          1555 /* OLEDragDrop */, OnOLEDragDrop, 
          VTS_PDISPATCH VTS_PI4 VTS_PI2 VTS_PI2 VTS_PR4 VTS_PR4)
        ... other events...
    END_EVENTSINK_MAP()
    
    
    // Handle OnOLEDragDrop. Extract the dropped
    // data if we know its format
    BOOL CTargetGrid::OnOLEDragDrop(LPDISPATCH FAR* Data, 
        long FAR* Effect, short FAR* Button, 
        short FAR* Shift, float FAR* x, float FAR* y) 
    {
        int row = GetMouseRow();
        int col = GetMouseCol();
    
        // Get access to the object being droped
        CMSFlexGridDataObject dataObject(*Data);
    
        // Let's examine the format
        short s_format = 0;
        // dragged from CSourceGrid
        if (dataObject.GetFormat(ARBITRARY_FORMAT1)) 
        {
            s_format = (short)ARBITRARY_FORMAT1;
        }
        // dragged from within this control
        else if (dataObject.GetFormat(ARBITRARY_FORMAT2)) 
        {
            s_format = (short)ARBITRARY_FORMAT2;
        }
        else if (dataObject.GetFormat(1)) // CFText
        {
            s_format = 1;
        }
        
        // Good format
        if (s_format != 0)
        {
    
            // Get data
            _variant_t itemData = dataObject.GetData(s_format);
            if (s_format == 1)
            {
                // Simple text, easy
                ////////////////////////
                SetTextMatrix(row,col,_bstr_t(itemData));
            }
            else
            {
                // Arbitrary format, extract from the byte array
                ////////////////////////
                CellData myData;
                <A href="#ByteArrayGet">ByteArrayGet</A>(itemData, (BYTE *) &myData);
    
                // Use the dropped data
                _example_only_ConsumeData(row, col, myData);
            }
    
            // Redraw the cell
            _example_only_UpdateCellDisplay();
        }
        else
        {
            *Effect = *Effect&DROPEFFECT_NONE;
        }
    
        dataObject.DetachDispatch();
        
        // Give chance to other handlers
        return FALSE;
    }
  5. Finally, handle OLECompleteDrag event

    Handle the OLECompleteDrag in the source grid context to end the drag state. If the drag effect was DROPEFFECT_MOVE, this would be a good time to delete the source data.

    C++
    // Event Sink Map
    BEGIN_EVENTSINK_MAP(CSourceGrid, CMSFlexGrid)
        ON_EVENT_REFLECT(CSourceGrid, 
          1553 /* OLECompleteDrag */, 
          OnOLECompleteDrag, VTS_PI4)
        ... other events...
    END_EVENTSINK_MAP()
    
    // Handle OLECompleteDrag to finalize the drag-drop 
    // operation. Delete 'moved' data as appropriate.
    BOOL CSourceGrid::OnOLECompleteDrag(long FAR* Effect) 
    {
    
        if (*Effect&DROPEFFECT_MOVE)
        {
           _example_only_CellDataMoved(m_rowDragging, m_colDragging)
        }
    
        m_dragging = FALSE;
        m_colDragging = -1;
        m_rowDragging = -1;
    
        // Give chance to other handlers
        return FALSE;
    }

    In the code snippets above, the arbitrary (my local) formats were defined in stdafx.h as:

    C++
    const long ARBITRARY_FORMAT1 = 991L;
    const long ARBITRARY_FORMAT2 = 992L;
    const long ARBITRARY_FORMAT3 = 993L;

    You can define your own, just make sure it wont collide with predefined formats like 1-text, 2-bitmap, 3-metafile, 8-DIB and 9-pallete.

Helper functions

Aside from text, bitmap, meta files, CDataObject supports any format as long as the 'unknown' data is stuffed as a byte array. The following two functions are useful in converting to and extracting from byte array format. These helpers are located in the file stdafx.h of the sample project.

C++
inline void ByteArrayFill(VARIANT* p_variant, 
                BYTE * p_arraySource, int i_size)

Fills the p_variant with a byte array taken from the p_arraySource of size i_size

Parameters

  • VARIANT* p_variant
    • pointer to the variant to fill
  • BYTE * p_arraySource
    • memory location of the source data
  • int i_size
    • the size of the data to copy

Returns

  • None.
C++
inline void ByteArrayGet(VARIANT variant, BYTE * p_arrayDest)

Extracts the byte array from variant and copy it into p_arrayDest.

Parameters

  • VARIANT variant
    • the variant data containing a byte array
  • BYTE * p_arrayDest
    • memory location to where the byte array is to be extracted

      Returns

      • None.

      Notes

      Make sure p_arrayDest is pointing to an allocated memory enough to contain the data in the variant.

      Demo application

      The sample project is a dialog-based application containing three FlexGrid controls: one source grid and two target grids. The source grid supports drag (copy) only, while the target grid supports both drag (move) and drop.

      Aside from drag drop, you can see examples of:

      • Drawing bitmap or icons in a cell
      • Extracting bitmap from image list
      • Calculating cell sizes to fit and fill exactly the size of the grid control

      Finally, may I say - the codes here were written while I was cooking my dinner :-). It may not be perfect, but I hope it helped to illustrate my points.

    • License

      This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


      Written By
      Software Developer (Senior)
      United States United States
      Roaming halfway around the globe programming in C++, MFC, COM/ATL, WTL, C#, .NET, OLEDB, ADO, ADO/X.

      Living under the pleasant weather of Irvine, California, Ferdie is a Computer Engineering graduate of Mapua Institute of Technology (MIT Smile | :) ) in Philippines. Developed GIS applications in Japan for 5 years. Now a member of a team developing Windows GUI and real time software for semi-robotic equipments.

      Comments and Discussions

       
      BugProblem with Visual studio 2012 Pin
      PootisSpencer8-May-13 3:33
      PootisSpencer8-May-13 3:33 
      QuestionUp Down with Scroll button????? Pin
      qutekalp4-Feb-08 2:22
      qutekalp4-Feb-08 2:22 
      QuestionAuto Scroll when data Add Pin
      klpshsharma23-Oct-07 19:25
      klpshsharma23-Oct-07 19:25 
      AnswerRe: Auto Scroll when data Add Pin
      Ferdie26-Oct-07 19:18
      Ferdie26-Oct-07 19:18 
      GeneralRe: Auto Scroll when data Add Pin
      ponnalagan3-Apr-08 4:43
      ponnalagan3-Apr-08 4:43 
      GeneralBMP load Pin
      klpshsharma9-Oct-07 20:04
      klpshsharma9-Oct-07 20:04 
      GeneralRe: BMP load Pin
      Ferdie11-Oct-07 19:21
      Ferdie11-Oct-07 19:21 
      Generalflexgrid in dialog bar Pin
      anubis_ex20-Jun-05 9:09
      anubis_ex20-Jun-05 9:09 
      GeneralS.O.S Pin
      Anonymous14-May-05 9:08
      Anonymous14-May-05 9:08 
      GeneralDispID's Pin
      MochaJunkie9-Dec-04 6:13
      MochaJunkie9-Dec-04 6:13 
      GeneralRe: DispID's Pin
      Ferdie9-Dec-04 8:51
      Ferdie9-Dec-04 8:51 
      GeneralRe: DispID's Pin
      bhargavpp26-Dec-12 19:28
      bhargavpp26-Dec-12 19:28 
      GeneralFlexGrid Selection Pin
      Rodolfo Correa19-Apr-04 3:44
      sussRodolfo Correa19-Apr-04 3:44 
      GeneralRe: FlexGrid Selection Pin
      Ferdie20-Apr-04 8:22
      Ferdie20-Apr-04 8:22 
      Questiongrid source = grid target ? Pin
      mirceaatrds28-Jan-04 3:42
      mirceaatrds28-Jan-04 3:42 
      AnswerRe: grid source = grid target ? Pin
      Ferdie20-Apr-04 8:20
      Ferdie20-Apr-04 8:20 
      GeneralStuck Pin
      AnthonyPate24-Jul-03 18:40
      AnthonyPate24-Jul-03 18:40 
      GeneralRe: Stuck Pin
      Ferdie30-Jul-03 8:29
      Ferdie30-Jul-03 8:29 
      Questionmain frame flower icon borrowed from MSN Messenger? Pin
      Zhefu Zhang3-Jun-03 2:31
      Zhefu Zhang3-Jun-03 2:31 
      AnswerRe: main frame flower icon borrowed from MSN Messenger? Pin
      Ferdie3-Jun-03 4:26
      Ferdie3-Jun-03 4:26 
      GeneralRe: main frame flower icon borrowed from MSN Messenger? Pin
      Zhefu Zhang3-Jun-03 4:59
      Zhefu Zhang3-Jun-03 4:59 
      GeneralFlexGrid is good, why hate it Pin
      Anonymous2-Jun-03 21:29
      Anonymous2-Jun-03 21:29 
      my .02 Cool | :cool:
      GeneralRe: FlexGrid is good, why hate it Pin
      Ferdie2-Jun-03 21:40
      Ferdie2-Jun-03 21:40 
      GeneralRe: FlexGrid is good, why hate it Pin
      Atlence23-Dec-03 1:23
      Atlence23-Dec-03 1:23 
      GeneralYes, I was stuck too Pin
      Enlil2-Jun-03 21:21
      Enlil2-Jun-03 21:21 

      General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

      Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.