Будь умным!


У вас вопросы?
У нас ответы:) SamZan.net

Реализация команд Cut Copy и Pste В этом разделе приводится пример реализующий эти команды используя два фор

Работа добавлена на сайт samzan.net:


ПРОГРАММИРОВАНИЕ НА C и С++



Работа с буфером обмена.

Реализация команд Cut, Copy, и Paste

В этом разделе приводится пример, реализующий эти команды используя два формата буфера обмена: CF_OWNERDISPLAY и CF_TEXT. Обычно, перед тем, как информация будет скопирована в буфер обмена (clipboard), пользователь должен выделить определённый кусок текста. Для этого в приложении должны быть реализованы все возможности. После того, как текст будет выделен, необходимо реализовать всплывающее меню по правой кнопке мыши, а так же акселераторы к пунктам этого меню.

Чтобы сделать всплывающее меню, приложение должно обработать сообщение WM_INITMENUPOPUP:

case WM_INITMENUPOPUP:

    InitMenu((HMENU) wParam);

    break;

 

Функция InitMenu позволяет сделать определённые пункты меню доступными либо недоступными (серыми) и выглядит следующим образом:

Пример:

void WINAPI InitMenu(HMENU hmenu)

{

    int  cMenuItems = GetMenuItemCount(hmenu);

    int  nPos;

    UINT id;

    UINT fuFlags;

    PLABELBOX pbox = (hwndSelected == NULL) ? NULL :

        (PLABELBOX) GetWindowLong(hwndSelected, 0);

 

    for (nPos = 0; nPos < cMenuItems; nPos++)

    {

        id = GetMenuItemID(hmenu, nPos);

 

        switch (id)

        {

            case IDM_CUT:

            case IDM_COPY:

            case IDM_DELETE:

                if (pbox == NULL || !pbox->fSelected)

                    fuFlags = MF_BYCOMMAND | MF_GRAYED;

                else if (pbox->fEdit)

                    fuFlags = (id != IDM_DELETE && pbox->ichSel

                            == pbox->ichCaret) ?

                        MF_BYCOMMAND | MF_GRAYED :

                        MF_BYCOMMAND | MF_ENABLED;

                else

                    fuFlags = MF_BYCOMMAND | MF_ENABLED;

 

                EnableMenuItem(hmenu, id, fuFlags);

                break;

 

            case IDM_PASTE:

                if (pbox != NULL && pbox->fEdit)

                    EnableMenuItem(hmenu, id,

                        IsClipboardFormatAvailable(CF_TEXT) ?

                            MF_BYCOMMAND | MF_ENABLED :

                            MF_BYCOMMAND | MF_GRAYED

                    );

                else

                    EnableMenuItem(hmenu, id,

                        IsClipboardFormatAvailable(

                                uLabelFormat) ?

                            MF_BYCOMMAND | MF_ENABLED :

                            MF_BYCOMMAND | MF_GRAYED

                    );

 

        }

    }

}

 

Далее, для того, чтобы обрабатывать команды меню, необходимо добавить в приложение обработку команды WM_COMMAND в главную оконную процедуру:

Пример:

case WM_COMMAND:

    switch (LOWORD(wParam))

    {

        case IDM_CUT:

            if (EditCopy())

                EditDelete();

            break;

 

        case IDM_COPY:

            EditCopy();

            break;

 

        case IDM_PASTE:

            EditPaste();

            break;

 

        case IDM_DELETE:

            EditDelete();

            break;

 

        case IDM_EXIT:

            DestroyWindow(hwnd);

    }

    break;

Для реализации команд Copy и Cut используется функция EditCopy (СмКопирование данных в буфер обмена). Для реализации команды Paste используется функция EditPaste (См. Вставка данных из буфера обмена).

Копирование информации в буфер обмена

Процесс копирования информации в буфер обмена осуществляется следующим образом:

Открываем буфер обмена функцией OpenClipboard.

Очищаем буфер обмена функцией EmptyClipboard.

Вызываем функцию SetClipboardData для каждого формата буфера обмена, которые поддерживает приложение.

Закрываем буфер обмена функцией CloseClipboard.

Для того чтобы скопировать выделенный текст, используется следующая структура:

#define BOX_ELLIPSE  0

#define BOX_RECT     1

 

#define CCH_MAXLABEL 80

#define CX_MARGIN    12

 

typedef struct tagLABELBOX {

    RECT rcText;    // координаты прямоугольника с текстом

    BOOL fSelected; // TRUE если выделен label

    BOOL fEdit;     // TRUE если текст выделен

    int nType;      // прямоугольное или овальное

    int ichCaret;   // позиция каретки

    int ichSel;     // with ichCaret, delimits selection

    int nXCaret;    // window position corresponding to ichCaret

    int nXSel;      // window position corresponding to ichSel

    int cchLabel;   // длина текста в atchLabel

    TCHAR atchLabel[CCH_MAXLABEL];

} LABELBOX, *PLABELBOX;

 

А вот сама функция EditCopy:

Пример:

BOOL WINAPI EditCopy(VOID)

{

    PLABELBOX pbox;

    LPTSTR  lptstrCopy;

    HGLOBAL hglbCopy;

    int ich1, ich2, cch;

 

    if (hwndSelected == NULL)

        return FALSE;

 

    // Открываем буфер обмена и очищаем его.

 

    if (!OpenClipboard(hwndMain))

        return FALSE;

    EmptyClipboard();

 

    // Получаем указатель на структуру LABELBOX.

 

    pbox = (PLABELBOX) GetWindowLong(hwndSelected, 0);

 

    // Если текст выделен, то копируем его используя формат CF_TEXT.

 

    if (pbox->fEdit)

    {

        if (pbox->ichSel == pbox->ichCaret)     // нулевая длина

        {

            CloseClipboard();                   // выделение

            return FALSE;

        }

 

        if (pbox->ichSel < pbox->ichCaret)

        {

            ich1 = pbox->ichSel;

            ich2 = pbox->ichCaret;

        }

        else

        {

            ich1 = pbox->ichCaret;

            ich2 = pbox->ichSel;

        }

        cch = ich2 - ich1;

 

        // Выделяем память для текста.

 

        hglbCopy = GlobalAlloc(GMEM_MOVEABLE,

            (cch + 1) * sizeof(TCHAR));

        if (hglbCopy == NULL)

        {

            CloseClipboard();

            return FALSE;

        }

 

        // Блокируем хэндл и копируем текст в буфер.

 

        lptstrCopy = GlobalLock(hglbCopy);

        memcpy(lptstrCopy, &pbox->atchLabel[ich1],

            cch * sizeof(TCHAR));

        lptstrCopy[cch] = (TCHAR) 0;    // нулевой символ

        GlobalUnlock(hglbCopy);

 

        // Помещаем хэндл в буфер обмена.

 

        SetClipboardData(CF_TEXT, hglbCopy);

    }

 

    // Если текст не выделен, то копируем весь label.

 

    else

    {

        // Сохраняем копию выделенного лабела в локальной памяти.

        // С ней мы будем работать и освобождать в ответ на

        // сообщение WM_DESTROYCLIPBOARD.

 

        pboxLocalClip = (PLABELBOX) LocalAlloc(

            LMEM_FIXED,

            sizeof(LABELBOX)

        );

        if (pboxLocalClip == NULL)

        {

            CloseClipboard();

            return FALSE;

        }

        memcpy(pboxLocalClip, pbox, sizeof(LABELBOX));

        pboxLocalClip->fSelected = FALSE;

        pboxLocalClip->fEdit = FALSE;

 

        // Помещаем в буфер обмена данные трёх форматов.

 

        SetClipboardData(uLabelFormat, NULL);

        SetClipboardData(CF_OWNERDISPLAY, NULL);

        SetClipboardData(CF_TEXT, NULL);

    }

 

    // Закрываем буфер обмена.

 

    CloseClipboard();

 

    return TRUE;

}

 

Вставка данных из буфера обмена

Процесс вставки информации из буфера обмена осуществляется следующим образом:

Открываем буфер обмена функцией OpenClipboard.

Определяем форматы данных, хранящихся в буфере обмена.

Получаем хэндл данных нужного формата при помощи функции GetClipboardData.

Вставляем копию данных в документ.

Владельцем хэндла возвращённого GetClipboardData остаётся всё ещё буфер обмена, поэтому приложение не должно его освобождать.

Закрываем буфер обмена функцией CloseClipboard.

Определение структуры LABELBOX:

#define BOX_ELLIPSE  0

 #define BOX_RECT     1

 

#define CCH_MAXLABEL 80

#define CX_MARGIN    12

 

typedef struct tagLABELBOX {

    RECT rcText;    // координаты прямоугольника с текстом

    BOOL fSelected; // TRUE если будет копироваться весь label

    BOOL fEdit;     // TRUE если текст выделен

    int nType;      // прямоугольное или овальное

    int ichCaret;   // позиция каретки

    int ichSel;     // with ichCaret, delimits selection

    int nXCaret;    // window position corresponding to ichCaret

    int nXSel;      // window position corresponding to ichSel

    int cchLabel;   // длина текста в atchLabel

    TCHAR atchLabel[CCH_MAXLABEL];

} LABELBOX, *PLABELBOX;

 

