Windows API

Custom Tooltip Kontrolü

► Kontroller

Windows API ile bir tooltip kontrolü oluşturmak için CreateWindowEx() fonksiyonunun ikinci parametresinde TOOLTIPS_CLASS değerini kullanabiliriz. Aynı zamanda, bir Static kontrolü kullanarak kendimize özgü kullanışlı Tooltip kontrolleri oluşturabiliriz.

Bu amaçla, Static kontrol için alt sınıf oluşturma (Subclassing) yöntemiyle yeni bir fonksiyon oluşturarak, bu fonksiyon içinde Static kontrol boyama işlemlerini WM_PAINT mesaj seçeneği altında yapabiliriz.

Dosya başlık bilgilerini oluşturma

1. Öncelikle Burada gösterildiği gibi bir Windows API projesi oluşturalım. Projeyle birlikte otomatik olarak oluşturulan main.c dosyasının başlangıç kısmını aşağıdaki şekilde düzenleyelim:

#if defined(UNICODE) && !defined(_UNICODE)
    #define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
    #define UNICODE
#endif

/* commctrl.h için */
#define _WIN32_WINNT 0x0501
#define _WIN32_IE 0x0501

#define IDC_BUTTON01 101
#define IDC_BUTTON02 102
#define IDC_STATIC 103

#include <tchar.h>
#include <windows.h>
#include <commctrl.h>

/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/* Static ve Button kontrolleri için alt sınıf (Subclassing) fonksiyonları */
LRESULT CALLBACK StaticProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
LRESULT CALLBACK ButtonProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);

/* Diğer fonksiyon başlık tanımlamaları */
void OnMouseMove(HWND hwnd);
RECT CalcTextDim (HWND hwnd);

BOOL g_MouseTracking;

/*  Make the class name into a global variable  */
TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp");

/* Buton ve Tooltip pencere oluşturma */
HWND hwndButton01, hwndButton02, hwndTooltip;

int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)

Aşağıdaki satır ile commctrl.h başlık dosyası ile alt sınıf oluşturma ile ilgili fonksiyonların ve değerlerin kullanımı sağlanır.

#include <commctrl.h>

Aşağıdaki satırlar ile Static ve Button kontrolleri için alt sınıf (Subclassing) fonksiyonları tanımlamaları programa eklenir.

CALLBACK StaticProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
LRESULT CALLBACK ButtonProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);

Aşağıdaki satırlar ile fare izlemeyi başlatan ve Static kontrol metin boyutlarını hesaplayan fonksiyon tanımlamaları programa eklenir.

OnMouseMove(HWND hwnd);
RECT CalcTextDim (HWND hwnd);

Aşağıdaki satır ile fare izlemenin devrede aktif olup olmadığını gösteren g_MouseTracking global değişkeni tanımlanır.

BOOL g_MouseTracking;

Aşağıdaki satırlar ile iki adet Buton kontrolü ve bir adet Tooltip kontrolü oluşturulur.

HWND hwndButton01, hwndButton02, hwndToolTip;

Windows Procedure düzenleme

WindowProcedure içinde oluşturacağımız WM_CREATE seçeneğine aşağıdaki satırları ekleyelim:

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
        case WM_CREATE:
             /* Buton oluşturma */
             hwndButton01 = CreateWindowEx(0, "BUTTON", "Button01",
                         WS_CHILD | WS_VISIBLE | BS_NOTIFY, 20, 20, 120, 25,
                         hwnd, (HMENU) IDC_BUTTON01, NULL, NULL);
             hwndButton02 = CreateWindowEx(0, "BUTTON", "Button02",
                         WS_CHILD | WS_VISIBLE | BS_NOTIFY, 160, 20, 120, 25,
                         hwnd, (HMENU) IDC_BUTTON02, NULL, NULL);
             /* Tooltip için Static kontrol oluşturma */
             hwndTooltip = CreateWindowEx(0, "STATIC", "İpucu",
                                WS_CHILD | WS_VISIBLE | SS_LEFT | SS_NOTIFY, 300, 20, 200, 20,
                                hwnd, (HMENU) IDC_STATIC, NULL, NULL);
             /* Tooltip penceresini gizleme */
             ShowWindow(hwndTooltip, SW_HIDE);

             /* Alt sınıf (Subclassing) fonksiyonlarını atama */
             SetWindowSubclass(hwndButton01, ButtonProc, 0, 0);
             SetWindowSubclass(hwndButton02, ButtonProc, 0, 0);
             SetWindowSubclass(hwndTooltip, StaticProc, 0, 0);

             break;

