WinAPI i subclassing

0

Witam.

Zaplanowałem sobie że zbuduję sobie kilka klas które będą odpowiedzialne za działanie poszczególnych elementów interfejsu budowanego programu - mianowicie okienko główne, pola tekstowe, przyciski itp. Problemem jest funkcja obsługi zdarzeń - a z tego co wiem może być jedna w systemie do której Windows będzie aktualnie przekazywał komunikaty o zdarzeniach zachodzących w naszej aplikacji.

Mimo wszystko jest możliwość utworzenia np. obiektu "edit" i przekazanie mu w czasie tworzenia adresu procedury obsługi zdarzeń innej niż dla głównego okienka. Jak w takim wypadku sprawić aby akcja wykonana na okienku "edit" powodowała wywołanie procedury przypisanej do tego właśnie okienka???

Poniżej część kodu:

Plik z funkcją "WinMain"

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE,LPSTR,int)
{
    

    okienko x = okienko(hInstance);
            x.ustawRozmiar(810, 620);
            x.wyswietl();
              
    okienkoTXT zzz = okienkoTXT(x.kontekst());
              zzz.wyswietl(10, 420, 600, 160);
    
    x.dodajOkienkoTxt(&zzz); 
    
    
   MSG msg;

   while( msg.message!=WM_QUIT )
   {
      if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
      {
         TranslateMessage( &msg );
         DispatchMessage( &msg );
      }
   }

   return 0;
}

Plik z definicją klasy okna głównego

class okienkoTXT;

class okienko
{

  public:      

    okienko(HINSTANCE );
    ~okienko();
    
    inline HWND kontekst(){ return hWnd;};
    
    void ustawRozmiar(int x_T, int y_T) { poczatkowyRozmiarX = x_T;
                                          poczatkowyRozmiarY = y_T;};

    void dodajOkienkoTxt(okienkoTXT* tmp){oknoTxt = tmp;};
    
    void wyswietl();
    
  private:
              
    int poczatkowyRozmiarX;
    int poczatkowyRozmiarY;
    
    HINSTANCE hInst;
    HWND  hPrzycisk;
    HWND  hWnd;
    HBITMAP hBitmapa;
    
    
    UINT rozmiarStrWndClass;       //cbSize
    UINT opcjeKlsy;                //style
    WNDPROC wskNaProcZdarzeniowa;  //lpfnWndProc
    int iloscDodatkowychZasKlasy;  //cbClsExtra
    int iloscAlokowanychZasobow;   //cbWndExtra
    HICON uchwytIkony;             //hIcon
    HINSTANCE uchwytInstancjiPr;   //hInstance
    HCURSOR uchhwytKursora;        //hCursor
    HBRUSH uchwytPedzla;           //hbrBackground
    LPCTSTR nazwaZasPaskaMenu;     //lpszMenuName
    LPCTSTR nazwaKlasyOkna;        //lpszClassName
    HICON uchwytIkonyMalej;        //hIconSm
    
    
    static LRESULT CALLBACK StaticProcOkna(HWND,UINT,WPARAM,LPARAM);
    LRESULT ProcOkna(UINT,WPARAM,LPARAM);
    
    inline void ustawHwnd(HWND temp){hWnd = temp;};   
    
    okienkoTXT* oknoTxt;
    
};

Plik z implementacją metod z klasy okna głównego


/*****************************************************************************/

okienko::okienko(HINSTANCE hInstance):hInst(hInstance), 
                                      hWnd(NULL), 
                                      hPrzycisk(NULL),                                       
                                      hBitmapa(NULL),
                                      oknoTxt(NULL)
                                
{
                                      
    poczatkowyRozmiarX        = 800;                                  
    poczatkowyRozmiarY        = 600;
                                          
    rozmiarStrWndClass        = sizeof(WNDCLASSEX);
    opcjeKlsy                          = CS_HREDRAW|CS_VREDRAW;
    wskNaProcZdarzeniowa      = (WNDPROC) StaticProcOkna;
    iloscDodatkowychZasKlasy  = 0;
    iloscAlokowanychZasobow   = 0;
    uchwytIkony               = NULL;
    uchwytInstancjiPr         = GetModuleHandle(NULL); 
    uchhwytKursora            = LoadCursor(NULL,IDC_ARROW);
    uchwytPedzla              = (HBRUSH) COLOR_WINDOW;
    nazwaZasPaskaMenu         = NULL;
    nazwaKlasyOkna            = "ProgramGl";
    uchwytIkonyMalej          = NULL;
                                  
};


