Uso degli Appunti
In questa sezione sono disponibili esempi di codice per le attività seguenti:
-
Implementazione dei comandi Taglia, Copia e Incolla
- Selezionare i dati
- Creazione di un di menu Modifica
-
Elaborazione del
WM_INITMENUPOPUP
messaggio -
Elaborazione del messaggio
WM_COMMAND
- Copiare le informazioni negli Appunti
- Incollare informazioni dagli Appunti
- Registrazione di un formato Appunti
-
Elaborazione dei messaggi di
WM_RENDERFORMAT
eWM_RENDERALLFORMATS
-
Elaborazione del
WM_DESTROYCLIPBOARD
messaggio - Utilizzo del Owner-Display Formato Appunti
- monitoraggio del contenuto degli Appunti
- Consultazione del numero di sequenza degli Appunti
- Creazione di un listener in formato Appunti
- Creazione di una finestra del visualizzatore Appunti
- Aggiunta di una finestra alla catena del visualizzatore Appunti
Implementazione dei comandi Taglia, Copia e Incolla
In questa sezione viene descritto il modo in cui i comandi Taglia, Copiae Incolla vengono implementati in un'applicazione. L'esempio in questa sezione usa questi metodi per inserire i dati negli Appunti usando un formato degli Appunti registrato, il formato CF_OWNERDISPLAY
e il formato CF_TEXT
. Il formato registrato viene usato per rappresentare finestre di testo rettangolari o ellittiche, denominate etichette.
Selezione di dati
Prima che le informazioni possano essere copiate negli Appunti, l'utente deve selezionare informazioni specifiche da copiare o tagliare. Un'applicazione deve fornire un mezzo per consentire all'utente di selezionare le informazioni all'interno di un documento e un qualche tipo di feedback visivo per indicare i dati selezionati.
Creazione di un menu Modifica
Un'applicazione deve caricare una tabella di tasti di scelta rapida contenente gli acceleratori di tastiera standard per i comandi di menu Modifica. La funzione TranslateAccelerator
deve essere aggiunta al ciclo di messaggi dell'applicazione per rendere effettive le operazioni degli acceleratori. Per ulteriori informazioni sugli acceleratori da tastiera, vedere Acceleratori di Tastiera.
Elaborazione del messaggio di WM_INITMENUPOPUP
Non tutti i comandi degli Appunti sono disponibili per l'utente in un determinato momento. Un'applicazione deve elaborare il messaggio di WM_INITMENUPOPUP
per abilitare le voci di menu per i comandi disponibili e disabilitare i comandi non disponibili.
Di seguito è riportato il WM_INITMENUPOPUP
caso per un'applicazione denominata Label.
case WM_INITMENUPOPUP:
InitMenu((HMENU) wParam);
break;
La funzione InitMenu
è definita come segue.
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
);
}
}
}
Elaborazione del messaggio di WM_COMMAND
Per elaborare i comandi di menu, aggiungere il caso WM_COMMAND
alla procedura della finestra principale dell'applicazione. Di seguito è riportato il WM_COMMAND
caso per la procedura finestra dell'applicazione 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;
Per eseguire i comandi Copia e Taglia, la routine della finestra chiama la funzione EditCopy
definita dall'applicazione. Per ulteriori informazioni, vedere Copiare le informazioni negli appunti. Per eseguire il comando Incolla, la routine della finestra chiama la funzione EditPaste
definita dall'applicazione. Per ulteriori informazioni sulla funzione EditPaste
, vedere Incollare informazioni dagli Appunti.
Copiare informazioni negli Appunti
Nell'applicazione Label la funzione EditCopy definita dall'applicazione copia la selezione corrente negli Appunti. Questa funzione esegue le operazioni seguenti:
- Apre gli appunti chiamando la funzione
OpenClipboard
. - Svuota gli Appunti chiamando la funzione
EmptyClipboard
. - Chiama la funzione
SetClipboardData
una volta per ogni formato degli Appunti fornito dall'applicazione. - Chiude gli Appunti chiamando la funzione
CloseClipboard
.
A seconda della selezione corrente, la funzione EditCopy copia un intervallo di testo o copia una struttura definita dall'applicazione che rappresenta un'intera etichetta. La struttura, denominata LABELBOX
, è definita come segue.
#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;
Di seguito è riportata la funzione 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;
}
Incollare informazioni dagli Appunti
Nell'applicazione Label, la funzione EditPaste
definita dall'applicazione incolla il contenuto degli Appunti. Questa funzione esegue le operazioni seguenti:
- Apre la clipboard chiamando la funzione
OpenClipboard
. - Determina quale dei formati degli Appunti disponibili da recuperare.
- Ottiene l'handle dei dati nel formato selezionato chiamando la funzione
GetClipboardData
. - Inserisce una copia dei dati nel documento. L'handle restituito da
GetClipboardData
è ancora di proprietà degli Appunti, pertanto un'applicazione non deve liberarlo né lasciarlo bloccato. - Chiude la clipboard chiamando la funzione
CloseClipboard
.
Se è selezionata un'etichetta che contiene un punto di inserimento, la funzione EditPaste inserisce il testo dagli appunti nel punto di inserimento. Se non viene effettuata alcuna selezione o se è selezionata un'etichetta, la funzione crea una nuova etichetta utilizzando la struttura LABELBOX
definita dall'applicazione negli Appunti. La struttura LABELBOX
viene posizionata negli Appunti utilizzando un formato registrato.
La struttura, denominata LABELBOX
, è definita come segue.
#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;
Di seguito è riportata la funzione 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();
}
Registrazione di un formato appunti
Per registrare un formato negli Appunti, aggiungere una chiamata alla funzione RegisterClipboardFormat
nella funzione di inizializzazione dell'istanza della vostra applicazione, come indicato di seguito.
// 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;
Elaborazione dei messaggi di WM_RENDERFORMAT
e di WM_RENDERALLFORMATS
Se una finestra passa un handle di NULL
alla funzione SetClipboardData
, deve elaborare i messaggi WM_RENDERFORMAT
e WM_RENDERALLFORMATS
per il rendering dei dati su richiesta.
Se una finestra ritarda il rendering di un formato specifico e un'altra applicazione richiede dati in tale formato, viene inviato un messaggio WM_RENDERFORMAT
alla finestra. Inoltre, se una finestra ritarda il rendering di uno o più formati e se alcuni di questi formati rimangono annullati quando la finestra sta per essere distrutta, un messaggio di WM_RENDERALLFORMATS
viene inviato alla finestra prima della sua distruzione.
Per eseguire il rendering di un formato degli Appunti, la routine della finestra deve posizionare un handle di dati nonNULL
negli Appunti usando la funzione SetClipboardData
. Se la routine della finestra esegue il rendering di un formato in risposta al messaggio WM_RENDERFORMAT
, non deve aprire gli Appunti prima di chiamare SetClipboardData
. Tuttavia, se esegue il rendering di uno o più formati in risposta al messaggio WM_RENDERALLFORMATS
, deve aprire il clipboard e verificare che la finestra possieda ancora il clipboard prima di chiamare SetClipboardData
, e deve chiudere il clipboard prima di tornare.
L'applicazione Label elabora i messaggi WM_RENDERFORMAT
e WM_RENDERALLFORMATS
come indicato di seguito.
case WM_RENDERFORMAT:
RenderFormat((UINT) wParam);
break;
case WM_RENDERALLFORMATS:
if (OpenClipboard(hwnd))
{
if (GetClipboardOwner() == hwnd)
{
RenderFormat(uLabelFormat);
RenderFormat(CF_TEXT);
}
CloseClipboard();
}
break;
In entrambi i casi, la routine della finestra chiama la funzione di RenderFormat
definita dall'applicazione, definita come indicato di seguito.
La struttura, denominata LABELBOX
, è definita come segue.
#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);
}
}
Elaborazione del messaggio di WM_DESTROYCLIPBOARD
Una finestra può elaborare il messaggio di WM_DESTROYCLIPBOARD
per liberare tutte le risorse da riservare per supportare il rendering ritardato. Ad esempio, l'applicazione Label, quando si copia un'etichetta negli appunti, alloca un oggetto di memoria locale. Libera quindi questo oggetto in risposta al messaggio di WM_DESTROYCLIPBOARD
, come indicato di seguito.
case WM_DESTROYCLIPBOARD:
if (pboxLocalClip != NULL)
{
LocalFree(pboxLocalClip);
pboxLocalClip = NULL;
}
break;
Utilizzo del formato Clipboard Owner-Display
Se una finestra inserisce informazioni negli Appunti usando il formato degli Appunti CF_OWNERDISPLAY
, è necessario eseguire le operazioni seguenti:
- Elaborare il messaggio
WM_PAINTCLIPBOARD
. Questo messaggio viene inviato al proprietario degli appunti quando una parte della finestra del visualizzatore appunti deve essere ridipinta. - Elaborare il messaggio di
WM_SIZECLIPBOARD
. Questo messaggio viene inviato al proprietario degli Appunti quando la finestra del visualizzatore Appunti è stata ridimensionata o il relativo contenuto è stato modificato. In genere, una finestra risponde a questo messaggio impostando le posizioni e gli intervalli di scorrimento per la finestra del visualizzatore appunti. In risposta a questo messaggio, l'applicazione Label aggiorna anche una strutturaSIZE
per la finestra del visualizzatore appunti. - Elaborare i messaggi
WM_HSCROLLCLIPBOARD
eWM_VSCROLLCLIPBOARD
. questi messaggi vengono inviati al proprietario degli appunti quando si verifica un evento della barra di scorrimento nella finestra del visualizzatore appunti. - Elaborare il messaggio di
WM_ASKCBFORMATNAME
. La finestra del visualizzatore Appunti invia questo messaggio a un'applicazione per recuperare il nome del formato di visualizzazione proprietario.
La procedura della finestra per l'applicazione Etichetta elabora questi messaggi, come indicato di seguito.
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;
}
Monitoraggio dei contenuti degli Appunti
Esistono tre modi per monitorare le modifiche apportate agli Appunti. Il metodo più antico consiste nel creare una finestra del visualizzatore degli Appunti. Windows 2000 ha aggiunto la possibilità di interrogare il numero di sequenza degli Appunti e Windows Vista ha aggiunto il supporto per gli ascoltatori di formato degli Appunti. Le finestre del visualizzatore appunti sono supportate per garantire la compatibilità con versioni precedenti di Windows. I nuovi programmi devono usare gestori del formato Appunti o il numero di sequenza degli Appunti.
Esecuzione di query sul numero di sequenza degli Appunti
Ogni volta che il contenuto degli Appunti cambia, viene incrementato un valore a 32 bit noto come numero di sequenza degli Appunti. Un programma può recuperare il numero di sequenza degli Appunti corrente chiamando la funzione GetClipboardSequenceNumber
. Confrontando il valore restituito con un valore restituito da una chiamata precedente a GetClipboardSequenceNumber
, un programma può determinare se il contenuto degli Appunti è stato modificato. Questo metodo è più adatto ai programmi che memorizzano nella cache i risultati in base al contenuto corrente degli Appunti e devono sapere se i calcoli sono ancora validi prima di usare i risultati di tale cache. Si noti che non si tratta di un metodo di notifica e non deve essere usato in un ciclo di polling. Per ricevere una notifica quando il contenuto degli appunti cambia, utilizzare un ascoltatore di formato per appunti o un visualizzatore di appunti.
Creazione di un listener in formato Appunti
Una finestra listener del formato degli Appunti si è registrata per ricevere una notifica quando il contenuto degli Appunti è stato modificato. Questo metodo è consigliato per la creazione di una finestra del visualizzatore Appunti perché è più semplice implementare ed evitare problemi se i programmi non riescono a mantenere correttamente la catena del visualizzatore degli Appunti o se una finestra nella catena del visualizzatore degli Appunti smette di rispondere ai messaggi.
Una finestra si registra come ascoltatore del formato Appunti chiamando la funzione AddClipboardFormatListener
. Quando il contenuto degli Appunti cambia, alla finestra viene inviato un messaggio di WM_CLIPBOARDUPDATE
. La registrazione rimane valida fino a quando la finestra non annulla la registrazione chiamando la funzione RemoveClipboardFormatListener
.
Creazione di una finestra visualizzatore Appunti
Una finestra del visualizzatore appunti visualizza il contenuto corrente degli Appunti e riceve messaggi quando cambia il contenuto degli Appunti. Per creare una finestra del visualizzatore Appunti, l'applicazione deve eseguire le operazioni seguenti:
- Aggiungi la finestra alla catena del visualizzatore di appunti.
- Elaborare il messaggio
WM_CHANGECBCHAIN
. - Elaborare il messaggio di
WM_DRAWCLIPBOARD
. - Rimuovere la finestra dalla catena del visualizzatore degli Appunti prima che venga eliminata definitivamente.
Aggiunta di una finestra alla catena del visualizzatore Appunti
Una finestra si aggiunge alla catena del visualizzatore degli Appunti chiamando la funzione SetClipboardViewer
. Il valore di ritorno è il riferimento alla finestra successiva nella catena. Una finestra deve tenere traccia di questo valore, ad esempio salvandola in una variabile statica denominata hwndNextViewer
.
Nell'esempio seguente viene aggiunta una finestra nella catena dei visualizzatori degli Appunti in risposta al messaggio di WM_CREATE
.
case WM_CREATE:
// Add the window to the clipboard viewer chain.
hwndNextViewer = SetClipboardViewer(hwnd);
break;
I frammenti di codice vengono visualizzati per le attività seguenti:
-
Elaborazione del
WM_CHANGECBCHAIN
messaggio - Rimozione di una finestra dalla catena di visualizzatore Appunti
-
Elaborazione del messaggio
WM_DRAWCLIPBOARD
- Esempio di un visualizzatore Clipboard
Elaborazione del messaggio di WM_CHANGECBCHAIN
Una finestra del visualizzatore appunti riceve il messaggio WM_CHANGECBCHAIN
quando un'altra finestra viene rimossa dalla catena del visualizzatore degli Appunti. Se la finestra da rimuovere è la finestra successiva della catena, la finestra che riceve il messaggio deve scollegare la finestra successiva dalla catena. In caso contrario, questo messaggio deve essere passato alla finestra successiva nella catena.
Nell'esempio seguente viene illustrata l'elaborazione del messaggio di 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;
Rimuovere una finestra dalla catena del visualizzatore Appunti
Per rimuovere se stesso dalla catena del visualizzatore degli Appunti, una finestra chiama la funzione ChangeClipboardChain
. Nell'esempio seguente viene rimossa una finestra dalla catena del visualizzatore degli Appunti in risposta al messaggio WM_DESTROY
.
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
Elaborazione del messaggio di WM_DRAWCLIPBOARD
Il messaggio WM_DRAWCLIPBOARD
notifica a una finestra del visualizzatore appunti che il contenuto degli appunti ha cambiato. Una finestra deve eseguire le operazioni seguenti durante l'elaborazione del messaggio di WM_DRAWCLIPBOARD
:
- Determinare quali formati disponibili degli appunti visualizzare.
- Recuperare i dati degli Appunti e visualizzarli nella finestra. In alternativa, se il formato della clipboard è
CF_OWNERDISPLAY
, invia un messaggioWM_PAINTCLIPBOARD
al proprietario della clipboard. - Inviare il messaggio alla finestra successiva nella catena del visualizzatore appunti.
Per un esempio di elaborazione del messaggio WM_DRAWCLIPBOARD
, vedere l'elenco esemplificativo in Esempio di Visualizzatore Appunti.
Esempio di Visualizzatore Appunti
Nell'esempio seguente viene illustrata una semplice applicazione visualizzatore appunti.
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;
}