Het Klembord gebruiken
Deze sectie bevat codevoorbeelden voor de volgende taken:
-
Het Implementeren van de Opdrachten Knippen, Kopiëren, en Plakken
- Gegevens selecteren
- Een menu Bewerken maken
-
Het
WM_INITMENUPOPUP
bericht verwerken -
Het
WM_COMMAND
bericht verwerken - Gegevens naar het Klembord kopiëren
- Gegevens plakken vanaf het Klembord
- Een Klembordformaat registreren
-
Het verwerken van de
WM_RENDERFORMAT
enWM_RENDERALLFORMATS
berichten -
Het
WM_DESTROYCLIPBOARD
bericht verwerken - De Owner-Display Klembord-indeling gebruiken
- Inhoud van klembord controleren
- Het Klembordreeksnummer Opvragen
- Een listener voor klembordindeling maken
- Een klembordviewervenster maken
- Een venster aan de klembord-viewerketen toevoegen
De opdrachten knippen, kopiëren en plakken implementeren
In deze sectie wordt beschreven hoe standaard Knippen, Kopiërenen opdrachten in een toepassing worden geïmplementeerd. In het voorbeeld in deze sectie worden deze methoden gebruikt om gegevens op het klembord te plaatsen met behulp van een geregistreerde klembordindeling, de CF_OWNERDISPLAY
-indeling en de CF_TEXT
-indeling. De geregistreerde indeling wordt gebruikt om rechthoekige of elliptische tekstvensters weer te geven, ook wel labels genoemd.
Gegevens selecteren
Voordat informatie naar het klembord kan worden gekopieerd, moet de gebruiker specifieke informatie selecteren die moet worden gekopieerd of geknipt. Een toepassing moet een middel bieden voor de gebruiker om informatie in een document te selecteren en een soort visuele feedback om geselecteerde gegevens aan te geven.
Een menu Bewerken maken
Een toepassing moet een acceleratortabel laden met de standaardtoetsencombinaties voor de Bewerken menuopdrachten. De functie TranslateAccelerator
moet worden toegevoegd aan de berichtlus van de toepassing om de accelerators van kracht te laten worden. Zie Toetsenbordversnellersvoor meer informatie over toetsenbordversnellers.
Het WM_INITMENUPOPUP
-bericht verwerken
Niet alle klembordopdrachten zijn op elk gewenst moment beschikbaar voor de gebruiker. Een toepassing moet het WM_INITMENUPOPUP
bericht verwerken om de menu-items voor beschikbare opdrachten in te schakelen en niet-beschikbare opdrachten uit te schakelen.
Hier volgt het WM_INITMENUPOPUP
geval voor een toepassing met de naam Label.
case WM_INITMENUPOPUP:
InitMenu((HMENU) wParam);
break;
De functie InitMenu
wordt als volgt gedefinieerd.
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
);
}
}
}
Het WM_COMMAND
-bericht verwerken
Als u menuopdrachten wilt verwerken, voegt u de WM_COMMAND
case toe aan de hoofdvensterprocedure van uw toepassing. Hier volgt het WM_COMMAND
-geval voor de vensterprocedure van de Label-applicatie.
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;
Als u de opdrachten Copy en Cut wilt uitvoeren, roept de vensterprocedure de door de toepassing gedefinieerde EditCopy
-functie aan. Zie Gegevens kopiëren naar het Klembordvoor meer informatie. Als u de opdracht Plakken wilt uitvoeren, roept de vensterprocedure de door de toepassing gedefinieerde EditPaste
-functie aan. Voor meer informatie over de functie EditPaste
, zie informatie plakken van het klembord.
Gegevens kopiëren naar het Klembord
In de labeltoepassing kopieert de toepassingsgedefinieerde EditCopy-functie de huidige selectie naar het klembord. Deze functie doet het volgende:
- Opent het klembord door de functie
OpenClipboard
aan te roepen. - Hiermee wordt het klembord leeg gemaakt door de functie
EmptyClipboard
aan te roepen. - Roept de
SetClipboardData
functie eenmaal aan voor elke klembordindeling die de toepassing biedt. - Sluit het klembord door de functie
CloseClipboard
aan te roepen.
Afhankelijk van de huidige selectie kopieert de functie EditCopy een tekstbereik of kopieert u een door de toepassing gedefinieerde structuur die een heel label vertegenwoordigt. De structuur, LABELBOX
genoemd, wordt als volgt gedefinieerd.
#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;
Hieronder ziet u de functie EditCopy
.
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;
}
Gegevens van het Klembord plakken
In de labeltoepassing plakt de door de toepassing gedefinieerde EditPaste
functie de inhoud van het klembord. Deze functie doet het volgende:
- U opent het klembord door de functie
OpenClipboard
aan te roepen. - Bepaalt welke van de beschikbare klembordformaten kunnen worden opgehaald.
- Haalt de handle op naar de gegevens in de geselecteerde indeling door de
GetClipboardData
-functie aan te roepen. - Hiermee voegt u een kopie van de gegevens in het document in. Het handvat dat door
GetClipboardData
wordt geretourneerd, is nog steeds eigendom van het klembord, dus een applicatie mag het niet vrijgeven of ontgrendeld laten. - Sluit het klembord door functie
CloseClipboard
aan te roepen.
Als een label is geselecteerd en een invoegpositie bevat, wordt met de functie EditPaste de tekst vanaf het klembord op de invoegpositie ingevoegd. Als er geen selectie is of als er een label is geselecteerd, maakt de functie een nieuw label met behulp van de door de toepassing gedefinieerde LABELBOX
structuur op het klembord. De LABELBOX
structuur wordt op het klembord geplaatst met behulp van een geregistreerde klembordindeling.
De structuur, LABELBOX
genoemd, wordt als volgt gedefinieerd.
#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;
Hieronder ziet u de functie 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 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();
}
Een klembordindeling registreren
Als u een klembordformaat wilt registreren, voegt u een aanroep van de RegisterClipboardFormat
-functie toe aan de initialisatiefunctie van uw toepassing, als volgt.
// 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;
De WM_RENDERFORMAT
- en WM_RENDERALLFORMATS
-berichten verwerken
Als een venster een NULL
handle doorgeeft aan de functie SetClipboardData
, moet het de WM_RENDERFORMAT
en WM_RENDERALLFORMATS
berichten verwerken om gegevens op aanvraag weer te geven.
Als een venster het weergeven van een specifieke indeling vertraagt en vervolgens een andere toepassing gegevens in die indeling aanvraagt, wordt er een WM_RENDERFORMAT
bericht naar het venster verzonden. Bovendien, als een venster het weergeven van een of meer indelingen vertraagt en als sommige van deze indelingen onrendeerd blijven wanneer het venster op het punt staat te worden vernietigd, wordt er vóór de vernietiging een WM_RENDERALLFORMATS
bericht naar het venster verzonden.
Als u een klembordindeling wilt weergeven, moet de vensterprocedure een niet-NULL
gegevensgreep op het klembord plaatsen met behulp van de functie SetClipboardData
. Als de vensterprocedure een indeling weergeeft als reactie op het WM_RENDERFORMAT
bericht, mag het klembord niet worden geopend voordat SetClipboardData
wordt aangeroepen. Echter, als er een of meer formaten worden weergegeven als reactie op het WM_RENDERALLFORMATS
-bericht, moet het klembord worden geopend en moet worden gecontroleerd of het venster nog steeds eigenaar is van het klembord voordat SetClipboardData
wordt aangeroepen. Het klembord moet worden gesloten voordat er wordt teruggekeerd.
De labeltoepassing verwerkt de WM_RENDERFORMAT
en WM_RENDERALLFORMATS
berichten als volgt.
case WM_RENDERFORMAT:
RenderFormat((UINT) wParam);
break;
case WM_RENDERALLFORMATS:
if (OpenClipboard(hwnd))
{
if (GetClipboardOwner() == hwnd)
{
RenderFormat(uLabelFormat);
RenderFormat(CF_TEXT);
}
CloseClipboard();
}
break;
In beide gevallen roept de vensterprocedure de door de toepassing gedefinieerde RenderFormat
-functie aan, die als volgt is gedefinieerd.
De structuur, LABELBOX
genoemd, wordt als volgt gedefinieerd.
#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);
}
}
Het WM_DESTROYCLIPBOARD
-bericht verwerken
Een venster kan het WM_DESTROYCLIPBOARD
bericht verwerken om resources vrij te maken die zijn gereserveerd om vertraagde rendering te ondersteunen. Wanneer u bijvoorbeeld een label naar het klembord kopieert, wijst de toepassing Label een lokaal geheugenobject toe. Vervolgens wordt dit object als volgt vrijgemaakt als reactie op het WM_DESTROYCLIPBOARD
bericht.
case WM_DESTROYCLIPBOARD:
if (pboxLocalClip != NULL)
{
LocalFree(pboxLocalClip);
pboxLocalClip = NULL;
}
break;
De Owner-Display Klembordformaat gebruiken
Als een venster informatie op het klembord plaatst met behulp van de CF_OWNERDISPLAY
klembordindeling, moet dit het volgende doen:
- Het
WM_PAINTCLIPBOARD
bericht verwerken. Dit bericht wordt naar de eigenaar van het Klembord verzonden wanneer een deel van het venster van het Klembordviewer opnieuw moet worden geschilderd. - Het
WM_SIZECLIPBOARD
bericht verwerken. Dit bericht wordt verzonden naar de eigenaar van het klembord wanneer het venster van de klembordviewer is gewijzigd of de inhoud ervan is gewijzigd. Normaal gesproken reageert een venster op dit bericht door de schuifposities en bereiken voor het klembordviewervenster in te stellen. Als reactie op dit bericht werkt de labeltoepassing ook eenSIZE
structuur voor het klembordviewervenster bij. - De
WM_HSCROLLCLIPBOARD
enWM_VSCROLLCLIPBOARD
berichten verwerken. Deze berichten worden verzonden naar de eigenaar van het klembord wanneer een schuifbalk-gebeurtenis plaatsvindt in het venster van de klembordviewer. - Het
WM_ASKCBFORMATNAME
bericht verwerken. Het klembordviewervenster verzendt dit bericht naar een toepassing om de naam van het eigenaarsweergave-formaat op te halen.
De vensterprocedure voor de labeltoepassing verwerkt deze berichten als volgt.
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;
}
Inhoud van Klembord bewaken
Er zijn drie manieren om wijzigingen in het klembord te controleren. De oudste methode is door een klembordweergavevenster te maken. Windows 2000 heeft de mogelijkheid toegevoegd om een query uit te voeren op het reeksnummer van het Klembord en Windows Vista heeft ondersteuning toegevoegd voor listeners voor de klembordindeling. Klembordviewer-vensters worden ondersteund voor compatibiliteit met eerdere versies van Windows. Nieuwe programma's moeten klembordformaatluisteraars of het volgnummer van het klembord gebruiken.
Een query uitvoeren op het klembordreeksnummer
Telkens wanneer de inhoud van het klembord wordt gewijzigd, wordt een 32-bits waarde, ook wel het klembordreeksnummer genoemd, verhoogd. Een programma kan het huidige klembordreeksnummer ophalen door de GetClipboardSequenceNumber
functie aan te roepen. Door de geretourneerde waarde te vergelijken met een waarde die door een vorige aanroep naar GetClipboardSequenceNumber
wordt geretourneerd, kan een programma bepalen of de inhoud van het klembord is gewijzigd. Deze methode is geschikter voor programma's die resultaten opslaan op basis van de huidige inhoud van het klembord en moeten weten of de berekeningen nog geldig zijn voordat de resultaten van die cache worden gebruikt. Houd er rekening mee dat dit geen notificatiemethode is en niet mag worden gebruikt in een polling-lus. Als u een melding wilt ontvangen wanneer de inhoud van het klembord wordt gewijzigd, gebruikt u een listener voor de klembordindeling of een klembordviewer.
Een listener voor klembordindeling maken
Een listener voor klembordindeling is een venster dat is geregistreerd om op de hoogte te worden gesteld wanneer de inhoud van het klembord is gewijzigd. Deze methode wordt aanbevolen boven het maken van een klembordviewervenster, omdat het eenvoudiger is te implementeren en problemen te vermijden als programma's de keten van de klembordviewer niet goed onderhouden of als een venster in de keten van de klembordviewer stopt met reageren op berichten.
Een venster wordt geregistreerd als een klembordformaatlistener door de functie AddClipboardFormatListener
aan te roepen. Wanneer de inhoud van het klembord wordt gewijzigd, krijgt het venster een WM_CLIPBOARDUPDATE
bericht. De registratie blijft geldig totdat de registratie van het venster ongedaan wordt gemaakt door de RemoveClipboardFormatListener
functie aan te roepen.
Een KlembordViewer-venster maken
In een klembordviewervenster wordt de huidige inhoud van het klembord weergegeven en worden berichten ontvangen wanneer de inhoud van het klembord wordt gewijzigd. Als u een klembordviewervenster wilt maken, moet uw toepassing het volgende doen:
- Voeg het venster toe aan de viewerketen van het Klembord.
- Het
WM_CHANGECBCHAIN
bericht verwerken. - Het
WM_DRAWCLIPBOARD
bericht verwerken. - Verwijder het venster uit de klembordviewerketen voordat het wordt vernietigd.
Een venster toevoegen aan de klembord-kijkerketen
Een venster voegt zichzelf toe aan de viewerketen van het Klembord door de functie SetClipboardViewer
aan te roepen. De retourwaarde is de ingang naar het volgende venster in de keten. Een venster moet deze waarde bijhouden, bijvoorbeeld door deze op te slaan in een statische variabele met de naam hwndNextViewer
.
In het volgende voorbeeld wordt een venster toegevoegd aan de klembord-viewerketen in reactie op het WM_CREATE
-bericht.
case WM_CREATE:
// Add the window to the clipboard viewer chain.
hwndNextViewer = SetClipboardViewer(hwnd);
break;
Codefragmenten worden weergegeven voor de volgende taken:
-
Het
WM_CHANGECBCHAIN
bericht verwerken - Een venster verwijderen uit het Klembord-viewerketen
-
Het
WM_DRAWCLIPBOARD
bericht verwerken - voorbeeld van een Klembordviewer-
Het WM_CHANGECBCHAIN
-bericht verwerken
Een klembordkijkervenster ontvangt het WM_CHANGECBCHAIN
-bericht wanneer een ander venster zichzelf verwijdert uit de klembordkijkerketen. Als het venster dat wordt verwijderd het volgende venster in de keten is, moet het venster dat het bericht ontvangt, het volgende venster loskoppelen van de keten. Anders moet dit bericht worden doorgegeven aan het volgende venster in de keten.
In het volgende voorbeeld ziet u de verwerking van het WM_CHANGECBCHAIN
bericht.
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;
Een venster verwijderen uit de keten van klembordviewers
Om zichzelf uit de klembord viewerketen te verwijderen, roept een venster de functie ChangeClipboardChain
aan. In het volgende voorbeeld wordt een venster uit de klembordviewerketen verwijderd als reactie op het WM_DESTROY
-bericht.
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
Het WM_DRAWCLIPBOARD
-bericht verwerken
Het WM_DRAWCLIPBOARD
-bericht informeert een venster voor klembordweergave dat de inhoud van het klembord is gewijzigd. Een venster moet het volgende doen bij het verwerken van het WM_DRAWCLIPBOARD
bericht:
- Bepaal welke van de beschikbare klembordindelingen moet worden weergegeven.
- Haal de klembordgegevens op en geef deze weer in het venster. Ofwel, als de klembordindeling
CF_OWNERDISPLAY
is, stuurt u eenWM_PAINTCLIPBOARD
-bericht naar de eigenaar van het klembord. - Verzend het bericht naar het volgende venster in de viewerketen van het Klembord.
Zie de voorbeeldvermelding in Voorbeeld van een Klembordviewer-voor een voorbeeld van het verwerken van het WM_DRAWCLIPBOARD
bericht.
Voorbeeld van een Klembordviewer
In het volgende voorbeeld ziet u een eenvoudige klembordviewertoepassing.
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;
}