/*****************************************************************************/

okienko::~okienko()
{
    //.......
};

/*****************************************************************************/

LRESULT okienko::ProcOkna(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
             
   switch(uMsg)
   {
       
   case WM_SIZE:
        if (oknoTxt!=NULL)
        {
           oknoTxt->zmienRozmiar(10, 10, 100, 100);    
           InvalidateRect(oknoTxt->kontekst(), NULL, FALSE);
        }
        break; 
         
   default:
      return DefWindowProc(hWnd,uMsg,wParam,lParam);
   }
   return DefWindowProc(hWnd,uMsg,wParam,lParam);
};

/*****************************************************************************/

LRESULT CALLBACK okienko::StaticProcOkna(HWND hWndx,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
        
   okienko* pParent;

 
   if(uMsg == WM_CREATE)
   {
      
      
      pParent = (okienko*)((LPCREATESTRUCT)lParam)->lpCreateParams;
      SetWindowLongPtr(hWndx,GWL_USERDATA,(LONG_PTR)pParent);
   }
   else
   {
      pParent = (okienko*)GetWindowLongPtr(hWndx,GWL_USERDATA);
      if(!pParent) return DefWindowProc(hWndx,uMsg,wParam,lParam);
   }

   pParent->ustawHwnd(hWndx);

      
   
   return pParent->ProcOkna(uMsg,wParam,lParam);        
        
}

/*****************************************************************************/

void okienko::wyswietl()
{
  
          
     WNDCLASSEX klasaOkna;
     ZeroMemory(&klasaOkna, sizeof(klasaOkna));
   
     {     
           klasaOkna.cbClsExtra         = iloscDodatkowychZasKlasy;
           klasaOkna.cbSize             = rozmiarStrWndClass;
           klasaOkna.cbWndExtra         = iloscAlokowanychZasobow;
           klasaOkna.hbrBackground      = uchwytPedzla;
           klasaOkna.hCursor            = uchhwytKursora;
           klasaOkna.hIcon              = uchwytIkony;
           klasaOkna.hIconSm            = uchwytIkonyMalej;
           klasaOkna.hInstance          = uchwytInstancjiPr;
           klasaOkna.lpfnWndProc        = wskNaProcZdarzeniowa ;
           klasaOkna.lpszClassName      = nazwaKlasyOkna;
           klasaOkna.lpszMenuName       = nazwaZasPaskaMenu;
           klasaOkna.style              = opcjeKlsy;
     }
     
     RegisterClassEx(&klasaOkna);
   
     hWnd = CreateWindow(nazwaKlasyOkna,
                         nazwaKlasyOkna,
                         WS_OVERLAPPEDWINDOW  | WS_CAPTION,  //& ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX
                         CW_USEDEFAULT,
                         CW_USEDEFAULT,
                         poczatkowyRozmiarX,
                         poczatkowyRozmiarY,
                         HWND_DESKTOP,
                         NULL,
                         hInst,
                         this);
                         
     SetWindowLongPtr(hWnd, GWL_WNDPROC, (LONG)StaticProcOkna);
      
     ShowWindow(hWnd,SW_SHOW);
     
     UpdateWindow(hWnd);

};

/*****************************************************************************/

Plik z definicją klasy elementu "edit"

class okienkoTXT
{
      
    public:  
      
      okienkoTXT(HWND hwnd_T){parentHwnd = hwnd_T;};
      ~okienkoTXT(){};
      
      void wyswietl(int, int, int, int);
    
      void zmienRozmiar(int, int, int, int);
    
      HWND kontekst(){return textHwnd;};
    
      
    private:
            