Далее следует исходник функции EditPaste.

пример:

VOID WINAPI EditPaste(VOID)

{

    PLABELBOX pbox;

    HGLOBAL   hglb;

    LPTSTR    lptstr;

    PLABELBOX pboxCopy;

    int cx, cy;

    HWND hwnd;

 

    pbox = hwndSelected == NULL ? NULL :

        (PLABELBOX) GetWindowLong(hwndSelected, 0);

 

    // Если приложение находится в режиме редактирования,

    // то получаем текст из буфера обмена.

 

    if (pbox != NULL && pbox->fEdit)

    {

        if (!IsClipboardFormatAvailable(CF_TEXT))

            return;

        if (!OpenClipboard(hwndMain))

            return;

 

        hglb = GetClipboardData(CF_TEXT);

        if (hglb != NULL)

        {

            lptstr = GlobalLock(hglb);

            if (lptstr != NULL)

            {

                // Функция ReplaceSelection вставляет текст

                // и перерисовывает окно.

 

                ReplaceSelection(hwndSelected, pbox, lptstr);

                GlobalUnlock(hglb);

            }

        }

        CloseClipboard();

 

        return;

    }

 

    // Если приложение не находится в режиме редактирования,

    // то создаём окно label-а.

 

    if (!IsClipboardFormatAvailable(uLabelFormat))

        return;

    if (!OpenClipboard(hwndMain))

        return;

 

    hglb = GetClipboardData(uLabelFormat);

    if (hglb != NULL)

    {

        pboxCopy = GlobalLock(hglb);

        if (pboxCopy != NULL)

        {

            cx = pboxCopy->rcText.right + CX_MARGIN;

            cy = pboxCopy->rcText.top * 2 + cyText;

 

            hwnd = CreateWindowEx(

                WS_EX_NOPARENTNOTIFY | WS_EX_TRANSPARENT,

                atchClassChild, NULL, WS_CHILD, 0, 0, cx, cy,

                hwndMain, NULL, hinst, NULL

            );

            if (hwnd != NULL)

            {

                pbox = (PLABELBOX) GetWindowLong(hwnd, 0);

                memcpy(pbox, pboxCopy, sizeof(LABELBOX));

                ShowWindow(hwnd, SW_SHOWNORMAL);

                SetFocus(hwnd);

            }

            GlobalUnlock(hglb);

        }

    }

    CloseClipboard();

}

 

Как зарегистрировать формат буфера обмена

Для этого используется функция RegisterClipboardFormat, которая обычно вызывается при инициализации приложения.

// atchTemp может содержать имя формата

// и завершаться нулевым символом.

//

 LoadString(hinstCurrent, IDS_FORMATNAME, atchTemp,

    sizeof(atchTemp)/sizeof(TCHAR));

uLabelFormat = RegisterClipboardFormat(atchTemp);

if (uLabelFormat == 0)

    return FALSE;

 

Обработка сообщений WM_RENDERFORMAT и WM_RENDERALLFORMATS

Если не известно, какого формата данные будут помещены в буфер обмена, то можно передать в функцию SetClipboardData хэндл равный NULL, при этом приложение сгенерирует сообщениеWM_RENDERFORMAT либо WM_RENDERALLFORMATS и Вы должны позаботиться, чтобы обработать эти сообщения. В данном случае перед вызовом функции SetClipboardData нельзя открывать буфер обмена.

Сообщения WM_RENDERFORMAT и WM_RENDERALLFORMATS обрабатываются следующим образом:

case WM_RENDERFORMAT:

    RenderFormat((UINT) wParam);

    break;

 

case WM_RENDERALLFORMATS:

    RenderFormat(uLabelFormat);

    RenderFormat(CF_TEXT);

    break;

 

Код функции RenderFormat представлен ниже.

Как обычно, определение структуры LABELBOX:

#define BOX_ELLIPSE  0

#define BOX_RECT     1

 

#define CCH_MAXLABEL 80

#define CX_MARGIN    12

 

typedef struct tagLABELBOX {

    RECT rcText;    // координаты прямоугольника с текстом

    BOOL fSelected; // TRUE если будет копироваться весь label

    BOOL fEdit;     // TRUE если текст выделен

    int nType;      // прямоугольное или овальное

    int ichCaret;   // позиция каретки

    int ichSel;     // with ichCaret, delimits selection

    int nXCaret;    // window position corresponding to ichCaret

    int nXSel;      // window position corresponding to ichSel

    int cchLabel;   // длина текста в atchLabel

    TCHAR atchLabel[CCH_MAXLABEL];

} LABELBOX, *PLABELBOX;

 

Пример:

void WINAPI RenderFormat(UINT uFormat)

{

    HGLOBAL hglb;

    PLABELBOX pbox;

    LPTSTR  lptstr;

    int cch;

 

    if (pboxLocalClip == NULL)

        return;

 

    if (uFormat == CF_TEXT)

    {

        // Выделяем буфер для текста.

 

        cch = pboxLocalClip->cchLabel;

        hglb = GlobalAlloc(GMEM_MOVEABLE,

            (cch + 1) * sizeof(TCHAR));

        if (hglb == NULL)

            return;

 

        // Копируем текст из pboxLocalClip.

 

        lptstr = GlobalLock(hglb);

        memcpy(lptstr, pboxLocalClip->atchLabel,

            cch * sizeof(TCHAR));

        lptstr[cch] = (TCHAR) 0;

        GlobalUnlock(hglb);

 

        // Помещаем хэндл в буфер обмена.

 

        SetClipboardData(CF_TEXT, hglb);

    }

    else if (uFormat == uLabelFormat)

    {

        hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(LABELBOX));

        if (hglb == NULL)

            return;

        pbox = GlobalLock(hglb);

        memcpy(pbox, pboxLocalClip, sizeof(LABELBOX));

        GlobalUnlock(hglb);

 

        SetClipboardData(uLabelFormat, hglb);

    }

}

 

Обработка сообщения WM_DESTROYCLIPBOARD

Чтобы освободить различные ресурсы, можно включить в приложение обработку сообщения WM_DESTROYCLIPBOARD. Например, при копировании label-а в буфер обмена, выделяется локальная память. Эту память можно освободить при обработке сообщения WM_DESTROYCLIPBOARD.

case WM_DESTROYCLIPBOARD:

    if (pboxLocalClip != NULL)

    {

        LocalFree(pboxLocalClip);

        pboxLocalClip = NULL;

    }

    break;

 

Использование формата буфера обмена CF_OWNERDISPLAY

Если Вы помещаете данные в буфер обмена, используя формат CF_OWNERDISPLAY, то необходимо проделать следующее:

Обработать сообщение WM_PAINTCLIPBOARD. Это сообщение посылается владельцу буфера обмена, когда его "окно" должно быть перерисовано.

Обработать сообщение WM_SIZECLIPBOARD. Это сообщение посылается владельцу буфера обмена, когда размеры его "окна" изменились либо изменилось его содержимое.

Обычно при этом, устанавливается положение скрола и диапазон скроллирования окна буфера обмена.

Обработать сообщения WM_HSCROLLCLIPBOARD и WM_VSCROLLCLIPBOARD. Эти сообщения посылаются владельцу буфера обмена, когда "окно" буфера обмена было проскроллировано.

Обработать сообщение WM_ASKCBFORMATNAME. Это сообщение посылает буфер обмена приложению, чтобы узнать формат.

Ниже представлена оконная процедура, с обработкой этих сообщений.

Пример:

LRESULT CALLBACK MainWindowProc(hwnd, msg, wParam, lParam)

HWND hwnd;

UINT msg;

WPARAM wParam;

LPARAM lParam;

{

    static RECT rcViewer;

 

    RECT rc;

    LPRECT lprc;

    LPPAINTSTRUCT lpps;

 

    switch (msg)

    {

        //

        // Обрабатываем другие сообщения.

        //

        case WM_PAINTCLIPBOARD:

            // Определяем размер лабела.

 

            SetRect(&rc, 0, 0,

                pboxLocalClip->rcText.right + CX_MARGIN,

                pboxLocalClip->rcText.top * 2 + cyText

            );

 

            // Центрируем картинку в окне буфера обмена.

 

            if (rc.right < rcViewer.right)

            {

                rc.left = (rcViewer.right - rc.right) / 2;

                rc.right += rc.left;

            }

            if (rc.bottom < rcViewer.bottom)

            {

                rc.top = (rcViewer.bottom - rc.bottom) / 2;

                rc.bottom += rc.top;

            }

 

            // Рисуем изображение, используя структуру PAINTSTRUCT.

 

            lpps = (LPPAINTSTRUCT) GlobalLock((HGLOBAL) lParam);

            PaintLabel(lpps, pboxLocalClip, &rc);

            GlobalUnlock((HGLOBAL) lParam);

            break;

 

        case WM_SIZECLIPBOARD:

            // Записываем размеры окна в статической

            // структуре RECT.

 

            lprc = (LPRECT) GlobalLock((HGLOBAL) lParam);

            memcpy(&rcViewer, lprc, sizeof(RECT));

            GlobalUnlock((HGLOBAL) lParam);

 

            // Устанавливаем диапазон скроллирования в ноль.

 

            SetScrollRange((HWND) wParam, SB_HORZ, 0, 0, TRUE);

            SetScrollRange((HWND) wParam, SB_VERT, 0, 0, TRUE);

 

            break;

 

        case WM_ASKCBFORMATNAME:

            LoadString(hinst, IDS_OWNERDISPLAY,

                (LPSTR) lParam, wParam);

            break;

 

        default:

            return DefWindowProc(hwnd, msg, wParam, lParam);

    }

    return 0;

}

 

