Makale Özeti

Clipboard hepimizin hergün birçok kere yazı, resim, dosya kopyalayıp yapıştırmak için kullandığımız bir araç. Ben bu makalemde ilk olarak .NET'te Clipboard class'ını kullanarak clipboard'a veri kopyalama ve kopyalanmış olan veriyi okumanın nasıl yapılabileceğini basit bir örnek üzerinden anlatacağım. Ardından ise Win32 fonksiyonları ile bize sunulan Clipboard API'sini kullanarak panoya kopyalanmış olan yazıların görüntülendiği bir uygulama yapacağım.

Makale

    Merhabalar,

    Clipboard hepimizin hergün birçok kere yazı, resim, dosya kopyalayıp yapıştırmak için kullandığımız bir araç. Ben bu makalemde ilk olarak .NET'te Clipboard class'ını kullanarak clipboard'a veri kopyalama ve kopyalanmış olan veriyi okumanın nasıl yapılabileceğini basit bir örnek üzerinden anlatacağım. Ardından ise Win32 fonksiyonları ile bize sunulan Clipboard API'sini kullanarak panoya kopyalanmış olan yazıların görüntülendiği bir uygulama yapacağım.

    İlk olarak Clipboard'da tutulan yazı veya resmi uygulama üstündeki textbox veya picturebox'a yapıştırabileceğimiz ve textbox veya picturebox'daki resmi/yazıyı Clipboard'a kopyalayabileceğimiz uygulamamızı yazalım. Bir Windows uygulaması oluşturup formu aşağıdaki gibi tasarlayalım:



    Formu tasarladıktan sonra ilk önce yazı kopyalama/yapıştırma işlemlerini gerçekleştireceğimiz butonların click event'lerini yazalım:

private void btnYaziyiKopyala_Click(object sender, EventArgs e)
{
    Clipboard
.SetDataObject(txtYapistir.SelectedText, true);
}

private
void btnYaziyiYapistir_Click(object sender, EventArgs e)
{
    IDataObject
dObj = Clipboard.GetDataObject();
    if
(dObj.GetDataPresent(DataFormats.Text))
    {
        txtYapistir.Text = dObj.GetData(DataFormats.Text).ToString();
    }
}

    Görüldüğü gibi Clipboard nesnesinin iki tane önemli öğesi bulunmakta. Bunlardan birincisi olan SetDataObject metodu Clipboard'a veri eklemek için kullanılır. Bu metodun iki tane overload'ı vardır. Birinci overload'ı parametre olarak sadece object tipinde veri alır. Bu Clipboard'a kopyalanacak olan verinin tutulduğu parametredir. Diğer overload'ında ise object parametresinin yanısıra boolean tipinde bir parametre daha vardır. Bu parametrenin değerini true olarak set ettiğimizde uygulama sonlandığı zaman uygulamadan kopyalanmış olan verilerin Clipboard'dan silinmeden tutulmasını sağlarız. Böylece diğer uygulamalar da Clipboard'a kopyalanmış olan bu veriye erişebilirler. Bu parametreyi false olarak set ettiğimizde uygulama sonlanınca uygulama tarafından Clipboard'a kopyalanmış olan veriler de Clipboard'dan silinecektir. Biz kendi uygulamamızda bu parametreyi true olarak set ettik. 

    Clipboard'ın ikinci önemli öğesi olan GetDataObject fonksiyonu da Clipboard'da tutulmakta olan veriye erişebilmemizi sağlar. Bu fonksiyon IDataObject nesnesi döndürür. Fonksiyondan dönen IDataObject nesnesinin GetDataPresent fonksiyonunu kullanarak Clipboard'da tutulan verinin bizim istediğimiz tipte veri olup olmadığını anlayabiliriz. Biz bu örnekte dönen verinin tipi Text ise formumuzdaki textbox'ın Text property'sine yine IDataObject nesnesinin GetData fonksiyonunu kullanarak Clipboard'dan aldığımız veriyi atadık.

    Şimdi de yine benzer şekilde Clipboard'da duran bir resmi formumuz üstündeki picturebox'a atayalım ve formumuzdaki picturebox'da duran resmi Clipboard'a yapıştıralım. Bunun için Resmi Kopyala ve Resmi Yapıştır butonlarının Click eventlerine aşağıdaki kodu yazıyoruz:

private void btnResmiKopyala_Click(object sender, EventArgs e)
{
    Clipboard
.SetDataObject(pbxResim.Image, true);
}

private
void btnResmiYapistir_Click(object sender, EventArgs e)
{
    IDataObject
dObj = Clipboard.GetDataObject();
    if
(dObj.GetDataPresent(DataFormats.Bitmap))
    {
        pbxResim.Image = (Bitmap)dObj.GetData(DataFormats.Bitmap);
        pbxResim.SizeMode = PictureBoxSizeMode.StretchImage;
   
}
}

    Görüldüğü gibi bu örneği bir önceki örnekte yazdığımız kodla aynı mantıkta yazdık. Tek fark Clipboard'ın SetDataObject metodunda Text yerine Image nesnesini gönderdik ve veriyi  Clipboard'dan da okurken IDataObject nesnesinin GetDataPresent ve GetData fonksiyonlarında DataFormats enum'unun Bitmap öğesini parametre olarak gönderdik. Son olarak da formumuz üstündeki picturebox'ın SizeMode property'sini resmin tamamı picturebox'a sığsın diye StretchImage olarak set ettik.

    Uygulamamızda yazacağımız kodları tamamladık, artık çalıştırabiliriz:



    İlk olarak notepad'den kopyaladığım yazıyı uygulamadaki textbox'a Yazıyı Yapıştır butonuna tıklayarak yapıştırdım. Form üstündeki textbox'a birşeyler yazıp Yazıyı Kopyala butonuna basınca da text Clipboard'a kopyalanacaktır. Şimdi de paint'te bir resmi kopyalayıp form üstündeki picturebox'a yapıştıralım:



    Görüldüğü gibi .NET tarafından bize sunulan Clipboard class'ı ile uygulamamız üzerinden kopyalama-yapıştırma işlemlerini idare etmek oldukça kolay bir işlem. Şimdi sırada API yardımıyla Clipboard işlemlerini yapacağımız formumuzu geliştirmek var. Bu formumuz Clipboard'ı dinleyecek ve Clipboard'a bir yazı kopyalandığında formumuz üstündeki textbox'a yazıyı yapıştıracak. İlk olarak basitçe formumuzu aşağıdaki gibi tasarlayalım:



    Bu işlemi gerçekleştirmek için de Win32'nin Clipboard işlemleri için sunduğu SetClipboardViewer, ChangeClipboardChain, SendMessage fonksiyonlarını kullanacağız. Bu fonksiyonları .NET ortamından çağırmak için de DllImport attribute'ını kullanarak fonksiyonları import edeceğiz:

...
using System.Runtime.InteropServices;