      static LRESULT CALLBACK staticProcOknaTXT(HWND, UINT, WPARAM, LPARAM);
      LRESULT procOknaTXT(UINT, WPARAM, LPARAM);
     
      inline void ustawHwnd(HWND temp){parentHwnd = temp;};
    
      int pozycjaX;
      int pozycjaY;
    
      int rozmiarX;
      int rozmiarY;
            
      HWND parentHwnd;
      HWND textHwnd;  
         
};

Plik z implementacją metod z klasy elementu "edit"


/*****************************************************************************/
LRESULT okienkoTXT::procOknaTXT(UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch  (Msg) 
	{	


     case WM_CREATE:
         
          break;
        
	  case WM_PAINT:
                    break;
         
      case WM_CTLCOLOREDIT:

                    SetTextColor((HDC)wParam, RGB(0, 255, 0));
                    SetBkColor((HDC)wParam, RGB(50, 50, 50));                          
                    return (LRESULT)(CreateSolidBrush(RGB(50, 50, 50)));
                    break;              

      default:
                    return DefWindowProc(parentHwnd,Msg,wParam,lParam);
     };
     
    return DefWindowProc(parentHwnd,Msg,wParam,lParam);
        
};

/*****************************************************************************/

LRESULT CALLBACK okienkoTXT::staticProcOknaTXT(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
        
   okienkoTXT* pParent;

   
   
   if(uMsg == WM_CREATE)
   {
      
      pParent = (okienkoTXT*)((LPCREATESTRUCT)lParam)->lpCreateParams;
      SetWindowLongPtr(hWnd,GWL_USERDATA,(LONG_PTR)pParent);
      
        
      
   }
   else
   {
      pParent = (okienkoTXT*)GetWindowLongPtr(hWnd,GWL_USERDATA);
      if(!pParent) return DefWindowProc(hWnd,uMsg,wParam,lParam);
      
      
   }

   //pParent->ustawHwnd(hWnd);
   
   
   
   
   return pParent->procOknaTXT(uMsg,wParam,lParam);
    
 
};

/*****************************************************************************/

void okienkoTXT::wyswietl(int x1, int y1, int x2, int y2)
{
    
    
    
   textHwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "", WS_SIZEBOX | WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL ,x1,y1,x2,y2, parentHwnd,(HMENU)200, GetModuleHandle(NULL), this);

   SetWindowLongPtr(textHwnd, GWL_WNDPROC, (LONG)staticProcOknaTXT);
   
   HFONT smallfont;
   
   smallfont = CreateFont(	13,			// logical height of font
							5,			// logical average character width
							0,			// angle of escapement
							0,			// base-line orientation angle
							0,			// font weight
							0,			// italic attribute flag
							0,			// underline attribute flag
							0,			// strikeout attribute flag
							0,			// character set identifier
							0,			// output precision
							0,				// clipping precision
							ANTIALIASED_QUALITY,	// output quality
							FF_MODERN | VARIABLE_PITCH ,		// pitch and family
							"Areal" );		// pointer to typeface name string
   
   SendMessage(textHwnd, WM_SETFONT, ( WPARAM ) smallfont, false );
   
   
   
   ShowWindow(textHwnd,SW_SHOW);
   UpdateWindow(textHwnd);         
          
    
};

/*****************************************************************************/

void okienkoTXT::zmienRozmiar(int x1, int y1, int x2, int y2)
{
     SetWindowPos(textHwnd, HWND_TOP, x1, y1, x2, y2, SWP_NOMOVE);
     UpdateWindow(textHwnd); 
};

/*****************************************************************************/

Oczywiście pousuwałem część kodu (metod nie mających teraz znaczenia) z klas - mam jedynie nadzieję że nie wyciąłem zbyt wiele. Nie ma min. dyrektyw "#include ..."