1. Простейшие приемы работы с буфером обмена.

Прежде чем поместить в буфер обмена какую-либо информацию, ваша программа (далее просто окно) должна его открыть, используя функцию OpenClipboard. Однако следует учесть тот факт, что одновременно только одно окно может открыть буфер обмена. Следую хорошему тону программирования - если мы что-либо открыли, то нужно и закрыть. Работа с буфером обмена не является исключением для этого правила, следовательно, когда окно закончит работу, оно должно закрыть буфер обмена, вызвав функцию CloseClipboard.

После того как программа открыла буфер обмена, она должна его очистить от предыдущего содержания, для чего следует вызвать функцию EmptyClipboard. В обязанности этой функции входит посылка сообщения WM_DESTROYCLIPBOARD предыдущему владельцу буфера обмена, освобождение ресурсов, связанных с данными в буфере обмена. После этих операций данная функция передает в монопольное использование буфер обмена окну, которое вызвало функцию OpenClipboard. 

После того как мы открыли буфер обмена и очистили его, мы имеем полное право помещать в него свои данные в различных форматах, используя функцию SetClipboardData (см. описание функции для описания стандартных форматов данных).

Чтобы извлечь информацию из буфера обмена, окно должно вызвать функцию GetClipboardData. 
Данная функция в качестве параметра принимает формат буфера обмена, для того чтобы извлечь данные в этом формате. Если в буфер обмена данные поместила другая программа, вы можете проверить доступные форматы данных перед их непосредственным извлечением, используя функцию IsClipboardFormatAvailable.

Полученную теоретическую информацию следует применить на практике. Ниже приведены примеры работы с буфером обмена, в качестве форматов данных будет использован текст и изображение, поскольку работа с буфером обмена является типичной для любого формата данных, разница лишь в способе формирования исходных данных.

Пример записи и чтения текста.

CString source; 
//в эту переменную нужно записать текст, который в дальнейшем поместится в буфер обмена
//запись текста в буфер обмена
if(OpenClipboard())//открываем буфер обмена
{
   HGLOBAL hgBuffer;
   char* chBuffer;
   EmptyClipboard(); //очищаем буфер
   hgBuffer= GlobalAlloc(GMEM_DDESHARE, source.GetLength()+1);//выделяем память
   chBuffer= (char*)GlobalLock(hgBuffer); //блокируем память
   strcpy(chBuffer, LPCSTR(source));
   GlobalUnlock(hgBuffer);//разблокируем память
   SetClipboardData(CF_TEXT, hgBuffer);//помещаем текст в буфер обмена
   CloseClipboard(); //закрываем буфер обмена
}

//чтение текста из буфера обмена
CString fromClipboard;//в эту переменную сохраним текст из буфера обмена
if ( OpenClipboard() )//открываем буфер обмена
{
   HANDLE hData = GetClipboardData(CF_TEXT);//извлекаем текст из буфера обмена
   char* chBuffer= (char*)GlobalLock(hData);//блокируем память
   fromClipboard = chBuffer;
   GlobalUnlock(hData);//разблокируем память
   CloseClipboard();//закрываем буфер обмена
}



Пример записи и чтения изображения (bitmap).

//запись изображения в буфер обмена
if ( OpenClipboard() )//открываем буфер обмена
{
   EmptyClipboard(); //очищаем буфер
   //подготовим изображение для буфера обмена
   //в качестве изображения поместим снимок рабочего стола
   CDC memDC, dc;

   HWND hwnd = ::GetDesktopWindow();
   HDC hdc = ::GetWindowDC(hwnd);

   dc.Attach(hdc);
   memDC.CreateCompatibleDC(&dc);

   CBitmap bm;
   CRect rect;

   ::GetWindowRect(hwnd, &rect);
   CSize sz(rect.Width(), rect.Height());

   bm.CreateCompatibleBitmap(&dc, sz.cx, sz.cy);
   CBitmap* oldbm = memDC.SelectObject(&bm);
   memDC.BitBlt(0, 0, sz.cx, sz.cy, &dc, 0, 0, SRCCOPY);
 
   //помещаем данные в буфер обмена
   SetClipboardData(CF_BITMAP, bm.m_hObject);
   CloseClipboard(); //закрываем буфер обмена

   memDC.SelectObject(oldbm);
   ::ReleaseDC(hwnd, dc.Detach());

}

//чтение изображения из буфера обмена
if ( OpenClipboard() )//открываем буфер обмена
{
   //извлекаем данные из буфера обмена
   HBITMAP handle = (HBITMAP)GetClipboardData(CF_BITMAP);
   CBitmap * bm = CBitmap::FromHandle(handle);

   //отображаем данные из буфера
   BITMAP bit; 
   bm->GetBitmap(&bit);
   CClientDC cdc(this);
   CDC dc;
   dc.CreateCompatibleDC(&cdc);
   dc.SelectObject(bm);
   cdc.BitBlt(0, 0, bit.bmWidth, bit.bmHeight, &dc, 0, 0, SRCCOPY);

   CloseClipboard();//закрываем буфер обмена
}



Подводя итог этого раздела можно выделить следующие основные шаги для записи и чтения данных при работе с буфером обмена. Для того чтобы записать данные в буфер обмена, вы должны выполнить следующую последовательность действий:

  1.  Открыть буфер обмена функцией OpenClipboard
  2.  Очистить содержимое буфера обмена функцией EmptyClipboard
  3.  Заказать функцией GlobalAlloc глобальный блок памяти, имеющий размер, достаточный для размещения записываемых в буфер обмена данных
  4.  Заблокировать полученный блок памяти функцией GlobalLock
  5.  Записать в заблокированный блок памяти данные
  6.  Разблокировать блок памяти функцией GlobalUnlock
  7.  Поместить данные в буфер обмена функцией SetClipboardData
  8.  Закрыть буфер обмена функцией CloseClipboard


Процедура чтения данных из буфера обмена тоже проста. Приложение должно сделать следующее:

  1.  Открыть буфер обмена функцией OpenClipboard
  2.  Вызвать функцию GetClipboardData
  3.  Заблокировать блок памяти, идентификатор которого получен от функции GetClipboardData, функцией GlobalLock
  4.  Переписать данные из заблокированного буфера данных Clipboard в буфер, заказанный специально для этого приложением
  5.  Разблокировать блок памяти, идентификатор которого получен от функции GetClipboardData, функцией GlobalUnlock
  6.  Закрыть буфер обмена функцией CloseClipboard
  7.  Использовать макроопределения иногда удобно. Во многих случаях, конечно, стоит предпочесть использование параметризованных функций (шаблонов) и других механизмов, обеспечивающих проверку типов. Но использование препроцессора и макросов также имеет свою сферу применения.
  8.  Макроопределения и их недостатки
  9.  Например, функции отладки и трассировки. Согласитесь, что это довольно удобно:

#ifdef USE_MY_TRACE
#define TRACE(a) CallTrace(a)
#else
#define TRACE(a) ((void)0)
#endif

  1.  
    Если USE_MY_TRACE не определено, вызовы CallTrace просто будут исключены на уровне препроцессора, а оптимизация при компоновке просто не включит нигде теперь не используемую функцию CallTrace в конечный исполняемый код программы. Удобно, но...
    Обратим внимание на семейство макросов TRACE0, TRACE1, TRACE2, TRACE3, объявленных в MFC. Невозможность обойтись одним универсальным макросом объясняется следующими правилами синтаксиса:

    1. При объявлении нескольких макросов с одинаковым именем препроцессор использует последнее определение и выводит предупреждения на этапе трансляции.

    2. Следует из первого - в отличие от функций, макросы с одинаковыми именами, но различным числом или типом параметров, недопустимы.

    3. Синтаксическая запись произвольного числа параметров (многоточие, '...') для макроопределений недопустима и является синтаксической ошибкой.
  2.  Умные макроопределения
  3.  Оказывается, преодолеть названные выше недостатки макросов совершенно несложно. Сделать это можно при помощи простого трюка - использования класса, чем-то напоминающего так называемую идиому функторов. Это класс, для которого определен набор операторов "скобки". Итак, например, вот такой класс:

class MacroCall
{
public:

    MacroCall()
    {
    }

    void operator()(float val) const
    {
        printf("Float: %f\r\n", val);
    }

    void operator()(int val) const
    {
        printf("Integer: %d\r\n", val);
    }