Önce iki adet Button kontrolü ve bir adet Tooltip için kullanılacak Static kontrol oluşturulur.

Static kontrol penceresi gizlenir.

SetWindowSubclass() fonksiyonu kullanılarak, Button kontrolleri için ButtonProc() fonksiyonu, Static kontrol için StaticProc() fonksiyonu alt sınıf fonksiyonu olarak atanır.

StaticProc() fonksiyonunu oluşturma

Tooltip olarak kullanacağımız Static kontrolü için alt sınıf fonksiyonunu oluşturarak programa ekleyelim:

LRESULT CALLBACK StaticProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
  switch (message)
  {
     case WM_PAINT:
     {
          PAINTSTRUCT ps;
          HDC hdc;
          HPEN hPen;
          HFONT hFont;
          RECT rc;
          char cdizi[100];

          /* Pencere metin içeriğini alma */
          GetWindowText(hwnd, cdizi, sizeof(cdizi));

          hdc = BeginPaint(hwnd, &ps);
          SetBkMode(hdc, TRANSPARENT);

          GetClientRect(hwnd, &rc);
          hPen = CreatePen(PS_SOLID, 0, RGB(244, 244, 244)); /* Çerçeve rengi */
          SelectObject(hdc, hPen);
          hFont = CreateFont(13, 0, 0, 0, FW_NORMAL, 0, 0, 0, TURKISH_CHARSET, 0, 0, 0, 0, TEXT("Tahoma"));
          SelectObject(hdc, hFont);

          SelectObject(hdc, GetStockObject(DC_BRUSH));
          SetDCBrushColor(hdc, RGB(165, 226, 165)); /* Arka plan rengi */

          Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom); /* Dikdörtgen çizer ve içini doldurur. */
          DrawText(hdc, cdizi, strlen(cdizi), &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); /* Metin içeriğini yazar. */

          EndPaint(hwnd, &ps);
          return 0L;
     }
     break;
  }
  return DefSubclassProc(hwnd, message, wParam, lParam);
}

Static kontrol ile ilgili tüm boyama işlemleri StaticProc() fonksiyonu içinde WM_PAINT mesajı ile yapılır.

  1. GetWindowText() fonksiyonu ile Static kontrol metin değeri alınır.
  2. BeginPaint() fonksiyonu ile boyama işlemi başlatılır.
  3. SetBkMode() fonksiyonu ile yazılan yazının arka planı değiştirmesi engellenir.
  4. GetClientRect() fonksiyonu ile Static kontrolün çerçeve boyutları alınır.
  5. CreatePen() fonksiyonu ile Static kontrolün çerçeve rengi belirlenir.
  6. CreateFont() fonksiyonu ile Static kontrolün font tipi belirlenir.
  7. SetDCBrushColor() fonksiyonu ile Static kontrolün arka plan rengi belirlenir.
  8. Rectangle() fonksiyonu ile Static kontrolün çerçevesi çizilir.
  9. DrawText() fonksiyonu ile Static kontrolün metin kısmı yazılır.
  10. EndPaint() fonksiyonu ile boyama işlemi sonlandırılır.

ButtonProc() fonksiyonunu oluşturma

Button kontrolleri için alt sınıf fonksiyonunu oluşturarak programa ekleyelim:

LRESULT CALLBACK ButtonProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
  switch (message)
  {
     case WM_MOUSEMOVE: /* TrackMouseEvent() gerekmez */
     {
          POINT p;
          RECT rcButton, rcTooltip;
          char cdizi[100];

          /* Eğer fare izleme devre dışı ise, izlemeyi başlatma */
          if (!g_MouseTracking) OnMouseMove(hwnd);

          if (!IsWindowVisible(hwndTooltip)) {
              GetWindowText(hwnd, cdizi, sizeof(cdizi));
              if (hwnd==hwndButton02) strcat(cdizi, " ipucu");
              SetWindowText(hwndTooltip, cdizi);
              GetCursorPos(&p); /* Ekrana göre cursor koordinatları */
              ScreenToClient(hwnd, &p); /* Kontrola göre cursor koordinatları */
              GetClientRect(hwnd,&rcButton); /* Buton pencere genişlik ve yüksekliği */
              MapWindowPoints(hwnd, GetParent(hwnd), (LPPOINT)&rcButton, 2); /* Butonun ana pencere üzerindeki koordinatları */
              /* Tooltip penceresinin metin uzunluğuna göre pencere boyutunu hesaplama */
              rcTooltip = CalcTextDim (hwndTooltip);
              /* Tooltip penceresinin yerini ve boyutunu ayarlama */
              MoveWindow(hwndTooltip, p.x+rcButton.left, rcButton.bottom+5, rcTooltip.right+10, rcTooltip.bottom+4, TRUE);
              ShowWindow(hwndTooltip, SW_SHOW);
          }
          return 0;
     }

     case WM_MOUSELEAVE: /* TrackMouseEvent() gerekir */

          ShowWindow(hwndTooltip, SW_HIDE);
          g_MouseTracking = FALSE;
          return 0;

  }

  return DefSubclassProc(hwnd, message, wParam, lParam);
}