namespace
ClipboardMakale
{
    public
partial class Form2 : Form
   
{
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public
static extern IntPtr SetClipboardViewer(IntPtr hWnd);
       
[DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public
static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
...

    Ardından ise Clipboard'da tutulan verinin saklanacağı class level bir pointer tanımlayalım:
 

IntPtr SonrakiClipboardOgesi;

    Sonrasında ise formun load'ında SetClipboardViewer fonksiyonunu çağırıyoruz. Bu fonksiyon uygulamamızı Clipboard'ı izleyen uygulamaların listesine ekleyecek:

private void Form2_Load(object sender, EventArgs e)
{
    SonrakiClipboardOgesi = SetClipboardViewer(this.Handle);
}

    Şimdi ise sırada WndProc metodunu override etmek var:

protected override void WndProc(ref Message m)
{
    int
WM_DRAWCLIPBOARD = 0x0308;
    int
WM_CHANGECBCHAIN = 0x030D;

    if
(m.Msg == WM_DRAWCLIPBOARD)
   
{
        ClipboardOku();
        SendMessage(SonrakiClipboardOgesi, m.Msg, m.WParam, m.LParam);
    }
    else
if (m.Msg == WM_CHANGECBCHAIN)
   
{
        if
(m.WParam == SonrakiClipboardOgesi)
        {
            SonrakiClipboardOgesi = m.LParam;
        }
       
else
       
{
            SendMessage(SonrakiClipboardOgesi, m.Msg, m.WParam, m.LParam);
        }
    }
   
    base
.WndProc(ref m);
}

private
void ClipboardOku()
{
    IDataObject
dObj = Clipboard.GetDataObject();
    if
(dObj.GetDataPresent(DataFormats.Text))
    {
        textBox1.Text = (string)dObj.GetData(DataFormats.Text);
    }
}

    Windows, Clipboard'da tutulan veriler değiştiği zamanlarda verinin değiştiğine dair bildirimde bulunulacak olan pencerelerin bir listesini tutar. Ve WM_DRAWCLIPBOARD mesajı da clipboard içeriğini görüntüleyen ilk pencereye gönderilir. Bu, clipboard'ın içeriğini görüntüleyen pencerenin clipboard'daki en güncel içeriği elde etmesini sağlar. Bu içeriği sadece Clipboard içeriği görüntülemeye yetkili olan pencereler elde edebilirler. Biz zaten formumuz yüklenirken SetClipboardViewer fonksiyonunu çağırarak uygulamamıza clipboard içeriğini alabilme yetisini kazandırmıştık. Şimdi de WM_DRAWCLIPBOARD mesajını handle ederek Clipboard içeriğini elde edebiliriz. Kodda görüldüğü üzere pencereye WM_DRAWCLIPBOARD mesajı geldiğinde ilk olarak ClipboardOku metodunda gelen veriyi (Text ise) textbox'a yazdırıyoruz ve ardından SendMessage metodu ile de Clipboard'ı izleyen diğer pencerelere de Clipboard'daki bu değişikliğin yansımasını sağlıyoruz.

    Handle etmemiz gerekecek olan diğer mesaj da WM_CHANGECBCHAIN olacak. Bu mesaj Clipboard'ı izleyen uygulamalardan birisi ortadan kalktığında Clipboard'ı izleyen uygulamalardan ilkine gönderilir. Eğer uygulamamıza WM_CHANGECBCHAIN mesajı gelirse, uygulamamız SendMessage fonksiyonunu kullanarak bu mesajı Clipboard'ı izleyen sıradaki uygulamaya gönderecek. Eğer sıradaki uygulama zincirden kaldırılacak olan uygulama ise SonrakiClipboardOgesi zincirden kaldırılacak olan uygulamadan sonraki ilk uygulamaya atanacak.

    Son olarak bizim uygulamamız sonlanırken yani formun FormClosing event'inde ChangeClipboardChain fonksiyonunu çağırarak uygulamamızı Clipboard'ı izleyen uygulamaların listesinden çıkarıyoruz:
 

private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
    ChangeClipboardChain(this.Handle, SonrakiClipboardOgesi);
}

    Bu noktadan sonra artık uygulamamız Clipboard'daki değişiklikleri izleyebilecek ve Clipboard'daki herhangi bir değişiklik anında uygulamamıza yansıyacaktır. Örneğin uygulamamızı çalıştırıp notepad'den yazı kopyaladığımızda uygulamamızdaki textbox'da da kopyaladığımız yazıyı görebiliriz. Bu bilgiler ışığında dilerseniz siz de Office uygulamalarında olduğu gibi bilgisayarınızda çalışırken kopyaladığınız metinleri, resimleri saklayıp sonradan kullanabileceğiniz küçük bir uygulama yazabilirsiniz.

    Işıl ORHANEL
 

Örnek kodları buradan indirebilirsiniz.