    void operator() (const char *pszFmt, ...) const
    {
        if ( pszFmt == NULL || *pszFmt == 0 )
            return;

        va_list args; 
        va_start(args, pszFmt);

        int size_msgbuf = _vscprintf(pszFmt, args) + 1;
        char* msgbuf = new char[size_msgbuf];
        vsprintf(msgbuf, pszFmt, args);

        printf(msgbuf);

        delete[] msgbuf;
        va_end(args);
    }
};

  1.  А теперь объявим макроопределение:

#ifdef USE_MACRO
#define MYMACRO MacroCall()
#else
#define MYMACRO __noop
#endif

  1.  И, наконец, пример использования:

MYMACRO("%s : %d\r\n", "Value", 10);
MYMACRO(55);
MYMACRO(3.1415926f);

  1.  
  2.  Краткое обьяснение
  3.  Всё очень просто. Строка вызова макроопределения 

MYMACRO(55);

  1.  заменяется препроцессором на вызов

MacroCall()(55);

  1.  Т.е. вызываются конструктор и соответствующий «оператор скобки». Можно записать так:

MacroCall().operator()(55);

  1.  
    Заметим, что вызывается тот «оператор скобки», который соответствует типу и количеству аргументов – соответственно, для типов float и int разные при внешне одинаковом вызове одного и того же макроса:

MYMACRO(55);        // вызван operator()(int val)
MYMACRO(3.1415926f);    // 
вызван operator()(float val)
// operator() (const char *pszFmt, ...)
MYMACRO("%s : %d\r\n", "Value", 10);

  1.  
  2.  Дополнительные замечания
  3.  1. Обратим внимание на то, что в случае, если макрос MYMACRO не используется, он заменяется на __noop, специально введенный в компиляторе от Microsoft. Согласно документации, он позволяет компилятору правильно «проигнорировать» ненужный теперь список аргументов вызова при произвольном числе аргументов.
    Однако если компилятор не поддерживает __noop или нечто аналогичное, можно просто определять неиспользуемый макрос как «пустой» или как ((void)0):

#define MYMACRO

  1.  или 

#define MYMACRO ((void)0)

  1.  

    2. Сам вызов конструктора тоже может быть использован для дополнительных аргументов. Например:

class MacroCallLine
{
public:

    MacroCallLine(int L) : line_num(L)
    {
    }

    void operator()(const char* msg) const
    {
        printf("Line: %d Message: %s\r\n", line_num, msg);
    }

protected:
    int  line_num;
};

  1.  
    Теперь определим макрос:

#define TRACEMSG  MacroCallLine(__LINE__)

  1.  
    Макрос теперь автоматически получает номер строки вызова.
    И если его использовать где-нибудь в программе:

TRACEMSG("My message");

  1.  то мы получим что-то вроде следующего:
    Line: 10 Message: My message

    Замечу, что кроме __LINE__, определены также
    __DATE____FILE__ и многое другое, и что особенно ценно на мой взгляд, __FUNCTION__. Замечу, что __FUNCTION__ работает удивительно корректно, возвращая имя класса и имя метода, разделенных '::'. Причем всё вышеназванное работает и для release версии, открывая прекрасные возможности для трассировки и доводки.
  2.  И напоследок
  3.  1. Еще раз хочу обратить внимание: объявление макроса – это вызов конструктора, возможно с параметрами. Сам макрос не предполагает передачу аргументов. Например, был объявлен макрос в виде:

#define MYMACRO MacroCall()

  1.  
    Если объявить в следующей форме:

#define MYMACRO(p) MacroCall(p) // ЭТО УЖЕ ДРУГОЙ ВЫЗОВ

  1.  
    то это уже другая техника и другой случай, то есть это уже не вызов «оператора скобки», на котором всё и базируется.

    2. В классе MacroCall показан пример использования произвольного числа аргументов и форматирования при помощи vsprintf. Подробно обьяснять этот фрагмент я не буду, поскольку эти функции подробно описаны в MSDN.

О буфере обмена простыми словами — для новичков

Когда мы наводим на какой-то файл или папку курсор, нажимаем правой кнопкой мыши и выбираем «Копировать» (или сочетание клавиш Ctrl+C), в этом момент информация помещается в буфер обмена.

Туда мы можем копировать все что угодно: текст, папку, картинки, видео-файлы, аудиозаписи и прочие файлы. Компьютер выделит нужное место в оперативной памяти для хранения в буфере нужного объема информации.

Можно представить что это оперативная память компьютера, или своеобразная невидимая область, куда помещается информация на время, а затем удаляется.

То есть, когда мы перейдем в нужное место на нашем компьютере и опять же нажмем правой кнопкой мыши, но выберем уже «Вставить» (или сочетание клавиш Ctrl+V), то та папка, или файл, или кусок текста, который вы скопировали, возьмется из буфера обмена и вставится в нужное вам место.

А это значит, что начинающему пользователь не стоит беспокоиться о том, где именно это информация храниться. Главное, что мы можем вставить ее в нужное нам место.

Следует также понимать, что когда вы опять нажимаете «Копировать», то старая информация в буфере заменяется на новую и при вставке, естественно, вставляется новая. Тоже самое происходит, если вы что-то «вырезаете».

Вы также можете вставлять информацию из буфера любое количество раз, т.е. при вставке информация от-туда не удаляется, она как-бы копируется.

И если вы скопировали папку, то вы можете вставить ее и на диск С, и на диск Е и в любое другое место на компьютере.

Также обратите внимание на то, что если вы скопировали в буфер обмена какой-то кусок текста из документа, допустим программы Microsoft Word, или веб-страницы, то вам нужно в документ или текстовое поле его и вставлять. Просто в папку вставить не получится.

И наоборот, скопированную папку в документ тоже вы не вставите.

Также будьте осторожны с важной информацией, которую вы скопировали в буфер: если компьютер неожиданно выключится, перезагрузится, или вы случайно забудете и выключите его, то информация из буфера обмана удалиться.

Поэтому сразу же когда что-то поместили в него, ставьте в нужное вам место и сохраните, если это какой-то документ.

Чистить буфер не обязательно, ведь копируя какой-то файл, или текст, он заменяет предыдущий и поэтому память вашего компьютера засорятся не будет.

Комбинации клавиш, которые необходимы для работы

Ctrl+A

Выделить всё. Это могут быть все папки, весь текст, или все файлы в папке

Ctrl+C

Копировать выделенный документ или документы, файлы в папке

Ctrl+X

Вырезать выделенное. Аналогично предыдущему пункту, только вырезаем

Ctrl+V

Вставить все скопированные или вырезанное, что находится в буфере обмена

Ответ о нахождении буфера обмена для более продвинутых

Возможно среди читателей есть и те, кому хочется узнать, где находится та секретная папка, или то невидимое пространство, называемое буфером обмена.

Находим буфер обмена в Windows XP

В операционной системе Windows XP вы можете зайти на диск C, или на тот диск, на котором она находится, затем в папку «Documents and Settings», а дальше в «System 32″, то есть путь такой: «C:/Documents and Settings/System 32″.

Там есть файл (специальная программа) clipbrd.exe, запустив которую, вы можете увидеть что там находиться именно то, что вы скопировали.

Быстрее найти этот файл и запустить вы можете даже не заходя в папку «System 32″, а просто войти в меню «Пуск» > «Выполнить», ввести clipbrd.exe и нажать клавишу ввода.

Сейчас приведу пример работы этой программы. Я выделю фрагмент текста и нажму «копировать».

Практическое руководство. Добавление данных в буфер обмена

.NET Framework 4.5

Другие версии

Эта тема еще не получила оценку - Оценить эту тему

Класс Clipboard предоставляет методы, которые можно использовать для взаимодействия с функцией буфера обмена операционной системы Windows.Многие приложения используют буфер обмена в качестве временного хранилища данных. Например, текстовые процессоры используют буфер обмена во время операций вырезания и вставки. Буфер обмена также полезен для передачи данных из одного приложения в другое.

При добавлении данных в буфер обмена можно указать формат данных таким образом, чтобы другие приложения могли распознать данные, если они могут использовать этот формат. Можно также добавить данные в буфер обмена в нескольких различных форматах, чтобы увеличить количество других приложений, которые потенциально могут использовать данные.

Формат буфера обмена является строкой, которая определяет формат таким образом, чтобы приложение, использующее этот формат, могло получать связанные данные. Класс DataFormats предоставляет предварительно определенные имена форматов для использования. Можно также использовать собственные имена форматов или использовать тип объекта в качестве его формата.

Чтобы добавить данные в буфер обмена в одном или нескольких форматах, используйте метод SetDataObject. Этому методу можно передать любой объект, но для добавления данных в нескольких форматах необходимо сначала добавить данные в отдельный объект, предназначенный для работы с несколькими форматами данных. Обычно данные добавляются к объекту DataObject, но можно использовать любой тип, реализующий интерфейсIDataObject.

В .NET Framework 2.0 можно добавить данные непосредственно в буфер обмена с помощью новых методов, позволяющих упростить основные задачи при работе с буфером обмена. Используйте эти методы при работе с данными в одном общем формате, такими как текст.

 Примечание

Все приложения Windows используют один буфер обмена. Поэтому содержимое буфера обмена может изменяться при переходе к другому приложению.