ButtonProc() fonksiyonu içinde WM_MOUSEMOVE ve WM_MOUSELEAVE mesajlarına işlem yapılarak Tooltip olarak kullanılan Static kontrolün farenin üzerinde bulunduğu butonun değerlerine göre ekran da gösterilmesi ya da gizlenmesi sağlanır.

Bir alt sınıf fonksiyonunda WM_MOUSEMOVE mesajı TrackMouseEvent() fonksiyonu çağrılmadan alınabilir ve işlem yapılabilir. Ancak, WM_MOUSELEAVE mesajının alınmakı ve işlem yapılabilmesi için TrackMouseEvent() fonksiyonunun çağrılarak fare hareketlerinin izlenmesi gerekir.

WM_MOUSEMOVE mesajı alındığında, yani fare buton kontrollerinden herhangi bir üzerinde hareket ettiğinde, eğer g_MouseTracking global değişkeni FALSE bir değer taşıyorsa, OnMouseMove() fonksiyonu çağrılarak TrackMouseEvent() fonksiyonu farenin üzerinde olduğu Buton kontrolü pencere değeri ile çalıştırılır.

Eğer Tooltip penceresi (Static kontrol) görünür değilse,

  1. GetWindowText() fonksiyonu ile aktif butonun metin değeri alınır.
  2. Button02 butonu aktif ise metin değerine " ipucu" ifadesi eklenir.
  3. SetWindowText() fonksiyonu ile Static kontrolün metin değeri değiştirilir.
  4. GetCursorPos() fonksiyonu ile ekrana göre cursor koordinatları alınır.
  5. ScreenToClient() fonksiyonu ile kontrola göre cursor koordinatları alınır.
  6. GetClientRect() fonksiyonu ile buton pencere genişlik ve yüksekliği alınır.
  7. MapWindowPoints() fonksiyonu ile butonun ana pencere üzerindeki koordinatları alınır.
  8. CalcTextDim() fonksiyonu ile Static kontrolün çerçeve boyutları metin genişliğine ve yüksekliğine göre hesaplanır.
  9. MoveWindow() fonksiyonu ile Static kontrolün koordinat ve boyutları ayarlanır.
  10. ShowWindow() fonksiyonu ile Static kontrol ekranda gösterilir.

WM_MOUSELEAVE mesajı alındığında, yani fare buton kontrollerinden herhangi bir üzerinden ayrıldığında, ShowWindow() fonksiyonu ile Static kontrol gizlenir ve g_MouseTracking global değişkenine FALSE değeri atanır.

Eğer aşağıdaki ilk ve son satırı devre dışı bırakır, yani IsWindowVisible() fonksiyonunu çalıştırmazsanız, fare buton kontrollerinden herhangi biri üzerinde gezinirken, Tooltip penceresi sürekli hareket eder.

 // if (!IsWindowVisible(hwndTooltip)) {
     GetWindowText(hwnd, cdizi, sizeof(cdizi));
     if (hwnd==hwndButton02) strcat(cdizi, " ipucu");
     SetWindowText(hwndTooltip, cdizi);
     GetCursorPos(&p); /* Ekrana göre cursor koordinatları */
     ScreenToClient(hwnd, &p); /* Kontrola göre cursor koordinatları */
     GetClientRect(hwnd,&rcButton); /* Buton pencere genişlik ve yüksekliği */
     MapWindowPoints(hwnd, GetParent(hwnd), (LPPOINT)&rcButton, 2); /* Butonun ana pencere üzerindeki koordinatları */
     /* Tooltip penceresinin metin uzunluğuna göre pencere boyutunu hesaplama */
     rcTooltip = CalcTextDim (hwndTooltip);
     /* Tooltip penceresinin yerini ve boyutunu ayarlama */
     MoveWindow(hwndTooltip, p.x+rcButton.left, rcButton.bottom+5, rcTooltip.right+10, rcTooltip.bottom+4, TRUE);
     ShowWindow(hwndTooltip, SW_SHOW);
// }

OnMouseMove() fonksiyonunu oluşturma

Fare izlemeyi aktif hale getiren fonksiyonu oluşturarak programa ekleyelim:

void OnMouseMove(HWND hwnd)
{
  /* Mouse izlemeyi devreye sokma */
  TRACKMOUSEEVENT tme;
  tme.cbSize = sizeof(tme);
  tme.hwndTrack = hwnd;
  tme.dwFlags = TME_LEAVE;
  TrackMouseEvent(&tme);
  g_MouseTracking = TRUE;
}

CalcTextDim() fonksiyonunu oluşturma

Static kontrol metin boyutlarına göre kontrolün genişliğini ve yüksekliğini hesaplayan fonksiyonu oluşturarak programa ekleyelim:

RECT CalcTextDim (HWND hwnd)
{
  HDC hdc;
  HFONT hFont;
  RECT rc = {0};
  char cdizi[100];

  /* Pencere metin içeriğini alma */
  GetWindowText(hwnd, cdizi, sizeof(cdizi));

  hdc = GetDC(hwnd);
  SetBkMode(hdc, TRANSPARENT);

  hFont = CreateFont(13, 0, 0, 0, FW_NORMAL, 0, 0, 0, TURKISH_CHARSET, 0, 0, 0, 0, TEXT("Tahoma"));
  SelectObject(hdc, hFont);

  /* Metin içeriğine göre çerçevenin genişliğini ve yüksekliğini hesaplama */
  DrawText(hdc, cdizi, strlen(cdizi), &rc, DT_CALCRECT);

  ReleaseDC(hwnd, hdc);

  return rc;
}

Kodların son hali (main.c dosyası)

Yukarıdaki kodları eklediğimizde, main.c dosyasının en son hali aşağıdaki şekilde olacaktır.

main.c

#if defined(UNICODE) && !defined(_UNICODE)
    #define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
    #define UNICODE
#endif

/* commctrl.h için */
#define _WIN32_WINNT 0x0501
#define _WIN32_IE 0x0501

#define IDC_BUTTON01 101
#define IDC_BUTTON02 102
#define IDC_STATIC 103

#include <tchar.h>
#include <windows.h>
#include <commctrl.h>

/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/* Static ve Button kontrolleri için alt sınıf (Subclassing) fonksiyonları */
LRESULT CALLBACK StaticProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
LRESULT CALLBACK ButtonProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);

/* Diğer fonksiyon başlık tanımlamaları */
void OnMouseMove(HWND hwnd);
RECT CalcTextDim (HWND hwnd);

BOOL g_MouseTracking;

/*  Make the class name into a global variable  */
TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp");

/* Buton ve Tooltip pencere oluşturma */
HWND hwndButton01, hwndButton02, hwndTooltip;

int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           _T("Tooltip kontrolü oluşturma"),       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           544,                 /* The programs width */
           385,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}

/*  This function is called by the Windows function DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
        case WM_CREATE:
             /* Buton oluşturma */
             hwndButton01 = CreateWindowEx(0, "BUTTON", "Button01",
                         WS_CHILD | WS_VISIBLE | BS_NOTIFY, 20, 20, 120, 25,
                         hwnd, (HMENU) IDC_BUTTON01, NULL, NULL);
             hwndButton02 = CreateWindowEx(0, "BUTTON", "Button02",
                         WS_CHILD | WS_VISIBLE | BS_NOTIFY, 160, 20, 120, 25,
                         hwnd, (HMENU) IDC_BUTTON02, NULL, NULL);
             /* Tooltip için Static kontrol oluşturma */
             hwndTooltip = CreateWindowEx(0, "STATIC", "İpucu",
                                WS_CHILD | WS_VISIBLE | SS_LEFT | SS_NOTIFY, 300, 20, 200, 20,
                                hwnd, (HMENU) IDC_STATIC, NULL, NULL);
             /* Tooltip penceresini gizleme */
             ShowWindow(hwndTooltip, SW_HIDE);

             /* Alt sınıf (Subclassing) fonksiyonlarını atama */
             SetWindowSubclass(hwndButton01, ButtonProc, 0, 0);
             SetWindowSubclass(hwndButton02, ButtonProc, 0, 0);
             SetWindowSubclass(hwndTooltip, StaticProc, 0, 0);

             break;

        case WM_DESTROY:
             PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
             break;
        default:                        /* for messages that we don't deal with */
             return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

