Uso degli Appunti
In questa sezione sono disponibili esempi di codice per le attività seguenti:
- Implementazione dei comandi Taglia, Copia e Incolla
- Selezione di dati
- Creazione di un menu Modifica
- Elaborazione del
WM_INITMENUPOPUP
messaggio - Elaborazione del
WM_COMMAND
messaggio - Copia delle informazioni negli Appunti
- Incolla delle informazioni dagli Appunti
- Registrazione di un formato appunti
- Elaborazione dei
WM_RENDERFORMAT
messaggi eWM_RENDERALLFORMATS
- Elaborazione del
WM_DESTROYCLIPBOARD
messaggio - Uso del formato Degli Appunti proprietario
- Monitoraggio del contenuto degli Appunti
- Esecuzione di query sul numero di sequenza degli Appunti
- Creazione di un listener in formato Appunti
- Creazione di una finestra 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 Standard Taglia, Copia e Incolla vengono implementati in un'applicazione. L'esempio in questa sezione usa questi metodi per inserire i dati negli Appunti usando un formato registrato degli Appunti, il CF_OWNERDISPLAY
formato e il CF_TEXT
formato. Il formato registrato viene usato per rappresentare finestre di testo rettangolari o ellittiche, denominate etichette.
Selezione dei 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 TranslateAccelerator
funzione deve essere aggiunta al ciclo di messaggi dell'applicazione per rendere effettive le operazioni degli acceleratori. Per altre informazioni sui tasti di scelta rapida, vedere Tasti di scelta rapida.
Elaborazione del WM_INITMENUPOPUP
messaggio
Non tutti i comandi degli Appunti sono disponibili per l'utente in un determinato momento. Un'applicazione deve elaborare il WM_INITMENUPOPUP
messaggio per abilitare le voci di menu per i comandi disponibili e disabilitare i comandi non disponibili.
Di seguito è riportato il WM_INITMENUPOPUP
caso di un'applicazione denominata Label.
case WM_INITMENUPOPUP:
InitMenu((HMENU) wParam);
break;
La InitMenu
funzione è 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 WM_COMMAND
messaggio
Per elaborare i comandi di menu, aggiungere il WM_COMMAND
caso alla routine della finestra principale dell'applicazione. Di seguito è riportato il WM_COMMAND
caso della 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 definita dall'applicazione EditCopy
. Per altre informazioni, vedere Copia di informazioni negli Appunti. Per eseguire il comando Incolla , la routine della finestra chiama la funzione definita dall'applicazione EditPaste
. Per altre informazioni sulla EditPaste
funzione, vedere Incolla di informazioni negli Appunti.
Copia delle 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
OpenClipboard
funzione . - Svuota gli Appunti chiamando la
EmptyClipboard
funzione . - Chiama la
SetClipboardData
funzione una volta per ogni formato degli Appunti fornito dall'applicazione. - Chiude gli Appunti chiamando la
CloseClipboard
funzione .
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 EditCopy
funzione .
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;
}
Incolla delle informazioni dagli Appunti
Nell'applicazione Etichetta la funzione definita dall'applicazione EditPaste
incolla il contenuto degli Appunti. Questa funzione esegue le operazioni seguenti:
- Apre gli Appunti chiamando la
OpenClipboard
funzione . - Determina quale dei formati degli Appunti disponibili da recuperare.
- Recupera l'handle per i dati nel formato selezionato chiamando la
GetClipboardData
funzione . - Inserisce una copia dei dati nel documento. L'handle restituito dagli
GetClipboardData
Appunti è ancora di proprietà degli Appunti, pertanto un'applicazione non deve liberarla o lasciarla bloccata. - Chiude gli Appunti chiamando la
CloseClipboard
funzione .
Se un'etichetta è selezionata e contiene un punto di inserimento, la funzione EditPaste inserisce il testo dagli Appunti nel punto di inserimento. Se non è selezionata alcuna selezione o se è selezionata un'etichetta, la funzione crea una nuova etichetta usando la struttura definita dall'applicazione LABELBOX
negli Appunti. La LABELBOX
struttura viene posizionata negli Appunti utilizzando un formato registrato negli Appunti.
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 EditPaste
funzione .
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 degli Appunti, aggiungere una chiamata alla RegisterClipboardFormat
funzione di inizializzazione dell'istanza dell'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 WM_RENDERFORMAT
messaggi e WM_RENDERALLFORMATS
Se una finestra passa un NULL
handle alla SetClipboardData
funzione, deve elaborare i WM_RENDERFORMAT
messaggi 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 WM_RENDERFORMAT
messaggio 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 WM_RENDERALLFORMATS
messaggio 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 SetClipboardData
funzione . Se la routine della finestra esegue il rendering di un formato in risposta al WM_RENDERFORMAT
messaggio, non deve aprire gli Appunti prima di chiamare SetClipboardData
. Tuttavia, se esegue il rendering di uno o più formati in risposta al WM_RENDERALLFORMATS
messaggio, è necessario aprire gli Appunti e verificare che la finestra sia ancora proprietaria degli Appunti prima di chiamare SetClipboardData
e chiudere gli Appunti prima di tornare.
L'applicazione Etichetta elabora i WM_RENDERFORMAT
messaggi 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 window chiama la funzione definita dall'applicazione RenderFormat
, definita come segue.
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 WM_DESTROYCLIPBOARD
messaggio
Una finestra può elaborare il WM_DESTROYCLIPBOARD
messaggio per liberare tutte le risorse che ha messo da parte per supportare il rendering ritardato. Ad esempio, l'applicazione Label, quando si copia un'etichetta negli Appunti, alloca un oggetto memoria locale. Libera quindi questo oggetto in risposta al WM_DESTROYCLIPBOARD
messaggio, come indicato di seguito.
case WM_DESTROYCLIPBOARD:
if (pboxLocalClip != NULL)
{
LocalFree(pboxLocalClip);
pboxLocalClip = NULL;
}
break;
Uso del formato Degli Appunti proprietario
Se una finestra inserisce informazioni negli Appunti usando il CF_OWNERDISPLAY
formato degli Appunti, deve eseguire le operazioni seguenti:
- Elaborare il
WM_PAINTCLIPBOARD
messaggio. Questo messaggio viene inviato al proprietario degli Appunti quando è necessario riavviare una parte della finestra del visualizzatore appunti. - Elaborare il
WM_SIZECLIPBOARD
messaggio. 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 Etichetta aggiorna anche unaSIZE
struttura per la finestra del visualizzatore degli Appunti. - Elaborare i
WM_HSCROLLCLIPBOARD
messaggi 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
WM_ASKCBFORMATNAME
messaggio. 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 del contenuto degli Appunti
Esistono tre modi per monitorare le modifiche apportate agli Appunti. Il metodo meno recente consiste nel creare una finestra del visualizzatore degli Appunti. Windows 2000 ha aggiunto la possibilità di eseguire query sul numero di sequenza degli Appunti e Windows Vista ha aggiunto il supporto per i listener di formato Appunti. Le finestre del visualizzatore Appunti sono supportate per la compatibilità con le versioni precedenti di Windows. I nuovi programmi devono usare listener in 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 GetClipboardSequenceNumber
funzione . 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, usare un listener in formato Appunti o un visualizzatore appunti.
Creazione di un listener in formato Appunti
Un listener di formato Appunti è una finestra che ha registrato 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 viene registrata come listener in formato Appunti chiamando la AddClipboardFormatListener
funzione . Quando il contenuto degli Appunti cambia, la finestra viene inviata un WM_CLIPBOARDUPDATE
messaggio. La registrazione rimane valida fino a quando la finestra non annulla la registrazione chiamando la RemoveClipboardFormatListener
funzione .
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:
- Aggiungere la finestra alla catena del visualizzatore degli Appunti.
- Elaborare il
WM_CHANGECBCHAIN
messaggio. - Elaborare il
WM_DRAWCLIPBOARD
messaggio. - 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 di visualizzatore degli Appunti chiamando la SetClipboardViewer
funzione . Il valore restituito è l'handle della finestra successiva nella catena. Una finestra deve tenere traccia di questo valore, ad esempio salvandolo in una variabile statica denominata hwndNextViewer
.
Nell'esempio seguente viene aggiunta una finestra alla catena del visualizzatore degli Appunti in risposta al WM_CREATE
messaggio.
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 del visualizzatore Appunti
- Elaborazione del
WM_DRAWCLIPBOARD
messaggio - Esempio di visualizzatore Appunti
Elaborazione del WM_CHANGECBCHAIN
messaggio
Una finestra del visualizzatore Appunti riceve il WM_CHANGECBCHAIN
messaggio 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 WM_CHANGECBCHAIN
messaggio.
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;
Rimozione di una finestra dalla catena del visualizzatore Appunti
Per rimuovere se stesso dalla catena del visualizzatore degli Appunti, una finestra chiama la ChangeClipboardChain
funzione . Nell'esempio seguente viene rimossa una finestra dalla catena del visualizzatore degli Appunti in risposta al WM_DESTROY
messaggio.
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
Elaborazione del WM_DRAWCLIPBOARD
messaggio
Il WM_DRAWCLIPBOARD
messaggio notifica a una finestra del visualizzatore Appunti che il contenuto degli Appunti è stato modificato. Una finestra deve eseguire le operazioni seguenti durante l'elaborazione del WM_DRAWCLIPBOARD
messaggio:
- Determinare quali formati degli Appunti disponibili visualizzare.
- Recuperare i dati degli Appunti e visualizzarli nella finestra. In alternativa, se il formato degli Appunti è
CF_OWNERDISPLAY
, inviare unWM_PAINTCLIPBOARD
messaggio al proprietario degli Appunti. - Inviare il messaggio alla finestra successiva nella catena del visualizzatore appunti.
Per un esempio di elaborazione del WM_DRAWCLIPBOARD
messaggio, vedere l'elenco di esempio 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;
}