Класс Clipboard может использоваться только в потоках в режиме однопотокового подразделения. Чтобы использовать этот класс, убедитесь, что используемый метод Main помечен атрибутом STAThreadAttribute.

Для помещения в буфер обмена объект должен быть сериализуемым. Чтобы сделать тип сериализуемым, его необходимо пометить атрибутомSerializableAttribute. Если методу буфера обмена передается не сериализуемый объект, метод завершится неудачей без создания исключения.Дополнительные сведений о сериализации см. в разделе System.Runtime.Serialization.

Чтобы добавить данные в буфер обмена в одном общем формате, выполните следующие действия.

  1.  Используйте метод SetAudioSetFileDropListSetImage или SetTextЭти методы доступны только в .NET Framework 2.0.

C#

VB

// Demonstrates SetAudio, ContainsAudio, and GetAudioStream.

public System.IO.Stream SwapClipboardAudio(

   System.IO.Stream replacementAudioStream)

{

   System.IO.Stream returnAudioStream = null;

   if (Clipboard.ContainsAudio())

   {

       returnAudioStream = Clipboard.GetAudioStream();

       Clipboard.SetAudio(replacementAudioStream);

   }

   return returnAudioStream;

}

// Demonstrates SetFileDropList, ContainsFileDroList, and GetFileDropList

public System.Collections.Specialized.StringCollection

   SwapClipboardFileDropList(

   System.Collections.Specialized.StringCollection replacementList)

{

   System.Collections.Specialized.StringCollection returnList = null;

   if (Clipboard.ContainsFileDropList())

   {

       returnList = Clipboard.GetFileDropList();

       Clipboard.SetFileDropList(replacementList);

   }

   return returnList;

}

// Demonstrates SetImage, ContainsImage, and GetImage.

public System.Drawing.Image SwapClipboardImage(

   System.Drawing.Image replacementImage)

{

   System.Drawing.Image returnImage = null;

   if (Clipboard.ContainsImage())

   {

       returnImage = Clipboard.GetImage();

       Clipboard.SetImage(replacementImage);

   }

   return returnImage;

}

// Demonstrates SetText, ContainsText, and GetText.

public String SwapClipboardHtmlText(String replacementHtmlText)

{

   String returnHtmlText = null;

   if (Clipboard.ContainsText(TextDataFormat.Html))

   {

       returnHtmlText = Clipboard.GetText(TextDataFormat.Html);

       Clipboard.SetText(replacementHtmlText, TextDataFormat.Html);

   }

   return returnHtmlText;

}

Чтобы добавить данные в буфер обмена в пользовательском формате, выполните следующие действия.

  1.  Используйте метод SetData с именем пользовательского формата. Этот метод доступен только в .NET Framework 2.0.

Можно также использовать предварительно определенные имена форматов с помощью метода SetData. Дополнительные сведения см. в разделеDataFormats.

C#

VB

// Demonstrates SetData, ContainsData, and GetData

// using a custom format name and a business object.

public Customer TestCustomFormat

{

   get

   {

       Clipboard.SetData("CustomerFormat", new Customer("Customer Name"));

       if (Clipboard.ContainsData("CustomerFormat"))

       {

           return Clipboard.GetData("CustomerFormat") as Customer;

       }

       return null;

   }

}

...

[Serializable]

public class Customer

{

   private string nameValue = string.Empty;

   public Customer(String name)

   {

       nameValue = name;

   }

   public string Name

   {

       get { return nameValue; }

       set { nameValue = value; }

   }

}

Чтобы добавить данные в буфер обмена в нескольких форматах, выполните следующие действия.

  1.  Используйте метод SetDataObject и передайте ему объект DataObject, содержащий данные. Необходимо использовать этот метод для добавления данных в буфер обмена для более ранних версий, чем .NET Framework 2.0.

C#

VB

// Demonstrates how to use a DataObject to add

// data to the Clipboard in multiple formats.

public void TestClipboardMultipleFormats()

{

   DataObject data = new DataObject();

   // Add a Customer object using the type as the format.

   data.SetData(new Customer("Customer as Customer object"));

   // Add a ListViewItem object using a custom format name.

   data.SetData("CustomFormat",

       new ListViewItem("Customer as ListViewItem"));

   Clipboard.SetDataObject(data);

   DataObject retrievedData = (DataObject)Clipboard.GetDataObject();

   if (retrievedData.GetDataPresent("CustomFormat"))

   {

       ListViewItem item =

           retrievedData.GetData("CustomFormat") as ListViewItem;

       if (item != null)

       {

           MessageBox.Show(item.Text);

       }

   }

   if (retrievedData.GetDataPresent(typeof(Customer)))

   {

       Customer customer =

           retrievedData.GetData(typeof(Customer)) as Customer;

       if (customer != null)

       {

           MessageBox.Show(customer.Name);

       }

   }

}

...

[Serializable]

public class Customer

{

   private string nameValue = string.Empty;

   public Customer(String name)

   {

       nameValue = name;

   }

   public string Name

   {

       get { return nameValue; }

       set { nameValue = value; }

   }

}

Операции перетаскивания и поддержка буфера обмена

.NET Framework 4.5

Другие версии

Эта тема еще не получила оценку - Оценить эту тему

Пользовательские операции перетаскивания в приложении Windows можно включить, обрабатывая последовательность событий, которая, как правило, включает события DragEnter, DragLeave и DragDrop.

Можно также реализовать поддержку вырезания/копирования/вставки и передачу данных пользователя в буфер обмена в Windows-приложения с помощью вызова простых методов.

В этом подразделе

Пример. Выполнение операции перетаскивания в Windows Forms

Содержит объяснение того, как начать операцию перетаскивания.

Практическое руководство. Выполнение операции перетаскивания между приложениями

Примеры способов выполнения операций перетаскивания между приложениями.

Практическое руководство. Добавление данных в буфер обмена

Содержит описание способа вставки данных в буфер обмена программными средствами.

Практическое руководство. Извлечение данных из буфера обмена

Описание способов доступа к данным, хранящимся в буфере обмена.

Связанные подразделы

Функциональная возможность перетаскивания в Windows Forms

Описывает методы, события и классы, используемые для реализации перетаскивания.

QueryContinueDrag

Описание структуры события, с помощью которого запрашивается разрешение на продолжение операции перетаскивания.

DoDragDrop

Описание структуры метода, который необходим для начала операции перетаскивания.

Clipboard

Topic

Location

Практическое руководство. Отправка данных в активную дочернюю MDI-форму

Программирование Windows Forms

Практическое руководство. Отправка данных в активную дочернюю MDI-форму

dv_ManCli

Практическое руководство. Отправка данных в активную дочернюю MDI-форму

dv_ManCli

Функциональная возможность перетаскивания в Windows Forms

.NET Framework 4.5

Другие версии

Эта тема еще не получила оценку - Оценить эту тему

Windows Forms включает набор методов, событий и классов, реализующих поведение перетаскивания. В этом разделе приводится обзор поддержки перетаскивания в Windows Forms.

Выполнение операций перетаскивания

Для выполнения операции перетаскивания используйте метод DoDragDrop класса Control. Дополнительные сведения о том, как выполняется операция перетаскивания, см. в разделе DoDragDrop. Для получения прямоугольника, над которым должен быть перемещен указатель мыши перед началом операции перетаскивания, используется свойство DragSize класса SystemInformation.

События, относящиеся к операциям перетаскивания

В операции перетаскивания имеется две категории событий: события, возникающие в текущей цели операции перетаскивания, и события, возникающие в источнике операции перетаскивания.

События в текущей цели

В приведенной ниже таблице содержатся события, возникающие в текущей цели операции перетаскивания.

Событие мыши

Описание

DragEnter

Событие происходит при перемещении объекта внутрь границ элемента управления. Обработчик этого события получает аргумент типа DragEventArgs.

DragOver

Это событие происходит, когда объект перетаскивается во время расположения указателя мыши в пределах границ элемента управления. Обработчик этого события получает аргумент типа DragEventArgs.

DragDrop

Это событие возникает после завершения операции перетаскивания. Обработчик этого события получает аргумент типа DragEventArgs.

DragLeave

Событие возникает при перемещении объекта за границы элемента управления. Обработчик этого события получает аргумента типаEventArgs.

В классе DragEventArgs содержится расположение указателя мыши, текущее состояние кнопок мыши и клавиш CTRL, SHIFT и ALT, перетаскиваемые данные и значения DragDropEffects, которые указывают, какие операции допускаются источником события перетаскивания и результат перетаскивания на цель для операции.

События в источнике

В следующей таблице приведены события, возникающие в источнике операции перетаскивания.

Событие мыши

Описание

GiveFeedback

Это событие возникает во время операции перетаскивания. Оно предоставляет возможность дать пользователю визуальную подсказку о том, что происходит операция перетаскивания, в виде, например, изменения указателя мыши. Обработчик этого события получает аргумент типа GiveFeedbackEventArgs.

QueryContinueDrag

Это событие возникает во время операции перетаскивания и позволяет источнику перетаскиваемого объекта определить, следует ли отменить эту операцию. Обработчик этого события получает аргумент типа QueryContinueDragEventArgs.