LRESULT CALLBACK StaticProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
  switch (message)
  {
     case WM_PAINT:
     {
          PAINTSTRUCT ps;
          HDC hdc;
          HPEN hPen;
          HFONT hFont;
          RECT rc;
          char cdizi[100];

          /* Pencere metin içeriğini alma */
          GetWindowText(hwnd, cdizi, sizeof(cdizi));

          hdc = BeginPaint(hwnd, &ps);
          SetBkMode(hdc, TRANSPARENT);

          GetClientRect(hwnd, &rc);
          hPen = CreatePen(PS_SOLID, 0, RGB(244, 244, 244)); /* Çerçeve rengi */
          SelectObject(hdc, hPen);
          hFont = CreateFont(13, 0, 0, 0, FW_NORMAL, 0, 0, 0, TURKISH_CHARSET, 0, 0, 0, 0, TEXT("Tahoma"));
          SelectObject(hdc, hFont);

          SelectObject(hdc, GetStockObject(DC_BRUSH));
          SetDCBrushColor(hdc, RGB(165, 226, 165)); /* Arka plan rengi */

          Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom); /* Dikdörtgen çizer ve içini doldurur. */
          DrawText(hdc, cdizi, strlen(cdizi), &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); /* Metin içeriğini yazar. */

          EndPaint(hwnd, &ps);
          return 0L;
     }
     break;
  }
  return DefSubclassProc(hwnd, message, wParam, lParam);
}

LRESULT CALLBACK ButtonProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
  switch (message)
  {
     case WM_MOUSEMOVE: /* TrackMouseEvent() gerekmez */
     {
          POINT p;
          RECT rcButton, rcTooltip;
          char cdizi[100];

          /* Eğer fare izleme devre dışı ise, izlemeyi başlatma */
          if (!g_MouseTracking) OnMouseMove(hwnd);

          if (!IsWindowVisible(hwndTooltip)) {
              GetWindowText(hwnd, cdizi, sizeof(cdizi));
              if (hwnd==hwndButton02) strcat(cdizi, " ipucu");
              SetWindowText(hwndTooltip, cdizi);
              GetCursorPos(&p); /* Ekrana göre cursor koordinatları */
              ScreenToClient(hwnd, &p); /* Kontrola göre cursor koordinatları */
              GetClientRect(hwnd,&rcButton); /* Buton pencere genişlik ve yüksekliği */
              MapWindowPoints(hwnd, GetParent(hwnd), (LPPOINT)&rcButton, 2); /* Butonun ana pencere üzerindeki koordinatları */
              /* Tooltip penceresinin metin uzunluğuna göre pencere boyutunu hesaplama */
              rcTooltip = CalcTextDim (hwndTooltip);
              /* Tooltip penceresinin yerini ve boyutunu ayarlama */
              MoveWindow(hwndTooltip, p.x+rcButton.left, rcButton.bottom+5, rcTooltip.right+10, rcTooltip.bottom+4, TRUE);
              ShowWindow(hwndTooltip, SW_SHOW);
          }
          return 0;
     }

     case WM_MOUSELEAVE: /* TrackMouseEvent() gerekir */

          ShowWindow(hwndTooltip, SW_HIDE);
          g_MouseTracking = FALSE;
          return 0;

  }

  return DefSubclassProc(hwnd, message, wParam, lParam);
}

void OnMouseMove(HWND hwnd)
{
  /* Mouse izlemeyi devreye sokma */
  TRACKMOUSEEVENT tme;
  tme.cbSize = sizeof(tme);
  tme.hwndTrack = hwnd;
  tme.dwFlags = TME_LEAVE;
  TrackMouseEvent(&tme);
  g_MouseTracking = TRUE;
}

RECT CalcTextDim (HWND hwnd)
{
  HDC hdc;
  HFONT hFont;
  RECT rc = {0};
  char cdizi[100];

  /* Pencere metin içeriğini alma */
  GetWindowText(hwnd, cdizi, sizeof(cdizi));

  hdc = GetDC(hwnd);
  SetBkMode(hdc, TRANSPARENT);

  hFont = CreateFont(13, 0, 0, 0, FW_NORMAL, 0, 0, 0, TURKISH_CHARSET, 0, 0, 0, 0, TEXT("Tahoma"));
  SelectObject(hdc, hFont);

  /* Metin içeriğine göre çerçevenin genişliğini ve yüksekliğini hesaplama */
  DrawText(hdc, cdizi, strlen(cdizi), &rc, DT_CALCRECT);

  ReleaseDC(hwnd, hdc);

  return rc;
}

Program derleyip çalıştırdığımızda aşağıdakine benzer bir ekran görüntüsü karşımıza gelecektir. Fareyi buton kontrolünün üzerine getirdiğimizde ipucu penceresi ekranda görünecek, fareyi buton kontrolü üzerinden çektiğimizde ipucu penceresi kaybolacaktır.

Proje dosyaları ve .exe dosya

Programın kaynak kodları

Programın exe dosyası