Menggunakan Clipboard
Bagian ini memiliki sampel kode untuk tugas-tugas berikut:
-
Menerapkan Perintah Potong, Salin, dan Tempel
- Memilih data
- Membuat Menu Edit
-
Memproses Pesan
WM_INITMENUPOPUP
-
Memproses Pesan
WM_COMMAND
- Menyalin Informasi ke clipboard
- Menempelkan Informasi dari clipboard
- Mendaftarkan Format Clipboard
-
Memproses Pesan
WM_RENDERFORMAT
danWM_RENDERALLFORMATS
-
Memproses Pesan
WM_DESTROYCLIPBOARD
- Menggunakan Format Owner-Display Clipboard
- Konten Clipboard Pemantauan
- Mengkueri Nomor Urutan Clipboard
- Membuat Pemantau Format Clipboard
- Membuat Jendela Penampil Clipboard
-
Menambahkan Jendela ke Rantai Tampilan Clipboard
-
Memproses
WM_CHANGECBCHAIN
Pesan - Menghapus Jendela dari Rantai Penampil Clipboard
-
Memproses Pesan
WM_DRAWCLIPBOARD
- Contoh Tampilan Clipboard
-
Memproses
Menerapkan Perintah Potong, Salin, dan Tempel
Bagian ini menjelaskan bagaimana perintah Potongstandar , Salin, dan Tempel diterapkan dalam aplikasi. Contoh di bagian ini menggunakan metode ini untuk menempatkan data di clipboard menggunakan format clipboard terdaftar, format CF_OWNERDISPLAY
, dan format CF_TEXT
. 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 pemicu berfungsi. Untuk informasi selengkapnya tentang akselerator keyboard, lihat Keyboard Accelerators.
Memproses Pesan WM_INITMENUPOPUP
Tidak semua perintah clipboard tersedia untuk pengguna pada waktu tertentu. Aplikasi harus memproses pesan WM_INITMENUPOPUP
untuk mengaktifkan item menu untuk perintah yang tersedia dan menonaktifkan perintah yang tidak tersedia.
Berikut ini adalah kasus WM_INITMENUPOPUP
untuk aplikasi bernama Label.
case WM_INITMENUPOPUP:
InitMenu((HMENU) wParam);
break;
Fungsi 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 Pesan WM_COMMAND
Untuk memproses perintah menu, tambahkan kasus WM_COMMAND
ke prosedur jendela utama aplikasi Anda. Berikut ini adalah kasus WM_COMMAND
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 Copy dan Cut, prosedur jendela memanggil fungsi EditCopy
yang ditentukan aplikasi. Untuk informasi selengkapnya, lihat Menyalin Informasi ke Clipboard. Untuk melakukan perintah Tempel, prosedur jendela memanggil fungsi EditPaste
yang ditentukan aplikasi. Untuk informasi selengkapnya tentang fungsi 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 clipboard dengan memanggil fungsi
OpenClipboard
. - Mengosongkan clipboard dengan memanggil fungsi
EmptyClipboard
. - Memanggil fungsi
SetClipboardData
sekali untuk setiap format clipboard yang disediakan aplikasi. - Menutup clipboard dengan memanggil fungsi
CloseClipboard
.
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 fungsi 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 EditPaste
yang ditentukan aplikasi menempelkan konten clipboard. Fungsi ini melakukan hal berikut:
- Membuka clipboard dengan memanggil fungsi
OpenClipboard
. - Menentukan format clipboard mana yang tersedia untuk diambil.
- Mengambil handle ke data dalam format yang dipilih dengan memanggil fungsi
GetClipboardData
. - Menyisipkan salinan data ke dalam dokumen. Handle yang dikembalikan oleh
GetClipboardData
masih dimiliki oleh clipboard, sehingga aplikasi tidak boleh membebaskannya atau membiarkannya dalam keadaan terkunci. - Menutup clipboard dengan memanggil fungsi
CloseClipboard
.
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 LABELBOX
yang ditentukan 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 fungsi 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 fungsi RegisterClipboardFormat
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;
Memproses Pesan WM_RENDERFORMAT
dan WM_RENDERALLFORMATS
Jika jendela meneruskan handel NULL
ke fungsi SetClipboardData
, jendela harus memproses pesan WM_RENDERFORMAT
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 pegangan data non-NULL
pada clipboard menggunakan fungsi SetClipboardData
. Jika prosedur jendela merender format sebagai respons terhadap pesan WM_RENDERFORMAT
, prosedur tersebut tidak boleh membuka clipboard sebelum memanggil SetClipboardData
. Namun, jika sedang merender satu atau beberapa format sebagai respons terhadap pesan WM_RENDERALLFORMATS
, maka harus membuka papan klip dan memastikan bahwa jendela masih memiliki papan klip tersebut sebelum memanggil SetClipboardData
, dan papan klip harus ditutup sebelum kembali.
Aplikasi Label memproses pesan WM_RENDERFORMAT
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 RenderFormat
yang ditentukan aplikasi, yang 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 Pesan WM_DESTROYCLIPBOARD
Jendela dapat memproses pesan WM_DESTROYCLIPBOARD
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 pesan WM_DESTROYCLIPBOARD
, sebagai berikut.
case WM_DESTROYCLIPBOARD:
if (pboxLocalClip != NULL)
{
LocalFree(pboxLocalClip);
pboxLocalClip = NULL;
}
break;
Menggunakan Format Clipboard Owner-Display
Jika jendela menempatkan informasi di clipboard dengan menggunakan format clipboard CF_OWNERDISPLAY
, jendela harus melakukan hal berikut:
- Proses pesan
WM_PAINTCLIPBOARD
. Pesan ini dikirim ke pemilik clipboard ketika sebagian jendela penampil clipboard harus dicat ulang. - Proses pesan
WM_SIZECLIPBOARD
. 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. Menanggapi pesan ini, aplikasi Label juga memperbarui strukturSIZE
untuk jendela penampil clipboard. - Proses pesan
WM_HSCROLLCLIPBOARD
danWM_VSCROLLCLIPBOARD
. Pesan ini dikirim ke pemilik clipboard ketika peristiwa bilah gulir terjadi di jendela penampil clipboard. - Proses pesan
WM_ASKCBFORMATNAME
. 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 ke belakang 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 isi clipboard diperbarui, sebuah nilai 32-bit yang dikenal sebagai nomor urutan clipboard ditingkatkan. Program dapat mengambil nomor urutan papan klip saat ini dengan memanggil fungsi GetClipboardSequenceNumber
. 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 notifikasi dan tidak boleh digunakan dalam perulangan pengambilan suara. 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 dibandingkan dengan membuat jendela penampil clipboard karena lebih mudah untuk diterapkan dan menghindari masalah jika program gagal mempertahankan rantai penampil clipboard dengan benar atau jika ada jendela dalam rantai penampil clipboard yang berhenti merespons pesan.
Jendela mendaftar sebagai pendengar format clipboard dengan memanggil fungsi AddClipboardFormatListener
. Ketika konten clipboard berubah, jendela menerima pesan WM_CLIPBOARDUPDATE
. Pendaftaran tetap berlaku sampai jendela membatalkan pendaftaran sendiri dengan memanggil fungsi RemoveClipboardFormatListener
.
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 rangkaian penampil clipboard.
- Proses pesan
WM_CHANGECBCHAIN
. - Proses pesan
WM_DRAWCLIPBOARD
. - Hapus jendela dari rantai penampil clipboard sebelum dihancurkan.
Menambahkan Jendela pada Urutan Penampil Clipboard
Jendela menambahkan dirinya ke rantai penampil clipboard dengan memanggil fungsi SetClipboardViewer
. Nilai yang dikembalikan adalah handle ke jendela berikutnya dalam urutan. Jendela harus melacak nilai ini — misalnya, dengan menyimpannya dalam variabel statis bernama hwndNextViewer
.
Contoh berikut menambahkan jendela ke rantai penampil clipboard sebagai respons terhadap pesan WM_CREATE
.
case WM_CREATE:
// Add the window to the clipboard viewer chain.
hwndNextViewer = SetClipboardViewer(hwnd);
break;
Cuplikan kode ditampilkan untuk tugas-tugas berikut:
-
Memproses Pesan
WM_CHANGECBCHAIN
- Menghapus Jendela dari Rantai Penampil Clipboard
-
Memproses
WM_DRAWCLIPBOARD
Pesan - Contoh dari Penampil Clipboard
Memproses Pesan WM_CHANGECBCHAIN
Jendela penampil clipboard menerima pesan WM_CHANGECBCHAIN
ketika jendela lain menghapus dirinya sendiri dari rantai penampil clipboard. Jika jendela yang dihapus adalah jendela berikutnya dalam rantai jendela, jendela yang menerima pesan harus memutuskan tautan jendela berikutnya dari rantai jendela. Jika tidak, pesan ini harus diteruskan ke jendela berikutnya dalam rantai.
Contoh berikut menunjukkan pemrosesan pesan 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;
Menghapus Jendela dari Rantai Penampil Clipboard
Untuk menghapus dirinya dari rantai penampil clipboard, jendela memanggil fungsi ChangeClipboardChain
. Contoh berikut menghapus jendela dari rantai penampil clipboard sebagai respons terhadap pesan WM_DESTROY
.
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
Memproses Pesan WM_DRAWCLIPBOARD
Pesan WM_DRAWCLIPBOARD
memberi tahu jendela penampil clipboard bahwa konten papan klip telah berubah. Jendela harus melakukan hal berikut saat memproses pesan WM_DRAWCLIPBOARD
:
- Tentukan format papan klip mana yang tersedia untuk ditampilkan.
- Ambil data clipboard dan tampilkan di jendela. Atau jika format clipboard
CF_OWNERDISPLAY
, kirim pesanWM_PAINTCLIPBOARD
ke pemilik clipboard. - Kirim pesan ke jendela berikutnya di rantai penampil clipboard.
Untuk contoh pemrosesan pesan WM_DRAWCLIPBOARD
, 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;
}