Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Cette section contient des exemples de code pour les tâches suivantes :
-
Implémenter les commandes Couper, Copier et Coller
- Sélectionner des données
- Créer un menu Modifier
- Traiter le message WM_INITMENUPOPUP
- Traiter le message WM_COMMAND
- Copier des informations dans le presse-papiers
- Coller les informations du presse-papiers
- Inscrire un format presse-papiers
- Traiter les messages WM_RENDERFORMAT et WM_RENDERALLFORMATS
- Traiter le message WM_DESTROYCLIPBOARD
- Utiliser le format d’affichage du presse-papiers propriétaire
- Surveiller le contenu du Presse-papiers
- Interroger le numéro de séquence du Presse-papiers
- Créer un écouteur de format presse-papiers
- Créer une fenêtre visionneuse du Presse-papiers
- Ajouter une fenêtre à la chaîne de visualisation du Presse-papiers
Implémenter les commandes Couper, Copier et Coller
Cette section décrit comment les commandes Couper, Copier et Coller standard sont implémentées dans une application. L'exemple de cette section utilise ces méthodes pour placer des données dans le presse-papiers à l'aide d'un format de presse-papiers inscrit, le format CF_OWNERDISPLAY
et le format CF_TEXT
. Le format inscrit est utilisé pour représenter des fenêtres de texte rectangulaires ou elliptiques, appelées étiquettes.
Sélectionner les données
Avant que les informations puissent être copiées dans le Presse-papiers, l’utilisateur doit sélectionner des informations spécifiques à copier ou couper. Une application doit fournir un moyen à l’utilisateur de sélectionner des informations dans un document et un type de commentaires visuels pour indiquer les données sélectionnées.
Créer un menu Modifier
Une application doit charger une table d’accélérateurs contenant les accélérateurs clavier standard pour les commandes de menu Modifier . La fonction TranslateAccelerator doit être ajoutée à la boucle de message de l’application pour que les accélérateurs prennent effet. Pour plus d’informations sur les accélérateurs de clavier, consultez Raccourcis clavier.
Traiter le message WM_INITMENUPOPUP
Toutes les commandes du Presse-papiers ne sont pas disponibles pour l’utilisateur à un moment donné. Une application doit traiter le message WM_INITMENUPOPUP pour activer les éléments de menu pour les commandes disponibles et désactiver les commandes indisponibles.
Voici le cas WM_INITMENUPOPUP d’une application nommée Label.
case WM_INITMENUPOPUP:
InitMenu((HMENU) wParam);
break;
La InitMenu
fonction est définie comme suit.
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
);
}
}
}
Traiter le message WM_COMMAND
Pour traiter les commandes de menu, ajoutez le cas WM_COMMAND à la procédure principale de votre application. Voici le WM_COMMAND
cas de la procédure de fenêtre de l’application Label.
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;
Pour exécuter les commandes Copier et Couper , la procédure de fenêtre appelle la fonction définie par EditCopy
l’application. Pour plus d’informations, consultez Copier les informations dans le Presse-papiers. Pour exécuter la commande Coller , la procédure de fenêtre appelle la fonction définie par EditPaste
l’application. Pour plus d’informations sur la fonction EditPaste
, consultez Coller des informations depuis le Presse-papiers.
Copier des informations dans le Presse-papiers
Dans l’application Label, la fonction EditCopy définie par l’application copie la sélection actuelle dans le Presse-papiers. Cette fonction effectue les opérations suivantes :
- Ouvre le Presse-papiers en appelant la fonction OpenClipboard .
- Vide le Presse-papiers en appelant la fonction EmptyClipboard .
- Appelle la fonction SetClipboardData une fois pour chaque format de presse-papiers fourni par l’application.
- Ferme le Presse-papiers en appelant la fonction CloseClipboard .
Selon la sélection actuelle, la fonction EditCopy copie une plage de texte ou copie une structure définie par l’application représentant une étiquette entière. La structure, appelée LABELBOX
, est définie comme suit :
#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;
Voici la EditCopy
fonction :
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;
}
Coller des informations à partir du Presse-papiers
Dans l'application Label, la fonction EditPaste
définie par l'application colle le contenu du Presse-papiers. Cette fonction effectue les opérations suivantes :
- Ouvre le Presse-papiers en appelant la fonction OpenClipboard .
- Détermine quels formats de presse-papiers disponibles à récupérer.
- Récupère le handle des données du format sélectionné en appelant la fonction GetClipboardData.
- Insère une copie des données dans le document. Le handle retourné par GetClipboardData appartient toujours au Presse-papiers. Donc, une application ne doit ni le libérer ni le laisser verrouillé.
- Ferme le Presse-papiers en appelant la fonction CloseClipboard .
Si une étiquette est sélectionnée et contient un point d’insertion, la fonction *EditPaste* insère le texte du presse-papiers au point d’insertion. S’il n’existe aucune sélection ou si une étiquette est sélectionnée, la fonction crée une nouvelle étiquette en utilisant la structure définie par l’application dans le Presse-papiers. La LABELBOX
structure est placée dans le presse-papiers à l'aide d'un format de presse-papiers enregistré.
La structure, appelée LABELBOX
, est définie comme suit :
#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;
Voici la EditPaste
fonction :
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();
}
Enregistrer un format du presse-papiers
Pour inscrire un format presse-papiers, ajoutez un appel à la fonction RegisterClipboardFormat
dans la fonction d’initialisation d’instance de votre application, comme suit :
// 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;
Traiter les messages WM_RENDERFORMAT et WM_RENDERALLFORMATS
Si une fenêtre transmet un NULL
handle à la fonction SetClipboardData , elle doit traiter les WM_RENDERFORMAT et WM_RENDERALLFORMATS messages pour afficher les données à la demande.
Si une fenêtre retarde le rendu d’un format spécifique, puis qu’une autre application demande des données dans ce format, un message WM_RENDERFORMAT est envoyé à la fenêtre. En outre, si une fenêtre retarde le rendu d’un ou plusieurs formats, et si certains de ces formats restent non affichés lorsque la fenêtre est sur le point d’être détruite, un message WM_RENDERALLFORMATS est envoyé à la fenêtre avant sa destruction.
Pour rendre un format de Presse-papiers, la routine de fenêtre doit placer un handle de données non-NULL
dans le Presse-papiers à l’aide de la fonction SetClipboardData. Si la procédure de fenêtre affiche un format en réponse au message WM_RENDERFORMAT, elle ne doit pas ouvrir le Presse-papiers avant d'appeler la fonction SetClipboardData
. Mais s’il affiche un ou plusieurs formats en réponse au message WM_RENDERALLFORMATS, il doit ouvrir le Presse-papiers et vérifier que la fenêtre possède toujours le Presse-papiers avant d’appeler SetClipboardData
, et il doit fermer le Presse-papiers avant de revenir.
L’application Label traite les messages WM_RENDERFORMAT et WM_RENDERALLFORMATS comme suit :
case WM_RENDERFORMAT:
RenderFormat((UINT) wParam);
break;
case WM_RENDERALLFORMATS:
if (OpenClipboard(hwnd))
{
if (GetClipboardOwner() == hwnd)
{
RenderFormat(uLabelFormat);
RenderFormat(CF_TEXT);
}
CloseClipboard();
}
break;
Dans les deux cas, la procédure de fenêtre appelle la fonction définie par l'application, qui est définie dans le code qui suit.
La structure, appelée LABELBOX
, est définie comme suit :
#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);
}
}
Traiter le message WM_DESTROYCLIPBOARD
Une fenêtre peut traiter le message WM_DESTROYCLIPBOARD afin de libérer toutes les ressources mises de côté pour prendre en charge le rendu différé. Par exemple, l’application Label, lors de la copie d’une étiquette dans le Presse-papiers, alloue un objet mémoire local. Il libère ensuite cet objet en réponse au WM_DESTROYCLIPBOARD
message, comme suit :
case WM_DESTROYCLIPBOARD:
if (pboxLocalClip != NULL)
{
LocalFree(pboxLocalClip);
pboxLocalClip = NULL;
}
break;
Utiliser le format presse-papiers d'affichage propriétaire
Si une fenêtre place des informations dans le Presse-papiers à l’aide du CF_OWNERDISPLAY
format presse-papiers, elle doit effectuer les opérations suivantes :
- Traitez le message WM_PAINTCLIPBOARD . Ce message est envoyé au propriétaire du Presse-papiers lorsqu’une partie de la fenêtre de visionneuse du Presse-papiers doit être repeinte.
- Traitez le message WM_SIZECLIPBOARD . Ce message est envoyé au propriétaire du Presse-papiers lorsque la fenêtre de la visionneuse du Presse-papiers a été redimensionnée ou que son contenu a changé. En règle générale, une fenêtre réagit à ce message en définissant les positions et plages de défilement pour la fenêtre de visionneuse du Presse-papiers. En réponse à ce message, l'application Label met également à jour une structure SIZE pour la fenêtre de la visionneuse du presse-papiers.
- Traitez les messages WM_HSCROLLCLIPBOARD et WM_VSCROLLCLIPBOARD. Ces messages sont envoyés au propriétaire du Presse-papiers lorsqu’un événement de barre de défilement a lieu dans la fenêtre de visualisation du Presse-papiers.
- Traitez le message WM_ASKCBFORMATNAME . La fenêtre de visionneuse du presse-papiers envoie ce message à une application pour récupérer le nom du format d'affichage du propriétaire.
La procédure de fenêtre pour l’application Label traite ces messages comme suit :
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;
}
Surveiller le contenu du Presse-papiers
Il existe trois façons de surveiller les modifications apportées au presse-papiers. La méthode la plus ancienne consiste à créer une fenêtre de visionneuse pour le Presse-papiers. Windows 2000 a ajouté la possibilité d’interroger le numéro de séquence du Presse-papiers, et Windows Vista a ajouté la prise en charge des écouteurs de format du Presse-papiers. Les fenêtres de visionneuse du Presse-papiers sont prises en charge pour la compatibilité rétroactive avec les versions antérieures de Windows. Les nouveaux programmes doivent utiliser des écouteurs de format presse-papiers ou le numéro de séquence du Presse-papiers.
Interroger le numéro de séquence du Presse-papiers
Chaque fois que le contenu du Presse-papiers change, une valeur 32 bits appelée numéro de séquence du Presse-papiers est incrémentée. Un programme peut récupérer le numéro de séquence actuel du presse-papiers en appelant la fonction GetClipboardSequenceNumber. En comparant la valeur renvoyée à celle d'un appel précédent GetClipboardSequenceNumber
, un programme peut déterminer si le contenu du Presse-papiers a changé. Cette méthode est plus adaptée aux programmes qui utilisent les résultats du cache en fonction du contenu actuel du Presse-papiers et doivent savoir si les calculs sont toujours valides avant d’utiliser les résultats de ce cache. Notez qu’il ne s’agit pas d’une méthode de notification et ne doit pas être utilisée dans une boucle d’interrogation. Pour être averti lorsque le contenu du presse-papiers change, utiliser un écouteur de format de presse-papiers ou une visionneuse du presse-papiers.
Créer un écouteur de format presse-papiers
Un écouteur de format presse-papiers est une fenêtre qui a été enregistrée pour être avertie lorsque le contenu du presse-papiers change. Cette méthode est recommandée plutôt que de créer une fenêtre de visionneuse du Presse-papiers, car elle est plus simple à mettre en œuvre et évite les problèmes si les programmes ne parviennent pas à maintenir correctement la chaîne de la visionneuse de Presse-papiers ou si une fenêtre de cette chaîne cesse de répondre aux messages.
Une fenêtre s’inscrit en tant qu’écouteur au format Presse-papiers en appelant la fonction AddClipboardFormatListener . Lorsque le contenu du Presse-papiers change, la fenêtre reçoit un message WM_CLIPBOARDUPDATE. L’inscription reste valide jusqu’à ce que la fenêtre se désinscrit elle-même en appelant la fonction RemoveClipboardFormatListener .
Créer une fenêtre de visualiseur du presse-papiers
Une fenêtre de visualisation affiche le contenu actuel du Presse-papiers et reçoit des messages lorsque ce contenu change. Pour créer une fenêtre d'affichage du Presse-papiers, votre application doit effectuer les opérations suivantes :
- Ajoutez la fenêtre à la chaîne de visionneuse de presse-papiers.
- Traitez le message WM_CHANGECBCHAIN .
- Traitez le message WM_DRAWCLIPBOARD .
- Supprimez la fenêtre de la chaîne de visionneuse du Presse-papiers avant sa destruction.
Ajouter une fenêtre à la chaîne de visionneuse du Presse-papiers
Une fenêtre s’ajoute à la chaîne de visualiseurs du Presse-papiers en appelant la fonction SetClipboardViewer. La valeur de retour est le handle de la fenêtre suivante dans la chaîne. Une fenêtre doit suivre cette valeur, par exemple en l’enregistrant dans une variable statique nommée hwndNextViewer
.
L'exemple suivant ajoute une fenêtre à la chaîne de visionneuse du Presse-papiers en réponse au message WM_CREATE.
case WM_CREATE:
// Add the window to the clipboard viewer chain.
hwndNextViewer = SetClipboardViewer(hwnd);
break;
Les extraits de code sont affichés pour les tâches suivantes :
- Traiter le message WM_CHANGECBCHAIN
- Supprimer une fenêtre de la chaîne des visualisateurs du Presse-papiers
- Traiter le message WM_DRAWCLIPBOARD
- Exemple de visionneuse de Presse-papiers
Traiter le message WM_CHANGECBCHAIN
Une fenêtre de visionneuse du Presse-papiers reçoit le message WM_CHANGECBCHAIN lorsqu’une autre fenêtre se supprime de la chaîne de visionneuse du Presse-papiers. Si la fenêtre en cours de suppression est la fenêtre suivante de la chaîne, la fenêtre qui reçoit le message doit dissocier la fenêtre suivante de la chaîne. Dans le cas contraire, ce message doit être transmis à la fenêtre suivante de la chaîne.
L’exemple suivant montre le traitement du message 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;
Supprimer une fenêtre de la chaîne de la visionneuse du presse-papiers
Pour se supprimer de la chaîne de visionneuse du Presse-papiers, une fenêtre appelle la fonction ChangeClipboardChain . L’exemple suivant supprime une fenêtre de la chaîne de visionneuse du Presse-papiers en réponse au message WM_DESTROY .
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
Traiter le message WM_DRAWCLIPBOARD
Le message WM_DRAWCLIPBOARD informe une fenêtre de visualisation du presse-papiers que le contenu du presse-papiers a changé. Une fenêtre doit effectuer les opérations suivantes lors du traitement du WM_DRAWCLIPBOARD
message :
- Choisissez les formats du presse-papiers disponibles à afficher.
- Récupérez les données du Presse-papiers et affichez-les dans la fenêtre. Ou si le format du Presse-papiers est
CF_OWNERDISPLAY
, envoyez un message WM_PAINTCLIPBOARD au propriétaire du Presse-papiers. - Envoyez le message à la fenêtre suivante de la chaîne de visionneuse du Presse-papiers.
Pour obtenir un exemple de traitement du message WM_DRAWCLIPBOARD , consultez l’exemple de liste dans la section suivante.
Exemple de visionneuse de presse-papiers
L’exemple suivant montre une visionneuse de presse-papiers simple et complète :
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;
}