В классе QueryContinueDragEventArgs содержится текущее состояние кнопок мыши и клавиш CTRL, SHIFT и ALT, значение, указывающее, была ли нажата клавиша ESC, и значение DragAction, которое может быть установлено для указания того, следует ли продолжать операцию перетаскивания.

Control.QueryContinueDrag - событие

.NET Framework 4.5

Другие версии

Эта тема еще не получила оценку - Оценить эту тему

Происходит во время операции перетаскивания и позволяет источнику перетаскивания определить, следует ли отменить эту операцию.

Пространство имен:  System.Windows.Forms
Сборка:  System.Windows.Forms (в System.Windows.Forms.dll)

Синтаксис

C#

C++

F#

VB

public event QueryContinueDragEventHandler QueryContinueDrag

Заметки

Событие QueryContinueDrag происходит при изменении состояния клавиатуры или кнопки мыши во время операции перетаскивания. СобытиеQueryContinueDrag позволяет источнику операции перетаскивания определить, следует ли отменить эту операцию.

Далее описывается, как и когда вызываются события, связанные с операциями перетаскивания.

Метод DoDragDrop определяет элемент управления при текущем местоположении курсора. Затем он проверяет, является ли элемент управления допустимым для конечного местоположения объекта перетаскивания.

Если элемент управления является допустимым для конечного местоположения объекта перетаскивания, вызывается событие GiveFeedback с указанным эффектом перетаскивания. Список эффектов перетаскивания см. в перечислении DragDropEffects.

Отслеживаются изменения позиции курсора мыши, состояния клавиатуры и кнопки мыши.

  1.  Если пользователь перемещает курсор мыши за пределы окна, происходит событие DragLeave.
  2.  Если курсор мыши перемещается на другой элемент управления, для этого элемента вызывается событие DragEnter.
  3.  При перемещении мыши в пределах одного элемента управления происходит событие DragOver.

При изменении состояния клавиатуры или кнопки мыши происходит событие QueryContinueDrag, определяющее, следует ли продолжить или завершить перетаскивание либо отменить операцию на основании значения свойства Action аргумента QueryContinueDragEventArgs данного события.

  1.  Если значение объекта DragAction равно Continue, возникает событие DragOver, чтобы продолжить операцию, и событие GiveFeedback с новым эффектом, что позволяет задать соответствующую визуальную обратную связь. Список допустимых эффектов перетаскивания см. в перечислении DragDropEffects.

Примечание

События DragOver и GiveFeedback объединены в пару, чтобы при перемещении мыши над конечным местоположением перетаскивания отображались самые последние сведения о расположении мыши.

  1.  Если значение DragAction равно Drop, значение эффекта перетаскивания возвращается источнику, благодаря чему исходное приложение может выполнить соответствующую операцию с исходными данными (например, вырезать данные, если это была операция перемещения).
  2.  Если значение DragAction равно Cancel, происходит событие DragLeave.

По умолчанию событие QueryContinueDrag задает для свойства Action значение Cancel в объекте DragAction, если была нажата клавиша ESC, и задает для свойства Action значение Drop в объекте DragAction, если была нажата левая, средняя или правая кнопка мыши.

Дополнительные сведения об обработке событий см. в разделе Прием событий.

Примеры

В следующем примере кода показана операция перетаскивания между двумя элементами управления ListBox. В примере во время запуска действия перетаскивания вызывается метод DoDragDrop. Действие перетаскивания начинается, если во время события MouseDown мышь сместилась относительно своего положения больше чем на SystemInformation.DragSize. Метод IndexFromPoint применяется для определения индекса элемента, перетаскиваемого во время события MouseDown.

В этом примере также показывается использование пользовательских курсоров для операции перетаскивания. В данном примере требуется, чтобы в каталоге приложения имелось два файла курсоров: 3dwarro.cur и 3dwno.cur — первый для пользовательского курсора перетаскивания, а второй для курсора с запрещенным перетаскиванием. Пользовательские курсоры используются, если установлен флажок CheckBoxUseCustomCursorsCheck.Пользовательские курсоры задаются в обработчике событий GiveFeedback.

Состояние клавиатуры проверяется в обработчике события DragOver для правого элемента ListBox, чтобы на основании состояния клавиш SHIFT, CTRL, ALT или сочетания клавиш CTRL+ALT определить, какая операция перетаскивания будет выполнена. Расположение в элементе управленияListBox, куда будет перемещен объект, также определяется во время события DragOver. Если перетаскиваемые данные не относятся к типу String, для свойства DragEventArgs.Effect задается значение None в объекте DragDropEffects. Наконец, состояние перетаскивания отображается в объектеLabelDropLocationLabel.

Данные, перетаскиваемые в правый элемент управления ListBox, определяются в обработчике событий DragDrop, и значение типа String добавляется в соответствующее место списка ListBox. Если перетаскиваемый объект перемещается за пределы формы, то операция перетаскивания отменяется в обработчике событий QueryContinueDrag.

В данном фрагменте кода показано использование события QueryContinueDrag. Полный пример кода см. в описании метода DoDragDrop.

C#

C++

VB

private void ListDragSource_QueryContinueDrag(object sender, System.Windows.Forms.QueryContinueDragEventArgs e) {

   // Cancel the drag if the mouse moves off the form.

   ListBox lb = sender as ListBox;

   if (lb != null) {

       Form f = lb.FindForm();

       // Cancel the drag if the mouse moves off the form. The screenOffset

       // takes into account any desktop bands that may be at the top or left

       // side of the screen.

       if (((Control.MousePosition.X - screenOffset.X) < f.DesktopBounds.Left) ||

           ((Control.MousePosition.X - screenOffset.X) > f.DesktopBounds.Right) ||

           ((Control.MousePosition.Y - screenOffset.Y) < f.DesktopBounds.Top) ||

           ((Control.MousePosition.Y - screenOffset.Y) > f.DesktopBounds.Bottom)) {

           e.Action = DragAction.Cancel;

       }

   }

}

Сведения о версии

.NET Framework

Поддерживается в версиях: 4.5, 4, 3.5, 3.0, 2.0, 1.1, 1.0

.NET Framework (клиентский профиль)

Поддерживается в версиях: 4, 3.5 с пакетом обновления 1 (SP1)

Платформы

Windows 8, Windows Server 2012, Windows 7, Windows Vista с пакетом обновления 2 (SP2), Windows Server 2008 (роль основных серверных компонентов не поддерживается), Windows Server 2008 R2 (роль основных серверных компонентов поддерживается в пакете обновления 1 (SP1) или выше; системы на базе Itanium не поддерживаются)

.NET Framework поддерживает не все версии каждой платформы. Поддерживаемые версии перечислены в разделе Требования к системе для .NET Framework.

Control.DoDragDrop - метод

.NET Framework 4.5

Другие версии

Эта тема еще не получила оценку - Оценить эту тему

Начинает операцию перетаскивания.

Пространство имен:  System.Windows.Forms
Сборка:  System.Windows.Forms (в System.Windows.Forms.dll)

Синтаксис

C#

C++

F#

VB

[UIPermissionAttribute(SecurityAction.Demand, Clipboard = UIPermissionClipboard.OwnClipboard)]

public DragDropEffects DoDragDrop(

Object data,

DragDropEffects allowedEffects

)

Параметры

data

ТипSystem.Object
Перетаскиваемые данные

allowedEffects

ТипSystem.Windows.Forms.DragDropEffects
Одно из значений DragDropEffects

Возвращаемое значение

Тип: System.Windows.Forms.DragDropEffects
Значение перечисления 
DragDropEffects, представляющее конечный результат выполнения операции перетаскивания.

Заметки

Параметр allowedEffects определяет, какие операции перетаскивания возможны. Если операции перетаскивания требуется взаимодействие с приложениями в другом процессе, данные должны быть либо экземпляром базового управляемого класса (StringBitmap или Metafile), либо объектом, реализующим ISerializable или IDataObject.

Далее описывается, как и когда вызываются события, связанные с операциями перетаскивания.

Метод DoDragDrop определяет элемент управления при текущем местоположении курсора. Затем он проверяет, является ли элемент управления допустимым для конечного местоположения объекта перетаскивания.

Если элемент управления является допустимым для конечного местоположения объекта перетаскивания, вызывается событие GiveFeedback с указанным эффектом перетаскивания. Список эффектов перетаскивания см. в перечислении DragDropEffects.

Отслеживаются изменения позиции курсора мыши, состояния клавиатуры и кнопки мыши.

  1.  Если пользователь перемещает курсор мыши за пределы окна, происходит событие DragLeave.
  2.  Если курсор мыши перемещается на другой элемент управления, для этого элемента вызывается событие DragEnter.
  3.  При перемещении мыши в пределах одного элемента управления происходит событие DragOver.

При изменении состояния клавиатуры или кнопки мыши происходит событие QueryContinueDrag, определяющее, следует ли продолжить или завершить перетаскивание либо отменить операцию на основании значения свойства Action аргумента QueryContinueDragEventArgs данного события.

  1.  Если значение объекта DragAction равно Continue, возникает событие DragOver, чтобы продолжить операцию, и событие GiveFeedback с новым эффектом, что позволяет задать соответствующую визуальную обратную связь. Список допустимых эффектов перетаскивания см. в перечислении DragDropEffects.