Małe objaśnienie kodu:

  1. Są dwie klasy główne - odpowiedzialna za okienko główne (klasa "okienko") oraz za pole tekstowe (klasa "okienkoTXT")
  2. Klasa okienka głównego ma dostęp do metod klasy okienka tekstowego przez wskaźnik do tej właśnie klasy pola tekstowego umieszczony w obrębie klasy okienka głównego (zmienna o nazwie "oknoTxt" w klasie "okienko")
  3. Klasa okienka głównego ma przypisaną swoją procedurę obsługi wywoływaną przez system Windows w momencie jakiegoś zdarzenia wykonanego na tym okienku (procedura: "... okienko::StaticProcOkna( ... ){ ... }" )
  4. Procedura "... okienko::StaticProcOkna ..." przekazuje sterowanie do funkcji wewnętrznej klasy "okienko" aby ta miała dostęp do wewnętrznych elementów tej klasy (funkcja ta to: "... okienko::ProcOkna( ... ){ ... }")
  5. Pole "edit" w klasie "okienkoTXT" ma również przypisaną swoją procedurę obsługi która (teoretycznie) powinna stać się domyślną procedurą gdy zostanie wywołane jakieś zdarzenie na polu "edit" (procedura ta to: "... okienkoTXT::StaticProcOknaTXT( ... ){ ... }")
  6. Procedura "... okienkoTXT::StaticProcOknaTXT ..." przekazuje sterowanie do funkcji wewnętrznej klasy "okienkoTXT" aby ta miała dostęp do wewnętrznych elementów tej klasy (funkcja ta to: "... okienkoTXT::ProcOknaTXT( ... ){ ... }")

Co chciałem osiągnąć:

  1. Klasy odpowiadają za poszczególne elementy "funkcjonalne" aplikacji - łącznie z obsługą zdarzeń przekazanych do "kontrolowanego" elementu aplikacji (np. powyżej pole "edit")
  2. Zmiana rozmiaru okna głównego (parent) powoduje jakąś ustaloną zmianę rozmiaru elementu na tym okienku (child)

I teraz co się dziej po uruchomieniu:

  1. Wyświetla się okno główne i na nim pole tekstowe. Gdy zmienię rozmiar okna głównego to faktycznie jest zmieniony rozmiar okienka tekstowego do określonych rozmiarów (czyli procedura "okienko::ProcOkna()" działa i jest wywoływana z komunikatem WM_SIZE). Natomiast problemem jest to że pole edit nie posiada określonego tła ani też czcionki... i w ogóle nie jest aktywne.
  2. Gdy w metodzie "void okienkoTXT::wyswietl(int x1, int y1, int x2, int y2) {}" zmienię kod "SetWindowLongPtr(textHwnd, GWL_WNDPROC, (LONG)staticProcOknaTXT);" na "SetWindowLongPtr(parentHwnd, GWL_WNDPROC, (LONG)staticProcOknaTXT);" to pole "edit" wyświetlane jest w odp. kolorze i kolor czcionki jest taki jak ustaliłem (pole jest aktywne i można w nim pisać) ale zmiana rozmiarów okna głównego NIE POCIĄGA za sobą określonej zmiany rozmiaru pola "edit"

NO I PYTANIE:
Coś tutaj totalnie schrzaniłem.... coś źle sobie "wykombinowałem" lub o czymś zapomniałem.... Jak zrobić aby każdy element zamknięty (opakowany) w klasę miał soją procedurę obsługi zdarzeń... i jak wymusić interakcję między klasami... W jaki sposób zrobić aby akcja na okienku powodowała wywołanie procedury obsługi okienka, a akcja na polu "edit" powodowała wywołanie procedury obsługi pola "edit"?????</span>

ZA WSZELKĄ POMOC SERDECZNIE DZIĘKUJĘ!!! POZDRAWIAM!!!!
0

moze to bedzie pomocne
http://4programmers.net/Delphi/Artykuły/Wywołanie_funkcji_zwrotnych_z_klasy

w necie jest troche o tym w c++ (wiecej na pewno niz w delphi). Popatrzyl bym na codeproject.

0

Dzięki reichel. Podpowiedziano mi już że prawdopodobnie chodzi o brak wywołania CallWindowProc... jak znajdę chwilę czasu to postaram se przebudować kod aby uwzględniał wywołanie tej funkcji... i jak by coś wyszło - to dam znać.

pzdr.

1 użytkowników online, w tym zalogowanych: 0, gości: 1