Menggunakan Clipboard
Bagian ini memiliki sampel kode untuk tugas-tugas berikut:
- Menerapkan Perintah Potong, Salin, dan Tempel
- Memilih Data
- Membuat Menu Edit
- Memproses
WM_INITMENUPOPUP
Pesan - Memproses
WM_COMMAND
Pesan - Menyalin Informasi ke Clipboard
- Menempelkan Informasi dari Clipboard
- Mendaftarkan Format Clipboard
WM_RENDERFORMAT
Memproses pesan danWM_RENDERALLFORMATS
- Memproses
WM_DESTROYCLIPBOARD
Pesan - Menggunakan Format Clipboard Tampilan Pemilik
- Memantau Isi Papan Klip
- Mengkueri Nomor Urutan Clipboard
- Membuat Pendengar Format Clipboard
- Membuat Jendela Penampil Clipboard
- Menambahkan Jendela ke Rantai Penampil Clipboard
Menerapkan Perintah Potong, Salin, dan Tempel
Bagian ini menjelaskan bagaimana perintah Potong, Salin, dan Tempel standar diterapkan dalam aplikasi. Contoh di bagian ini menggunakan metode ini untuk menempatkan data di clipboard menggunakan format clipboard terdaftar, CF_OWNERDISPLAY
format, dan CF_TEXT
format . Format terdaftar digunakan untuk mewakili jendela teks persegi panjang atau elips, yang disebut label.
Memilih Data
Sebelum informasi dapat disalin ke clipboard, pengguna harus memilih informasi tertentu untuk disalin atau dipotong. Aplikasi harus menyediakan sarana bagi pengguna untuk memilih informasi dalam dokumen dan beberapa jenis umpan balik visual untuk menunjukkan data yang dipilih.
Membuat Menu Edit
Aplikasi harus memuat tabel akselerator yang berisi akselerator keyboard standar untuk perintah menu Edit . Fungsi TranslateAccelerator
harus ditambahkan ke perulangan pesan aplikasi agar akselerator berlaku. Untuk informasi selengkapnya tentang akselerator keyboard, lihat Akselerator Keyboard.
Memproses WM_INITMENUPOPUP
Pesan
Tidak semua perintah clipboard tersedia untuk pengguna pada waktu tertentu. Aplikasi harus memproses WM_INITMENUPOPUP
pesan untuk mengaktifkan item menu untuk perintah yang tersedia dan menonaktifkan perintah yang tidak tersedia.
Berikut ini adalah WM_INITMENUPOPUP
kasus untuk aplikasi bernama Label.
case WM_INITMENUPOPUP:
InitMenu((HMENU) wParam);
break;
Fungsi ini InitMenu
didefinisikan sebagai berikut.
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
);
}
}
}
Memproses WM_COMMAND
Pesan
Untuk memproses perintah menu, tambahkan kasus ke WM_COMMAND
prosedur jendela utama aplikasi Anda. Berikut ini adalah WM_COMMAND
kasus untuk prosedur jendela aplikasi 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;
Untuk melakukan perintah Salin dan Potong , prosedur jendela memanggil fungsi yang ditentukan EditCopy
aplikasi. Untuk informasi selengkapnya, lihat Menyalin Informasi ke Clipboard. Untuk melakukan perintah Tempel , prosedur jendela memanggil fungsi yang ditentukan EditPaste
aplikasi. Untuk informasi selengkapnya tentang fungsi ini EditPaste
, lihat Menempelkan Informasi dari Clipboard.
Menyalin Informasi ke Clipboard
Dalam aplikasi Label, fungsi EditCopy yang ditentukan aplikasi menyalin pilihan saat ini ke clipboard. Fungsi ini melakukan hal berikut:
- Membuka papan klip dengan memanggil
OpenClipboard
fungsi. - Empties clipboard dengan memanggil
EmptyClipboard
fungsi . SetClipboardData
Memanggil fungsi sekali untuk setiap format clipboard yang disediakan aplikasi.- Menutup papan klip dengan memanggil
CloseClipboard
fungsi.
Bergantung pada pilihan saat ini, fungsi EditCopy menyalin rentang teks atau menyalin struktur yang ditentukan aplikasi yang mewakili seluruh label. Struktur, yang disebut LABELBOX
, didefinisikan sebagai berikut.
#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;
Berikut ini adalah fungsinya 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;
}
Menempelkan Informasi dari Clipboard
Dalam aplikasi Label, fungsi yang ditentukan EditPaste
aplikasi menempelkan konten clipboard. Fungsi ini melakukan hal berikut:
- Membuka papan klip dengan memanggil
OpenClipboard
fungsi. - Menentukan format clipboard mana yang tersedia untuk diambil.
- Mengambil handel ke data dalam format yang dipilih dengan memanggil
GetClipboardData
fungsi . - Menyisipkan salinan data ke dalam dokumen. Handel yang dikembalikan oleh
GetClipboardData
masih dimiliki oleh clipboard, sehingga aplikasi tidak boleh membebaskannya atau membiarkannya terkunci. - Menutup papan klip dengan memanggil
CloseClipboard
fungsi.
Jika label dipilih dan berisi titik penyisipan, fungsi EditPaste menyisipkan teks dari clipboard pada titik penyisipan. Jika tidak ada pilihan atau jika label dipilih, fungsi membuat label baru, menggunakan struktur yang ditentukan LABELBOX
aplikasi pada clipboard. Struktur LABELBOX
ditempatkan pada clipboard dengan menggunakan format clipboard terdaftar.
Struktur, yang disebut LABELBOX
, didefinisikan sebagai berikut.
#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;
Berikut ini adalah fungsinya 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();
}
Mendaftarkan Format Clipboard
Untuk mendaftarkan format clipboard, tambahkan panggilan ke RegisterClipboardFormat
fungsi ke fungsi inisialisasi instans aplikasi Anda, sebagai berikut.
// 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;
WM_RENDERFORMAT
Memproses pesan dan WM_RENDERALLFORMATS
Jika jendela meneruskan NULL
handel ke SetClipboardData
fungsi, jendela harus memproses WM_RENDERFORMAT
pesan dan WM_RENDERALLFORMATS
untuk merender data berdasarkan permintaan.
Jika jendela menunda penyajian format tertentu lalu aplikasi lain meminta data dalam format tersebut, maka pesan WM_RENDERFORMAT
dikirim ke jendela. Selain itu, jika jendela menunda penyajian satu atau beberapa format, dan jika beberapa format tersebut tetap tidak dirender ketika jendela akan dihancurkan, maka pesan WM_RENDERALLFORMATS
dikirim ke jendela sebelum penghancurannya.
Untuk merender format clipboard, prosedur jendela harus menempatkan handel non-dataNULL
pada clipboard menggunakan SetClipboardData
fungsi . Jika prosedur jendela merender format sebagai respons terhadap WM_RENDERFORMAT
pesan, itu tidak boleh membuka clipboard sebelum memanggil SetClipboardData
. Tetapi jika merender satu atau beberapa format sebagai respons terhadap WM_RENDERALLFORMATS
pesan, itu harus membuka clipboard dan memeriksa bahwa jendela masih memiliki clipboard sebelum memanggil SetClipboardData
, dan harus menutup clipboard sebelum kembali.
Aplikasi Label memproses WM_RENDERFORMAT
pesan dan WM_RENDERALLFORMATS
sebagai berikut.
case WM_RENDERFORMAT:
RenderFormat((UINT) wParam);
break;
case WM_RENDERALLFORMATS:
if (OpenClipboard(hwnd))
{
if (GetClipboardOwner() == hwnd)
{
RenderFormat(uLabelFormat);
RenderFormat(CF_TEXT);
}
CloseClipboard();
}
break;
Dalam kedua kasus, prosedur jendela memanggil fungsi yang ditentukan RenderFormat
aplikasi, didefinisikan sebagai berikut.
Struktur, yang disebut LABELBOX
, didefinisikan sebagai berikut.
#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);
}
}
Memproses WM_DESTROYCLIPBOARD
Pesan
Jendela dapat memproses WM_DESTROYCLIPBOARD
pesan untuk membebaskan sumber daya apa pun yang disisihkannya untuk mendukung penyajian yang tertunda. Misalnya aplikasi Label, saat menyalin label ke clipboard, mengalokasikan objek memori lokal. Kemudian membebaskan objek ini sebagai respons terhadap WM_DESTROYCLIPBOARD
pesan, sebagai berikut.
case WM_DESTROYCLIPBOARD:
if (pboxLocalClip != NULL)
{
LocalFree(pboxLocalClip);
pboxLocalClip = NULL;
}
break;
Menggunakan Format Clipboard Tampilan Pemilik
Jika jendela menempatkan informasi pada clipboard dengan menggunakan CF_OWNERDISPLAY
format clipboard, jendela harus melakukan hal berikut:
WM_PAINTCLIPBOARD
Proses pesan. Pesan ini dikirim ke pemilik clipboard ketika sebagian jendela penampil clipboard harus dicat ulang.WM_SIZECLIPBOARD
Proses pesan. Pesan ini dikirim ke pemilik papan klip ketika jendela penampil papan klip telah diubah ukurannya atau isinya telah berubah. Biasanya, jendela merespons pesan ini dengan mengatur posisi dan rentang gulir untuk jendela penampil clipboard. Sebagai respons terhadap pesan ini, aplikasi Label juga memperbaruiSIZE
struktur untuk jendela penampil clipboard.WM_HSCROLLCLIPBOARD
Memproses pesan danWM_VSCROLLCLIPBOARD
. Pesan ini dikirim ke pemilik clipboard ketika peristiwa bilah gulir terjadi di jendela penampil clipboard.WM_ASKCBFORMATNAME
Proses pesan. Jendela penampil clipboard mengirimkan pesan ini ke aplikasi untuk mengambil nama format tampilan pemilik.
Prosedur jendela untuk aplikasi Label memproses pesan ini, sebagai berikut.
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;
}
Memantau Isi Papan Klip
Ada tiga cara memantau perubahan pada clipboard. Metode terlama adalah membuat jendela penampil clipboard. Windows 2000 menambahkan kemampuan untuk mengkueri nomor urutan clipboard, dan Windows Vista menambahkan dukungan untuk pendengar format clipboard. Jendela penampil Clipboard didukung untuk kompatibilitas mundur dengan versi Windows yang lebih lama. Program baru harus menggunakan pendengar format papan klip atau nomor urutan papan klip.
Mengkueri Nomor Urutan Clipboard
Setiap kali konten clipboard berubah, nilai 32-bit yang dikenal sebagai nomor urutan clipboard bertahap. Program dapat mengambil nomor urutan papan klip saat ini dengan memanggil GetClipboardSequenceNumber
fungsi. Dengan membandingkan nilai yang dikembalikan terhadap nilai yang dikembalikan oleh panggilan sebelumnya ke GetClipboardSequenceNumber
, program dapat menentukan apakah konten clipboard telah berubah. Metode ini lebih cocok untuk program yang hasil cache berdasarkan isi papan klip saat ini dan perlu mengetahui apakah perhitungan masih valid sebelum menggunakan hasil dari cache tersebut. Perhatikan bahwa ini bukan metode pemberitahuan dan tidak boleh digunakan dalam perulangan polling. Untuk diberi tahu ketika konten clipboard berubah, gunakan pendengar format clipboard atau penampil clipboard.
Membuat Pendengar Format Clipboard
Pendengar format clipboard adalah jendela yang telah terdaftar untuk diberi tahu ketika konten clipboard telah berubah. Metode ini direkomendasikan untuk membuat jendela penampil clipboard karena lebih mudah untuk menerapkan dan menghindari masalah jika program gagal mempertahankan rantai penampil clipboard dengan benar atau jika jendela di rantai penampil clipboard berhenti merespons pesan.
Jendela mendaftar sebagai pendengar format clipboard dengan memanggil AddClipboardFormatListener
fungsi . Ketika konten clipboard berubah, jendela diposting pesan WM_CLIPBOARDUPDATE
. Pendaftaran tetap berlaku sampai jendela membatalkan pendaftaran sendiri dengan memanggil RemoveClipboardFormatListener
fungsi .
Membuat Jendela Penampil Clipboard
Jendela penampil papan klip menampilkan isi papan klip saat ini, dan menerima pesan ketika isi papan klip berubah. Untuk membuat jendela penampil clipboard, aplikasi Anda harus melakukan hal berikut:
- Tambahkan jendela ke rantai penampil clipboard.
WM_CHANGECBCHAIN
Proses pesan.WM_DRAWCLIPBOARD
Proses pesan.- Hapus jendela dari rantai penampil clipboard sebelum dihancurkan.
Menambahkan Jendela ke Rantai Penampil Clipboard
Jendela menambahkan dirinya ke rantai penampil clipboard dengan memanggil SetClipboardViewer
fungsi . Nilai yang dikembalikan adalah handel ke jendela berikutnya dalam rantai. Jendela harus melacak nilai ini — misalnya, dengan menyimpannya dalam variabel statis bernama hwndNextViewer
.
Contoh berikut menambahkan jendela ke rantai penampil clipboard sebagai respons terhadap WM_CREATE
pesan.
case WM_CREATE:
// Add the window to the clipboard viewer chain.
hwndNextViewer = SetClipboardViewer(hwnd);
break;
Cuplikan kode ditampilkan untuk tugas-tugas berikut:
- Memproses
WM_CHANGECBCHAIN
Pesan - Menghapus Jendela dari Rantai Penampil Clipboard
- Memproses
WM_DRAWCLIPBOARD
Pesan - Contoh Penampil Papan Klip
Memproses WM_CHANGECBCHAIN
Pesan
Jendela penampil clipboard menerima WM_CHANGECBCHAIN
pesan ketika jendela lain menghapus dirinya sendiri dari rantai penampil clipboard. Jika jendela yang dihapus adalah jendela berikutnya dalam rantai, jendela yang menerima pesan harus membatalkan tautan jendela berikutnya dari rantai. Jika tidak, pesan ini harus diteruskan ke jendela berikutnya dalam rantai.
Contoh berikut menunjukkan pemrosesan WM_CHANGECBCHAIN
pesan.
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;
Menghapus Jendela dari Rantai Penampil Clipboard
Untuk menghapus dirinya sendiri dari rantai penampil clipboard, jendela memanggil ChangeClipboardChain
fungsi. Contoh berikut menghapus jendela dari rantai penampil clipboard sebagai respons terhadap WM_DESTROY
pesan.
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
Memproses WM_DRAWCLIPBOARD
Pesan
Pesan WM_DRAWCLIPBOARD
memberi tahu jendela penampil papan klip bahwa isi papan klip telah berubah. Jendela harus melakukan hal berikut saat memproses WM_DRAWCLIPBOARD
pesan:
- Tentukan format papan klip mana yang tersedia untuk ditampilkan.
- Ambil data clipboard dan tampilkan di jendela. Atau jika format clipboard adalah
CF_OWNERDISPLAY
, kirim pesanWM_PAINTCLIPBOARD
ke pemilik clipboard. - Kirim pesan ke jendela berikutnya di rantai penampil clipboard.
Untuk contoh pemrosesan WM_DRAWCLIPBOARD
pesan, lihat contoh daftar di Contoh Penampil Clipboard.
Contoh Penampil Papan Klip
Contoh berikut menunjukkan aplikasi penampil clipboard sederhana.
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;
}