Примечание

События DragOver и GiveFeedback объединены в пару, чтобы при перемещении мыши над конечным местоположением перетаскивания отображались самые последние сведения о расположении мыши.

  1.  Если значение DragAction равно Drop, значение эффекта перетаскивания возвращается источнику, благодаря чему исходное приложение может выполнить соответствующую операцию с исходными данными (например, вырезать данные, если это была операция перемещения).
  2.  Если значение DragAction равно Cancel, происходит событие DragLeave.

Примечание

Метод DoDragDrop фиксирует все исключения и отображает повторно только следующие исключения безопасности или критические исключения:

  1.  SecurityException
  2.  NullReferenceException
  3.  StackOverflowException
  4.  OutOfMemoryException
  5.  ThreadAbortException
  6.  ExecutionEngineException
  7.  IndexOutOfRangeException
  8.  AccessViolationException

Примеры

В следующем примере кода показана операция перетаскивания между двумя элементами управления ListBox. В примере во время запуска действия перетаскивания вызывается метод DoDragDrop. Действие перетаскивания начинается, если во время события MouseDown мышь сместилась относительно своего положения больше чем на SystemInformation.DragSize. Метод IndexFromPoint применяется для определения индекса элемента, перетаскиваемого во время события MouseDown.

В этом примере также показывается использование пользовательских курсоров для операции перетаскивания. В данном примере требуется, чтобы в каталоге приложения имелось два файла курсоров: 3dwarro.cur и 3dwno.cur — первый для пользовательского курсора перетаскивания, а второй для курсора с запрещенным перетаскиванием. Пользовательские курсоры используются, если установлен флажок CheckBoxUseCustomCursorsCheck.Пользовательские курсоры задаются в обработчике событий GiveFeedback.

Состояние клавиатуры проверяется в обработчике события DragOver для правого элемента ListBox, чтобы на основании состояния клавиш SHIFT, CTRL, ALT или сочетания клавиш CTRL+ALT определить, какая операция перетаскивания будет выполнена. Расположение в элементе управленияListBox, куда будет перемещен объект, также определяется во время события DragOver. Если перетаскиваемые данные не относятся к типу String, для свойства DragEventArgs.Effect задается значение None в объекте DragDropEffects. Наконец, состояние перетаскивания отображается в объектеLabelDropLocationLabel.

Данные, перетаскиваемые в правый элемент управления ListBox, определяются в обработчике событий DragDrop, и значение типа String добавляется в соответствующее место списка ListBox. Если перетаскиваемый объект перемещается за пределы формы, то операция перетаскивания отменяется в обработчике событий QueryContinueDrag.

C#

C++

VB

using System;

using System.Drawing;

using System.Windows.Forms;

namespace Snip_DragNDrop

{

   public class Form1 : System.Windows.Forms.Form

   {

       private System.Windows.Forms.ListBox ListDragSource;

       private System.Windows.Forms.ListBox ListDragTarget;

       private System.Windows.Forms.CheckBox UseCustomCursorsCheck;

       private System.Windows.Forms.Label DropLocationLabel;

       private int indexOfItemUnderMouseToDrag;

       private int indexOfItemUnderMouseToDrop;        

       private Rectangle dragBoxFromMouseDown;

       private Point screenOffset;

       private Cursor MyNoDropCursor;

       private Cursor MyNormalCursor;

       /// The main entry point for the application.

       [STAThread]

       static void Main()

       {

           Application.Run(new Form1());

       }

       public Form1()

       {

           this.ListDragSource = new System.Windows.Forms.ListBox();

           this.ListDragTarget = new System.Windows.Forms.ListBox();

           this.UseCustomCursorsCheck = new System.Windows.Forms.CheckBox();

           this.DropLocationLabel = new System.Windows.Forms.Label();

           this.SuspendLayout();

           // ListDragSource

           this.ListDragSource.Items.AddRange(new object[] {"one", "two", "three", "four",

                                                               "five", "six", "seven", "eight",

                                                               "nine", "ten"});

           this.ListDragSource.Location = new System.Drawing.Point(10, 17);

           this.ListDragSource.Size = new System.Drawing.Size(120, 225);

           this.ListDragSource.MouseDown += new System.Windows.Forms.MouseEventHandler(this.ListDragSource_MouseDown);

           this.ListDragSource.QueryContinueDrag += new System.Windows.Forms.QueryContinueDragEventHandler(this.ListDragSource_QueryContinueDrag);

           this.ListDragSource.MouseUp += new System.Windows.Forms.MouseEventHandler(this.ListDragSource_MouseUp);

           this.ListDragSource.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ListDragSource_MouseMove);

           this.ListDragSource.GiveFeedback += new System.Windows.Forms.GiveFeedbackEventHandler(this.ListDragSource_GiveFeedback);

           // ListDragTarget

           this.ListDragTarget.AllowDrop = true;

           this.ListDragTarget.Location = new System.Drawing.Point(154, 17);

           this.ListDragTarget.Size = new System.Drawing.Size(120, 225);

           this.ListDragTarget.DragOver += new System.Windows.Forms.DragEventHandler(this.ListDragTarget_DragOver);

           this.ListDragTarget.DragDrop += new System.Windows.Forms.DragEventHandler(this.ListDragTarget_DragDrop);

           this.ListDragTarget.DragEnter += new System.Windows.Forms.DragEventHandler(this.ListDragTarget_DragEnter);

           this.ListDragTarget.DragLeave += new System.EventHandler(this.ListDragTarget_DragLeave);

           // UseCustomCursorsCheck

           this.UseCustomCursorsCheck.Location = new System.Drawing.Point(10, 243);

           this.UseCustomCursorsCheck.Size = new System.Drawing.Size(137, 24);

           this.UseCustomCursorsCheck.Text = "Use Custom Cursors";

           // DropLocationLabel

           this.DropLocationLabel.Location = new System.Drawing.Point(154, 245);

           this.DropLocationLabel.Size = new System.Drawing.Size(137, 24);

           this.DropLocationLabel.Text = "None";

           // Form1

           this.ClientSize = new System.Drawing.Size(292, 270);

           this.Controls.AddRange(new System.Windows.Forms.Control[] {this.ListDragSource,

                                                       this.ListDragTarget, this.UseCustomCursorsCheck,

                                                       this.DropLocationLabel});

           this.Text = "drag-and-drop Example";

           this.ResumeLayout(false);

       }

