Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Ta sekcja zawiera przykłady kodu dla następujących zadań:
-
Implementowanie poleceń wycinania, kopiowania i wklejania
- Wybieranie danych
- Tworzenie menu edycji
- Przetwarzanie komunikatu WM_INITMENUPOPUP
- Przetwarzanie komunikatu WM_COMMAND
- Kopiowanie informacji do schowka
- Wklej informacje ze schowka
- Zarejestruj format schowka
- Przetwarzanie komunikatów WM_RENDERFORMAT i WM_RENDERALLFORMATS
- Przetwarzanie komunikatu WM_DESTROYCLIPBOARD
- Korzystanie z formatu schowka z wyświetlaniem właściciela
- Monitoruj zawartość schowka
- Zapytaj numer sekwencyjny schowka
- Tworzenie odbiornika formatu schowka
- Utwórz okno przeglądarki schowka
- Dodawanie okna do łańcucha przeglądarki schowka
Implementowanie poleceń wycinania, kopiowania i wklejania
W tej sekcji opisano sposób implementacji standardowych poleceń wycinania, kopiowania i wklejania w aplikacji. Przykład w tej części przedstawia wykorzystanie metod do umieszczania danych w schowku za pomocą zarejestrowanego formatu schowka oraz formatów CF_OWNERDISPLAY
i CF_TEXT
. Zarejestrowany format służy do reprezentowania prostokątnych lub wielokropkowych okien tekstowych nazywanych etykietami.
Wybieranie danych
Aby można było skopiować informacje do schowka, użytkownik musi wybrać określone informacje, które mają zostać skopiowane lub wycięte. Aplikacja powinna dostarczyć użytkownikowi środki do wybierania informacji w dokumencie i pewnego rodzaju opinii wizualnej w celu wskazania wybranych danych.
Tworzenie menu edycji
Aplikacja powinna załadować tabelę akceleratora zawierającą standardowe akceleratory klawiaturowe dla poleceń menu Edycja . Aby akceleratory zaczęły obowiązywać, należy dodać funkcję TranslateAccelerator do pętli komunikatów aplikacji. Aby uzyskać więcej informacji na temat akceleratorów klawiatury, zobacz Akceleratory klawiaturowe.
Przetwarzanie komunikatu WM_INITMENUPOPUP
Nie wszystkie polecenia schowka są dostępne dla użytkownika w danym momencie. Aplikacja powinna przetworzyć komunikat WM_INITMENUPOPUP , aby włączyć elementy menu dla dostępnych poleceń i wyłączyć niedostępne polecenia.
Poniżej znajduje się przypadek WM_INITMENUPOPUP dla aplikacji o nazwie Label.
case WM_INITMENUPOPUP:
InitMenu((HMENU) wParam);
break;
Funkcja jest zdefiniowana InitMenu
w następujący sposób.
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
);
}
}
}
Przetwarzanie komunikatu WM_COMMAND
Aby przetworzyć polecenia menu, dodaj przypadek WM_COMMAND do głównej procedury okna aplikacji. Poniżej przedstawiono procedurę WM_COMMAND
okna aplikacji Etykieta.
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;
Aby wykonać polecenia kopiowania i wycinania , procedura okna wywołuje funkcję zdefiniowaną przez EditCopy
aplikację. Aby uzyskać więcej informacji, zobacz Kopiowanie informacji do schowka. Aby wykonać polecenie Wklej , procedura okna wywołuje funkcję zdefiniowaną przez EditPaste
aplikację. Aby dowiedzieć się więcej na temat funkcji EditPaste
, zobacz Wklejanie informacji ze schowka.
Skopiuj informacje do schowka
W aplikacji Label funkcja EditCopy, zdefiniowana przez aplikację, kopiuje bieżące zaznaczenie do schowka. Ta funkcja wykonuje następujące czynności:
- Schowek otwierany jest przez wywołanie funkcji OpenClipboard.
- Opróżnia schowek, wywołując funkcję EmptyClipboard .
- Wywołuje funkcję SetClipboardData raz dla każdego formatu schowka zapewnianego przez aplikację.
- Zamyka schowek, wykorzystując funkcję CloseClipboard.
W zależności od bieżącego zaznaczenia funkcja EditCopy kopiuje zakres tekstu lub kopiuje strukturę zdefiniowaną przez aplikację reprezentującą całą etykietę. Struktura o nazwie LABELBOX
, jest zdefiniowana w następujący sposób:
#define BOX_ELLIPSE 0
#define BOX_RECT 1
#define CCH_MAXLABEL 80
#define CX_MARGIN 12
typedef struct tagLABELBOX { // box
RECT rcText; // coordinates of rectangle containing text
BOOL fSelected; // TRUE if the label is selected
BOOL fEdit; // TRUE if text is selected
int nType; // rectangular or elliptical
int ichCaret; // caret position
int ichSel; // with ichCaret, delimits selection
int nXCaret; // window position corresponding to ichCaret
int nXSel; // window position corresponding to ichSel
int cchLabel; // length of text in atchLabel
TCHAR atchLabel[CCH_MAXLABEL];
} LABELBOX, *PLABELBOX;
Poniżej przedstawiono EditCopy
funkcję:
BOOL WINAPI EditCopy(VOID)
{
PLABELBOX pbox;
LPTSTR lptstrCopy;
HGLOBAL hglbCopy;
int ich1, ich2, cch;
if (hwndSelected == NULL)
return FALSE;
// Open the clipboard, and empty it.
if (!OpenClipboard(hwndMain))
return FALSE;
EmptyClipboard();
// Get a pointer to the structure for the selected label.
pbox = (PLABELBOX) GetWindowLong(hwndSelected, 0);
// If text is selected, copy it using the CF_TEXT format.
if (pbox->fEdit)
{
if (pbox->ichSel == pbox->ichCaret) // zero length
{
CloseClipboard(); // selection
return FALSE;
}
if (pbox->ichSel < pbox->ichCaret)
{
ich1 = pbox->ichSel;
ich2 = pbox->ichCaret;
}
else
{
ich1 = pbox->ichCaret;
ich2 = pbox->ichSel;
}
cch = ich2 - ich1;
// Allocate a global memory object for the text.
hglbCopy = GlobalAlloc(GMEM_MOVEABLE,
(cch + 1) * sizeof(TCHAR));
if (hglbCopy == NULL)
{
CloseClipboard();
return FALSE;
}
// Lock the handle and copy the text to the buffer.
lptstrCopy = GlobalLock(hglbCopy);
memcpy(lptstrCopy, &pbox->atchLabel[ich1],
cch * sizeof(TCHAR));
lptstrCopy[cch] = (TCHAR) 0; // null character
GlobalUnlock(hglbCopy);
// Place the handle on the clipboard.
SetClipboardData(CF_TEXT, hglbCopy);
}
// If no text is selected, the label as a whole is copied.
else
{
// Save a copy of the selected label as a local memory
// object. This copy is used to render data on request.
// It is freed in response to the WM_DESTROYCLIPBOARD
// message.
pboxLocalClip = (PLABELBOX) LocalAlloc(
LMEM_FIXED,
sizeof(LABELBOX)
);
if (pboxLocalClip == NULL)
{
CloseClipboard();
return FALSE;
}
memcpy(pboxLocalClip, pbox, sizeof(LABELBOX));
pboxLocalClip->fSelected = FALSE;
pboxLocalClip->fEdit = FALSE;
// Place a registered clipboard format, the owner-display
// format, and the CF_TEXT format on the clipboard using
// delayed rendering.
SetClipboardData(uLabelFormat, NULL);
SetClipboardData(CF_OWNERDISPLAY, NULL);
SetClipboardData(CF_TEXT, NULL);
}
// Close the clipboard.
CloseClipboard();
return TRUE;
}
Wklej informacje ze schowka
W aplikacji Label funkcja EditPaste
zdefiniowana przez aplikację wkleja zawartość schowka. Ta funkcja wykonuje następujące czynności:
- Schowek otwierany jest przez wywołanie funkcji OpenClipboard.
- Określa, które z dostępnych formatów schowka mają zostać pobrane.
- Pobiera uchwyt do danych w wybranym formacie, wywołując funkcję GetClipboardData .
- Wstawia kopię danych do dokumentu. Uchwyt zwrócony przez GetClipboardData jest nadal własnością schowka, więc aplikacja nie może go zwolnić ani pozostawić go zablokowanego.
- Zamyka schowek, wywołując funkcję CloseClipboard .
Jeśli etykieta jest zaznaczona i zawiera miejsce wstawiania, funkcja EditPaste wprowadza tekst ze schowka w tym miejscu. Jeśli nie ma zaznaczenia lub jeśli jest zaznaczona etykieta, funkcja tworzy nową etykietę, używając struktury LABELBOX
zdefiniowanej przez aplikację w schowku. Struktura LABELBOX
jest umieszczana w schowku przy użyciu zarejestrowanego formatu.
Struktura o nazwie LABELBOX
, jest zdefiniowana w następujący sposób:
#define BOX_ELLIPSE 0
#define BOX_RECT 1
#define CCH_MAXLABEL 80
#define CX_MARGIN 12
typedef struct tagLABELBOX { // box
RECT rcText; // coordinates of rectangle containing text
BOOL fSelected; // TRUE if the label is selected
BOOL fEdit; // TRUE if text is selected
int nType; // rectangular or elliptical
int ichCaret; // caret position
int ichSel; // with ichCaret, delimits selection
int nXCaret; // window position corresponding to ichCaret
int nXSel; // window position corresponding to ichSel
int cchLabel; // length of text in atchLabel
TCHAR atchLabel[CCH_MAXLABEL];
} LABELBOX, *PLABELBOX;
Poniżej przedstawiono EditPaste
funkcję:
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 the application is in edit mode,
// get the clipboard text.
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)
{
// Call the application-defined ReplaceSelection
// function to insert the text and repaint the
// window.
ReplaceSelection(hwndSelected, pbox, lptstr);
GlobalUnlock(hglb);
}
}
CloseClipboard();
return;
}
// If the application is not in edit mode,
// create a label window.
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();
}
Zarejestruj format schowka
Aby zarejestrować format schowka, dodaj wywołanie funkcji RegisterClipboardFormat
w ramach funkcji inicjalizującej instancję aplikacji w następujący sposób:
// Register a clipboard format.
// We assume that atchTemp can contain the format name and
// a null-terminator, otherwise it is truncated.
//
LoadString(hinstCurrent, IDS_FORMATNAME, atchTemp,
sizeof(atchTemp)/sizeof(TCHAR));
uLabelFormat = RegisterClipboardFormat(atchTemp);
if (uLabelFormat == 0)
return FALSE;
Przetwarzanie komunikatów WM_RENDERFORMAT i WM_RENDERALLFORMATS
Jeśli okno przekazuje NULL
dojście do funkcji SetClipboardData, musi przetworzyć komunikaty WM_RENDERFORMAT i WM_RENDERALLFORMATS w celu renderowania danych na żądanie.
Jeśli okno opóźni renderowanie określonego formatu, a następnie inna aplikacja żąda danych w tym formacie, komunikat WM_RENDERFORMAT zostanie wysłany do okna. Ponadto jeśli okno opóźni renderowanie co najmniej jednego formatu, a niektóre z tych formatów pozostaną nierenderowane, gdy okno zostanie zniszczone, wówczas komunikat WM_RENDERALLFORMATS zostanie wysłany do okna przed jego zniszczeniem.
Aby przetworzyć format schowka, procedura okna powinna umieścić w schowku uchwyt typu NULL
przy użyciu funkcji SetClipboardData . Jeśli procedura okna renderuje format w odpowiedzi na komunikat WM_RENDERFORMAT, nie może otworzyć schowka przed wywołaniem SetClipboardData
. Jeśli jednak renderuje jeden lub więcej formatów w odpowiedzi na komunikat WM_RENDERALLFORMATS, musi otworzyć schowek, sprawdzić, czy okno jest nadal właścicielem schowka przed wywołaniem SetClipboardData
, i musi zamknąć schowek przed zakończeniem operacji.
Aplikacja Label przetwarza komunikaty WM_RENDERFORMAT i WM_RENDERALLFORMATS w następujący sposób:
case WM_RENDERFORMAT:
RenderFormat((UINT) wParam);
break;
case WM_RENDERALLFORMATS:
if (OpenClipboard(hwnd))
{
if (GetClipboardOwner() == hwnd)
{
RenderFormat(uLabelFormat);
RenderFormat(CF_TEXT);
}
CloseClipboard();
}
break;
W obu przypadkach procedura okna wywołuje funkcję zdefiniowaną przez aplikację, która jest przedstawiona w poniższym kodzie.
Struktura o nazwie LABELBOX
, jest zdefiniowana w następujący sposób:
#define BOX_ELLIPSE 0
#define BOX_RECT 1
#define CCH_MAXLABEL 80
#define CX_MARGIN 12
typedef struct tagLABELBOX { // box
RECT rcText; // coordinates of rectangle containing text
BOOL fSelected; // TRUE if the label is selected
BOOL fEdit; // TRUE if text is selected
int nType; // rectangular or elliptical
int ichCaret; // caret position
int ichSel; // with ichCaret, delimits selection
int nXCaret; // window position corresponding to ichCaret
int nXSel; // window position corresponding to ichSel
int cchLabel; // length of text in 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)
{
// Allocate a buffer for the text.
cch = pboxLocalClip->cchLabel;
hglb = GlobalAlloc(GMEM_MOVEABLE,
(cch + 1) * sizeof(TCHAR));
if (hglb == NULL)
return;
// Copy the text from pboxLocalClip.
lptstr = GlobalLock(hglb);
memcpy(lptstr, pboxLocalClip->atchLabel,
cch * sizeof(TCHAR));
lptstr[cch] = (TCHAR) 0;
GlobalUnlock(hglb);
// Place the handle on the clipboard.
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);
}
}
Przetwarzanie komunikatu WM_DESTROYCLIPBOARD
Okno może przetworzyć komunikat WM_DESTROYCLIPBOARD, aby zwolnić wszystkie zasoby, które zostały przeznaczone na potrzeby opóźnionego renderowania. Na przykład aplikacja Label podczas kopiowania etykiety do schowka przydziela obiekt pamięci lokalnej. Następnie zwalnia ten obiekt w odpowiedzi na WM_DESTROYCLIPBOARD
komunikat w następujący sposób:
case WM_DESTROYCLIPBOARD:
if (pboxLocalClip != NULL)
{
LocalFree(pboxLocalClip);
pboxLocalClip = NULL;
}
break;
Korzystanie z formatu schowka z wyświetlaniem właściciela
Jeśli okno umieszcza informacje w schowku za pomocą CF_OWNERDISPLAY
formatu schowka, musi wykonać następujące czynności:
- Przetwórz komunikat WM_PAINTCLIPBOARD . Ta wiadomość jest wysyłana do właściciela schowka, gdy część okna przeglądarki schowka musi zostać przemalowana.
- Przetwórz komunikat WM_SIZECLIPBOARD . Ta wiadomość jest wysyłana do właściciela schowka po zmianie rozmiaru okna przeglądarki schowka lub jego zawartości. Zazwyczaj okno odpowiada na ten komunikat, ustawiając pozycje przewijania i zakresy dla okna przeglądarki schowka. W odpowiedzi na ten komunikat aplikacja Label aktualizuje również strukturę SIZE okna przeglądarki schowka.
- Przetwarzanie komunikatów WM_HSCROLLCLIPBOARD i WM_VSCROLLCLIPBOARD . Te wiadomości są wysyłane do właściciela schowka po wystąpieniu zdarzenia paska przewijania w oknie podglądu schowka.
- Przetwórz komunikat WM_ASKCBFORMATNAME . Okno przeglądarki schowka wysyła ten komunikat do aplikacji w celu pobrania nazwy formatu wyświetlania właściciela.
Procedura okna aplikacji Label przetwarza te komunikaty w następujący sposób:
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)
{
//
// Handle other messages.
//
case WM_PAINTCLIPBOARD:
// Determine the dimensions of the label.
SetRect(&rc, 0, 0,
pboxLocalClip->rcText.right + CX_MARGIN,
pboxLocalClip->rcText.top * 2 + cyText
);
// Center the image in the clipboard viewer window.
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;
}
// Paint the image, using the specified PAINTSTRUCT
// structure, by calling the application-defined
// PaintLabel function.
lpps = (LPPAINTSTRUCT) GlobalLock((HGLOBAL) lParam);
PaintLabel(lpps, pboxLocalClip, &rc);
GlobalUnlock((HGLOBAL) lParam);
break;
case WM_SIZECLIPBOARD:
// Save the dimensions of the window in a static
// RECT structure.
lprc = (LPRECT) GlobalLock((HGLOBAL) lParam);
memcpy(&rcViewer, lprc, sizeof(RECT));
GlobalUnlock((HGLOBAL) lParam);
// Set the scroll ranges to zero (thus eliminating
// the need to process the WM_HSCROLLCLIPBOARD and
// WM_VSCROLLCLIPBOARD messages).
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;
}
Monitorowanie zawartości schowka
Istnieją trzy sposoby monitorowania zmian w schowku. Najstarszą metodą jest utworzenie okna przeglądarki schowka. System Windows 2000 dodał możliwość wykonywania zapytań dotyczących numeru sekwencji schowka, a system Windows Vista dodał obsługę odbiorników formatu schowka. Okna podglądu Schowka są obsługiwane dla zapewnienia zgodności z poprzednimi wersjami systemu Windows. Nowe programy powinny używać odbiorników formatu schowka lub numeru sekwencji schowka.
Wykonywanie zapytań względem numeru sekwencji schowka
Za każdym razem, gdy zawartość schowka zmienia się, wartość 32-bitowa znana jako numer sekwencji schowka jest zwiększana. Program może pobrać aktualny numer sekwencji schowka, wywołując funkcję GetClipboardSequenceNumber. Porównując wartość zwróconą z wartością zwróconą przez poprzednie wywołanie GetClipboardSequenceNumber
, program może określić, czy zawartość schowka uległa zmianie. Ta metoda jest bardziej odpowiednia dla programów, które cache'ują wyniki na podstawie bieżącej zawartości schowka i muszą wiedzieć, czy obliczenia są wciąż aktualne przed użyciem wyników z tego cache. Należy pamiętać, że nie jest to metoda powiadamiania i nie powinna być używana w pętli sondowania. Aby otrzymywać powiadomienia o zmianie zawartości schowka, użyj odbiornika formatu schowka lub przeglądarki schowka.
Utwórz nasłuchiwacz formatu schowka
Odbiornik zmian formatu schowka to okno, które zostało zarejestrowane, aby otrzymywać powiadomienia, gdy zawartość schowka ulegnie zmianie. Ta metoda jest zalecana w przypadku tworzenia okna przeglądarki schowka, ponieważ łatwiej jest zaimplementować i uniknąć problemów, jeśli programy nie będą prawidłowo obsługiwać łańcucha przeglądarki schowka lub jeśli okno w łańcuchu przeglądarki schowka przestanie odpowiadać na komunikaty.
Okno rejestruje się jako odbiornik formatu schowka, wywołując funkcję AddClipboardFormatListener . Kiedy zawartość schowka się zmienia, do okna zostanie wysłana wiadomość WM_CLIPBOARDUPDATE. Rejestracja pozostaje prawidłowa, dopóki okno nie wyrejestruje się, wywołując funkcję RemoveClipboardFormatListener .
Utwórz okno przeglądarki schowka
Okno przeglądarki schowka wyświetla bieżącą zawartość schowka i odbiera powiadomienia po zmianie zawartości schowka. Aby utworzyć okno przeglądarki schowka, aplikacja musi wykonać następujące czynności:
- Dodaj okno do łańcucha przeglądarki schowka.
- Przetwórz komunikat WM_CHANGECBCHAIN .
- Przetwórz komunikat WM_DRAWCLIPBOARD .
- Usuń okno z łańcucha przeglądarki schowka przed jego zniszczeniem.
Dodawanie okna do łańcucha przeglądarki schowka
Okno dodaje się do łańcucha przeglądarki schowka, wywołując funkcję SetClipboardViewer. Wartość zwracana jest uchwytem do następnego okna w łańcuchu. Okno musi śledzić tę wartość — na przykład zapisując ją w zmiennej statycznej o nazwie hwndNextViewer
.
Poniższy przykład dodaje okno do łańcucha przeglądarki schowka w odpowiedzi na komunikat WM_CREATE .
case WM_CREATE:
// Add the window to the clipboard viewer chain.
hwndNextViewer = SetClipboardViewer(hwnd);
break;
Fragmenty kodu są wyświetlane dla następujących zadań:
- Przetwarzanie komunikatu WM_CHANGECBCHAIN
- Usuwanie okna z łańcucha przeglądarki schowka
- Przetwarzanie komunikatu WM_DRAWCLIPBOARD
- Przykład przeglądarki schowka
Przetwarzanie komunikatu WM_CHANGECBCHAIN
Okno przeglądarki schowka odbiera komunikat WM_CHANGECBCHAIN, gdy inne okno usuwa się z łańcucha przeglądarki schowka. Jeśli usuwane okno jest następnym oknem w łańcuchu, okno odbierające komunikat musi odłączyć następne okno z łańcucha. W przeciwnym razie ten komunikat powinien zostać przekazany do następnego okna w łańcuchu.
W poniższym przykładzie przedstawiono przetwarzanie komunikatu WM_CHANGECBCHAIN .
case WM_CHANGECBCHAIN:
// If the next window is closing, repair the chain.
if ((HWND) wParam == hwndNextViewer)
hwndNextViewer = (HWND) lParam;
// Otherwise, pass the message to the next link.
else if (hwndNextViewer != NULL)
SendMessage(hwndNextViewer, uMsg, wParam, lParam);
break;
Usuń okno z łańcucha przeglądarki schowka
Aby usunąć się z łańcucha przeglądarki schowka, okno wywołuje funkcję ChangeClipboardChain . Poniższy przykład usuwa okno z łańcucha widoku schowka w odpowiedzi na wiadomość WM_DESTROY.
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
Przetwarzanie komunikatu WM_DRAWCLIPBOARD
Komunikat WM_DRAWCLIPBOARD powiadamia okno przeglądarki schowka o zmianie zawartości schowka. Okno powinno wykonać następujące czynności podczas przetwarzania komunikatu WM_DRAWCLIPBOARD
:
- Określ, które z formatów schowka dostępnych mają być wyświetlane.
- Pobierz dane schowka i wyświetl je w oknie. Lub jeśli format schowka to
CF_OWNERDISPLAY
, wyślij wiadomość WM_PAINTCLIPBOARD do właściciela schowka. - Wyślij wiadomość do następnego okna w łańcuchu widoku schowka.
Aby zapoznać się z przykładem przetwarzania komunikatu WM_DRAWCLIPBOARD , zobacz przykładową listę w poniższej sekcji.
Przykład przeglądarki schowka
W poniższym przykładzie przedstawiono kompletną, prostą aplikację przeglądarki schowka:
HINSTANCE hinst;
UINT uFormat = (UINT)(-1);
BOOL fAuto = TRUE;
LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
static HWND hwndNextViewer;
HDC hdc;
HDC hdcMem;
PAINTSTRUCT ps;
LPPAINTSTRUCT lpps;
RECT rc;
LPRECT lprc;
HGLOBAL hglb;
LPSTR lpstr;
HBITMAP hbm;
HENHMETAFILE hemf;
HWND hwndOwner;
switch (uMsg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
// Branch depending on the clipboard format.
switch (uFormat)
{
case CF_OWNERDISPLAY:
hwndOwner = GetClipboardOwner();
hglb = GlobalAlloc(GMEM_MOVEABLE,
sizeof(PAINTSTRUCT));
lpps = GlobalLock(hglb);
memcpy(lpps, &ps, sizeof(PAINTSTRUCT));
GlobalUnlock(hglb);
SendMessage(hwndOwner, WM_PAINTCLIPBOARD,
(WPARAM) hwnd, (LPARAM) hglb);
GlobalFree(hglb);
break;
case CF_BITMAP:
hdcMem = CreateCompatibleDC(hdc);
if (hdcMem != NULL)
{
if (OpenClipboard(hwnd))
{
hbm = (HBITMAP)
GetClipboardData(uFormat);
SelectObject(hdcMem, hbm);
GetClientRect(hwnd, &rc);
BitBlt(hdc, 0, 0, rc.right, rc.bottom,
hdcMem, 0, 0, SRCCOPY);
CloseClipboard();
}
DeleteDC(hdcMem);
}
break;
case CF_TEXT:
if (OpenClipboard(hwnd))
{
hglb = GetClipboardData(uFormat);
lpstr = GlobalLock(hglb);
GetClientRect(hwnd, &rc);
DrawText(hdc, lpstr, -1, &rc, DT_LEFT);
GlobalUnlock(hglb);
CloseClipboard();
}
break;
case CF_ENHMETAFILE:
if (OpenClipboard(hwnd))
{
hemf = GetClipboardData(uFormat);
GetClientRect(hwnd, &rc);
PlayEnhMetaFile(hdc, hemf, &rc);
CloseClipboard();
}
break;
case 0:
GetClientRect(hwnd, &rc);
DrawText(hdc, "The clipboard is empty.", -1,
&rc, DT_CENTER | DT_SINGLELINE |
DT_VCENTER);
break;
default:
GetClientRect(hwnd, &rc);
DrawText(hdc, "Unable to display format.", -1,
&rc, DT_CENTER | DT_SINGLELINE |
DT_VCENTER);
}
EndPaint(hwnd, &ps);
break;
case WM_SIZE:
if (uFormat == CF_OWNERDISPLAY)
{
hwndOwner = GetClipboardOwner();
hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(RECT));
lprc = GlobalLock(hglb);
GetClientRect(hwnd, lprc);
GlobalUnlock(hglb);
SendMessage(hwndOwner, WM_SIZECLIPBOARD,
(WPARAM) hwnd, (LPARAM) hglb);
GlobalFree(hglb);
}
break;
case WM_CREATE:
// Add the window to the clipboard viewer chain.
hwndNextViewer = SetClipboardViewer(hwnd);
break;
case WM_CHANGECBCHAIN:
// If the next window is closing, repair the chain.
if ((HWND) wParam == hwndNextViewer)
hwndNextViewer = (HWND) lParam;
// Otherwise, pass the message to the next link.
else if (hwndNextViewer != NULL)
SendMessage(hwndNextViewer, uMsg, wParam, lParam);
break;
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
case WM_DRAWCLIPBOARD: // clipboard contents changed.
// Update the window by using Auto clipboard format.
SetAutoView(hwnd);
// Pass the message to the next window in clipboard
// viewer chain.
SendMessage(hwndNextViewer, uMsg, wParam, lParam);
break;
case WM_INITMENUPOPUP:
if (!HIWORD(lParam))
InitMenu(hwnd, (HMENU) wParam);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_EXIT:
DestroyWindow(hwnd);
break;
case IDM_AUTO:
SetAutoView(hwnd);
break;
default:
fAuto = FALSE;
uFormat = LOWORD(wParam);
InvalidateRect(hwnd, NULL, TRUE);
}
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return (LRESULT) NULL;
}
void WINAPI SetAutoView(HWND hwnd)
{
static UINT auPriorityList[] = {
CF_OWNERDISPLAY,
CF_TEXT,
CF_ENHMETAFILE,
CF_BITMAP
};
uFormat = GetPriorityClipboardFormat(auPriorityList, 4);
fAuto = TRUE;
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
}
void WINAPI InitMenu(HWND hwnd, HMENU hmenu)
{
UINT uFormat;
char szFormatName[80];
LPCSTR lpFormatName;
UINT fuFlags;
UINT idMenuItem;
// If a menu is not the display menu, no initialization is necessary.
if (GetMenuItemID(hmenu, 0) != IDM_AUTO)
return;
// Delete all menu items except the first.
while (GetMenuItemCount(hmenu) > 1)
DeleteMenu(hmenu, 1, MF_BYPOSITION);
// Check or uncheck the Auto menu item.
fuFlags = fAuto ? MF_BYCOMMAND | MF_CHECKED :
MF_BYCOMMAND | MF_UNCHECKED;
CheckMenuItem(hmenu, IDM_AUTO, fuFlags);
// If there are no clipboard formats, return.
if (CountClipboardFormats() == 0)
return;
// Open the clipboard.
if (!OpenClipboard(hwnd))
return;
// Add a separator and then a menu item for each format.
AppendMenu(hmenu, MF_SEPARATOR, 0, NULL);
uFormat = EnumClipboardFormats(0);
while (uFormat)
{
// Call an application-defined function to get the name
// of the clipboard format.
lpFormatName = GetPredefinedClipboardFormatName(uFormat);
// For registered formats, get the registered name.
if (lpFormatName == NULL)
{
// Note that, if the format name is larger than the
// buffer, it is truncated.
if (GetClipboardFormatName(uFormat, szFormatName,
sizeof(szFormatName)))
lpFormatName = szFormatName;
else
lpFormatName = "(unknown)";
}
// Add a menu item for the format. For displayable
// formats, use the format ID for the menu ID.
if (IsDisplayableFormat(uFormat))
{
fuFlags = MF_STRING;
idMenuItem = uFormat;
}
else
{
fuFlags = MF_STRING | MF_GRAYED;
idMenuItem = 0;
}
AppendMenu(hmenu, fuFlags, idMenuItem, lpFormatName);
uFormat = EnumClipboardFormats(uFormat);
}
CloseClipboard();
}
BOOL WINAPI IsDisplayableFormat(UINT uFormat)
{
switch (uFormat)
{
case CF_OWNERDISPLAY:
case CF_TEXT:
case CF_ENHMETAFILE:
case CF_BITMAP:
return TRUE;
}
return FALSE;
}