       private void ListDragSource_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)

       {

           // Get the index of the item the mouse is below.

           indexOfItemUnderMouseToDrag = ListDragSource.IndexFromPoint(e.X, e.Y);

           if (indexOfItemUnderMouseToDrag != ListBox.NoMatches) {

               // Remember the point where the mouse down occurred. The DragSize indicates

               // the size that the mouse can move before a drag event should be started.                

               Size dragSize = SystemInformation.DragSize;

               // Create a rectangle using the DragSize, with the mouse position being

               // at the center of the rectangle.

               dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width /2),

                                                              e.Y - (dragSize.Height /2)), dragSize);

           } else

               // Reset the rectangle if the mouse is not over an item in the ListBox.

               dragBoxFromMouseDown = Rectangle.Empty;

       }

       private void ListDragSource_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) {

           // Reset the drag rectangle when the mouse button is raised.

           dragBoxFromMouseDown = Rectangle.Empty;

       }

       private void ListDragSource_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)

       {

           if ((e.Button & MouseButtons.Left) == MouseButtons.Left) {

               // If the mouse moves outside the rectangle, start the drag.

               if (dragBoxFromMouseDown != Rectangle.Empty &&

                   !dragBoxFromMouseDown.Contains(e.X, e.Y)) {

                   // Create custom cursors for the drag-and-drop operation.

                   try {

                       MyNormalCursor = new Cursor("3dwarro.cur");

                       MyNoDropCursor = new Cursor("3dwno.cur");

                   } catch {

                       // An error occurred while attempting to load the cursors, so use

                       // standard cursors.

                       UseCustomCursorsCheck.Checked = false;

                   }finally {

                       // The screenOffset is used to account for any desktop bands

                       // that may be at the top or left side of the screen when

                       // determining when to cancel the drag drop operation.

                       screenOffset = SystemInformation.WorkingArea.Location;

                       // Proceed with the drag-and-drop, passing in the list item.                    

                       DragDropEffects dropEffect = ListDragSource.DoDragDrop(ListDragSource.Items[indexOfItemUnderMouseToDrag], DragDropEffects.All | DragDropEffects.Link);

                       // If the drag operation was a move then remove the item.

                       if (dropEffect == DragDropEffects.Move) {                        

                           ListDragSource.Items.RemoveAt(indexOfItemUnderMouseToDrag);

                           // Selects the previous item in the list as long as the list has an item.

                           if (indexOfItemUnderMouseToDrag > 0)

                               ListDragSource.SelectedIndex = indexOfItemUnderMouseToDrag -1;

                           else if (ListDragSource.Items.Count > 0)

                               // Selects the first item.

                               ListDragSource.SelectedIndex =0;

                       }

                       // Dispose of the cursors since they are no longer needed.

                       if (MyNormalCursor != null)

                           MyNormalCursor.Dispose();

                       if (MyNoDropCursor != null)

                           MyNoDropCursor.Dispose();

                   }

               }

           }

       }

       private void ListDragSource_GiveFeedback(object sender, System.Windows.Forms.GiveFeedbackEventArgs e)

       {

           // Use custom cursors if the check box is checked.

           if (UseCustomCursorsCheck.Checked) {

               // Sets the custom cursor based upon the effect.

               e.UseDefaultCursors = false;

               if ((e.Effect & DragDropEffects.Move) == DragDropEffects.Move)

                   Cursor.Current = MyNormalCursor;

               else 

                   Cursor.Current = MyNoDropCursor;

           }

       }

       private void ListDragTarget_DragOver(object sender, System.Windows.Forms.DragEventArgs e)

       {

           // Determine whether string data exists in the drop data. If not, then

           // the drop effect reflects that the drop cannot occur.

           if (!e.Data.GetDataPresent(typeof(System.String))) {

               e.Effect = DragDropEffects.None;

               DropLocationLabel.Text = "None - no string data.";

               return;

           }

           // Set the effect based upon the KeyState.

           if ((e.KeyState & (8+32)) == (8+32) &&

               (e.AllowedEffect & DragDropEffects.Link) == DragDropEffects.Link) {

               // KeyState 8 + 32 = CTL + ALT

               // Link drag-and-drop effect.

               e.Effect = DragDropEffects.Link;

           } else if ((e.KeyState & 32) == 32 &&

               (e.AllowedEffect & DragDropEffects.Link) == DragDropEffects.Link) {

               // ALT KeyState for link.

               e.Effect = DragDropEffects.Link;

           } else if ((e.KeyState & 4) == 4 &&

               (e.AllowedEffect & DragDropEffects.Move) == DragDropEffects.Move) {

               // SHIFT KeyState for move.

               e.Effect = DragDropEffects.Move;

           } else if ((e.KeyState & 8) == 8 &&

               (e.AllowedEffect & DragDropEffects.Copy) == DragDropEffects.Copy) {

               // CTL KeyState for copy.

               e.Effect = DragDropEffects.Copy;

           } else if ((e.AllowedEffect & DragDropEffects.Move) == DragDropEffects.Move)  {

               // By default, the drop action should be move, if allowed.

               e.Effect = DragDropEffects.Move;

           } else

               e.Effect = DragDropEffects.None;

           // Get the index of the item the mouse is below.

           // The mouse locations are relative to the screen, so they must be

           // converted to client coordinates.

           indexOfItemUnderMouseToDrop =

               ListDragTarget.IndexFromPoint(ListDragTarget.PointToClient(new Point(e.X, e.Y)));

           // Updates the label text.

           if (indexOfItemUnderMouseToDrop != ListBox.NoMatches){

               DropLocationLabel.Text = "Drops before item #" + (indexOfItemUnderMouseToDrop + 1);

           } else

               DropLocationLabel.Text = "Drops at the end.";

       }

       private void ListDragTarget_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)

       {

           // Ensure that the list item index is contained in the data.

           if (e.Data.GetDataPresent(typeof(System.String))) {

               Object item = (object)e.Data.GetData(typeof(System.String));

               // Perform drag-and-drop, depending upon the effect.

               if (e.Effect == DragDropEffects.Copy ||

                   e.Effect == DragDropEffects.Move) {

                   // Insert the item.

                   if (indexOfItemUnderMouseToDrop != ListBox.NoMatches)

                       ListDragTarget.Items.Insert(indexOfItemUnderMouseToDrop, item);

                   else

                       ListDragTarget.Items.Add(item);

               }

           }

           // Reset the label text.

           DropLocationLabel.Text = "None";

       }

       private void ListDragSource_QueryContinueDrag(object sender, System.Windows.Forms.QueryContinueDragEventArgs e) {

           // Cancel the drag if the mouse moves off the form.

           ListBox lb = sender as ListBox;

           if (lb != null) {

               Form f = lb.FindForm();

               // Cancel the drag if the mouse moves off the form. The screenOffset

               // takes into account any desktop bands that may be at the top or left

               // side of the screen.

               if (((Control.MousePosition.X - screenOffset.X) < f.DesktopBounds.Left) ||

                   ((Control.MousePosition.X - screenOffset.X) > f.DesktopBounds.Right) ||

                   ((Control.MousePosition.Y - screenOffset.Y) < f.DesktopBounds.Top) ||

                   ((Control.MousePosition.Y - screenOffset.Y) > f.DesktopBounds.Bottom)) {

                   e.Action = DragAction.Cancel;

               }

           }

       }

       private void ListDragTarget_DragEnter(object sender, System.Windows.Forms.DragEventArgs e) {

           // Reset the label text.

           DropLocationLabel.Text = "None";

       }

       private void ListDragTarget_DragLeave(object sender, System.EventArgs e) {

           // Reset the label text.

           DropLocationLabel.Text = "None";

       }

   }

}

В следующем примере кода показано использование перечисления DragDropEffects для задания способа передачи данных между элементами управления, вовлеченными в операцию перетаскивания. В данном примере требуется наличия формы, включающей элементы управления RichTextBoxи ListBox, а также элемента управления ListBox, заполненного списком допустимых имен файлов. При перетаскивании имени файла на элемент управления RichTextBox происходит событие DragEnter элемента управления. В обработчике событий свойство Effect объекта DragEventArgsинициализируется в объекте DragDropEffects, чтобы указать, что данные, на которые ссылается путь файла, следует скопировать в элемент управленияRichTextBox.

C#

C++

VB

private void Form1_Load(object sender, EventArgs e)

{

  // Sets the AllowDrop property so that data can be dragged onto the control.

  richTextBox1.AllowDrop = true;

  // Add code here to populate the ListBox1 with paths to text files.

}

private void listBox1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)

{

  // Determines which item was selected.

  ListBox lb =( (ListBox)sender);

  Point pt = new Point(e.X,e.Y);

  int index = lb.IndexFromPoint(pt);

  // Starts a drag-and-drop operation with that item.

  if(index>=0)

  {

     lb.DoDragDrop(lb.Items[index].ToString(), DragDropEffects.Link);

  }

}

private void richTextBox1_DragEnter(object sender, DragEventArgs e)

{

  // If the data is text, copy the data to the RichTextBox control.

  if(e.Data.GetDataPresent("Text"))

     e.Effect = DragDropEffects.Copy;

}

private void richTextBox1_DragDrop(object sender, DragEventArgs e)

{

  // Loads the file into the control.

  richTextBox1.LoadFile((String)e.Data.GetData("Text"), System.Windows.Forms.RichTextBoxStreamType.RichText);

}

Сведения о версии

.NET Framework

Поддерживается в версиях: 4.5, 4, 3.5, 3.0, 2.0, 1.1, 1.0

.NET Framework (клиентский профиль)

Поддерживается в версиях: 4, 3.5 с пакетом обновления 1 (SP1)

Платформы

Windows 8, Windows Server 2012, Windows 7, Windows Vista с пакетом обновления 2 (SP2), Windows Server 2008 (роль основных серверных компонентов не поддерживается), Windows Server 2008 R2 (роль основных серверных компонентов поддерживается в пакете обновления 1 (SP1) или выше; системы на базе Itanium не поддерживаются)

.NET Framework поддерживает не все версии каждой платформы. Поддерживаемые версии перечислены в разделе Требования к системе для .NET Framework.




1. Воздействие промышленности Пермской области на окружающую среду
2. тематика. Учение о смысле добродетелей
3. Аграрная реформа 1861 год
4. Основные свойства возбудимых тканей. Шпаргалка
5. Реферат- Сероквель атипичный нейролептик
6. Обогащение словарного запаса учащихся при подготовке к сочинению в седьмом классе
7. это вид множественности преступлений имеющий место там где лицом совершено два или более преступлений пр
8. СанктПетербургский Гуманитарный университет профсоюзов Кафедра социальнокультурных технологий
9.  Единство этики и логики Существует фундаментальное единство истины добра и красоты
10. Томас которая как всегда вдохновляла нас
11. Анализ экспортной стратегии промышленного предприятия на примере ЗАО КВАРТ
12. Богиня Востока 2014 Организаторы фестиваля- Ирина Шевченко ~ руководитель школы восточного танца Дали
13. СОЦИОЛОГИЯ МОДЫ ОСНОВНЫЕ ЗАДАЧИ КУРСА
14. РОМАНТИЗМ в музыке XIX в
15. по теме Электронные таблицы
16. Молекулярная физика и Термодинамика 10 класс А4 А 6 При неизм
17. й хитиновый 2й клетки гиподермы 2 слоя- 1й хитиновый 2й клетки гиподермы Полост
18. Мария Пономарева Автор текста преследовал следующую цель- освещение основных этапов биографии и творче
19. Представники течії кубізму
20. Развитие сети физкультурноспортивных сооружений и материальнотехнической базы физической культуры