Custom Control на MFC
От: AcidTheProgrammer Россия https://hts.tv/
Дата: 25.01.05 13:52
Оценка:
Все добрый день.

Столкнулся со следующей проблеммой:
Есть куча диалогов в которых используется некий CustomControl написанный на чистом API вставленный в редакторе ресурсов как Custom Control с указанием класса, стиле и т.д...

Мне понадобилось вмето него вставить свой, но написанный на MFC. Можно ли его вставлять также в редакторе ресурсов,т.е без написание своего кода, сабклашенья и т.д.... и если можно, то как это сделать, как написать контрол, что б он мог всталятся из редактора?
Re: Custom Control на MFC
От: kmn Украина  
Дата: 25.01.05 14:25
Оценка: 2 (1)
Здравствуйте, AcidTheProgrammer, Вы писали:

ATP>Все добрый день.


ATP>Столкнулся со следующей проблеммой:

ATP>Есть куча диалогов в которых используется некий CustomControl написанный на чистом API вставленный в редакторе ресурсов как Custom Control с указанием класса, стиле и т.д...

ATP>Мне понадобилось вмето него вставить свой, но написанный на MFC. Можно ли его вставлять также в редакторе ресурсов,т.е без написание своего кода, сабклашенья и т.д.... и если можно, то как это сделать, как написать контрол, что б он мог всталятся из редактора?


1. Надо зарегистрировать класс окна

BOOL CCustomCtrl::RegisterControlClass()
{
    WNDCLASS wcls;

    // check to see if class already registered
    static const TCHAR szClass[] = _T("custom_control_name");
    if (::GetClassInfo(AfxGetInstanceHandle(), szClass, &wcls))
    {
        // name already registered - ok if it was us
        return (wcls.lpfnWndProc == (WNDPROC)CCustomCtrl::WndProcHook);
    }

    // set new values
    wcls.lpfnWndProc = CCustomCtrl::WndProcHook;
    wcls.hInstance = AfxGetInstanceHandle();
    wcls.lpszClassName = szClass;
    return (RegisterClass(&wcls) != 0);
}


2. Реализовать CCustomCtrl::WndProcHook

LRESULT CALLBACK
CCustomCtrl::WndProcHook(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    // create new item and attach it
    CCustomCtrl* pCtrl = new CCustomCtrl(TRUE);
    pCtrl->Attach(hWnd);

    // set up wndproc to AFX one, and call it
    ::SetWindowLongPtr(hWnd, GWL_WNDPROC, (LONG_PTR)AfxWndProc);

    // then call it for this first message
    return ::CallWindowProc(AfxWndProc, hWnd, msg, wParam, lParam);
}


Да, и описание класса
class CCustomCtrl : public CWnd
{
public:
    CCustomCtrl(BOOL bAutoDelete = FALSE) 
        : m_bAutoDelete(bAutoDelete) { };

    static BOOL RegisterControlClass();
protected:
    virtual void PostNcDestroy() { if (m_bAutoDelete) delete this; }

    static LRESULT CALLBACK EXPORT WndProcHook(HWND, UINT, WPARAM, LPARAM);

    //{{AFX_MSG(CCustomCtrl)
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP();

private:
     BOOL m_bAutoDelete;
};
Re[2]: Custom Control на MFC
От: AcidTheProgrammer Россия https://hts.tv/
Дата: 25.01.05 15:27
Оценка:
Здравствуйте, kmn, Вы писали:

kmn>Здравствуйте, AcidTheProgrammer, Вы писали:


ATP>>Все добрый день.


ATP>>Столкнулся со следующей проблеммой:

ATP>>Есть куча диалогов в которых используется некий CustomControl написанный на чистом API вставленный в редакторе ресурсов как Custom Control с указанием класса, стиле и т.д...

ATP>>Мне понадобилось вмето него вставить свой, но написанный на MFC. Можно ли его вставлять также в редакторе ресурсов,т.е без написание своего кода, сабклашенья и т.д.... и если можно, то как это сделать, как написать контрол, что б он мог всталятся из редактора?


kmn>1. Надо зарегистрировать класс окна


kmn>
kmn>BOOL CCustomCtrl::RegisterControlClass()
kmn>{
kmn>    WNDCLASS wcls;

kmn>    // check to see if class already registered
kmn>    static const TCHAR szClass[] = _T("custom_control_name");
kmn>    if (::GetClassInfo(AfxGetInstanceHandle(), szClass, &wcls))
kmn>    {
kmn>        // name already registered - ok if it was us
kmn>        return (wcls.lpfnWndProc == (WNDPROC)CCustomCtrl::WndProcHook);
kmn>    }

kmn>    // set new values
kmn>    wcls.lpfnWndProc = CCustomCtrl::WndProcHook;
kmn>    wcls.hInstance = AfxGetInstanceHandle();
kmn>    wcls.lpszClassName = szClass;
kmn>    return (RegisterClass(&wcls) != 0);
kmn>}
kmn>


kmn>2. Реализовать CCustomCtrl::WndProcHook


kmn>
kmn>LRESULT CALLBACK
kmn>CCustomCtrl::WndProcHook(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
kmn>{
kmn>    // create new item and attach it
kmn>    CCustomCtrl* pCtrl = new CCustomCtrl(TRUE);
kmn>    pCtrl->Attach(hWnd);

kmn>    // set up wndproc to AFX one, and call it
kmn>    ::SetWindowLongPtr(hWnd, GWL_WNDPROC, (LONG_PTR)AfxWndProc);

kmn>    // then call it for this first message
kmn>    return ::CallWindowProc(AfxWndProc, hWnd, msg, wParam, lParam);
kmn>}

kmn>


kmn>Да, и описание класса

kmn>
kmn>class CCustomCtrl : public CWnd
kmn>{
kmn>public:
kmn>    CCustomCtrl(BOOL bAutoDelete = FALSE) 
kmn>        : m_bAutoDelete(bAutoDelete) { };

kmn>    static BOOL RegisterControlClass();
kmn>protected:
kmn>    virtual void PostNcDestroy() { if (m_bAutoDelete) delete this; }

kmn>    static LRESULT CALLBACK EXPORT WndProcHook(HWND, UINT, WPARAM, LPARAM);

kmn>    //{{AFX_MSG(CCustomCtrl)
kmn>    //}}AFX_MSG
kmn>    DECLARE_MESSAGE_MAP();

kmn>private:
kmn>     BOOL m_bAutoDelete;
kmn>};

kmn>



Спасибо!! Вы это из исходников выкопали?
Re[3]: Custom Control на MFC
От: kmn Украина  
Дата: 25.01.05 16:05
Оценка: 6 (1)
Здравствуйте, AcidTheProgrammer, Вы писали:

ATP>Спасибо!! Вы это из исходников выкопали?

Это из MSDN-кого примера (Samples\VC\MFC\general\ctrltest\)

а вот только что сочинил (для VC 7.x и, надеюсь, выше):
namespace CustomCtrlHlp
{
    LRESULT CALLBACK WndProcHook(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        //    WM_NCDESTROY прийдет только в случае если по каким-то причинам не получилось 
        //    найти или создать зарегистрированный экземпляр класса 
        if (msg == WM_NCDESTROY)
            return ::DefWindowProc(hWnd, msg, wParam, lParam);

        ASSERT (msg == WM_NCCREATE);
        LPCREATESTRUCT lpCreateStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);
        ASSERT (lpCreateStruct != 0);

        // create new item and attach it
        CWnd * pWnd = static_cast<CWnd*>(CRuntimeClass::CreateObject(lpCreateStruct->lpszClass));
        if (pWnd == NULL)
            return 0; // жди WM_NCDESTROY
        pWnd->Attach(hWnd);

        // set up wndproc to AFX one, and call it
        ::SetWindowLongPtr(hWnd, GWL_WNDPROC, (LONG_PTR)AfxWndProc);

        // then call it for this first message
        return ::CallWindowProc(AfxWndProc, hWnd, msg, wParam, lParam);
    }

    BOOL Register(CRuntimeClass * pClass)
    {
        //    Класс должен быть наследником CWnd
        if (!pClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)))
        {
            return FALSE;
        }

        //    класс надо уметь создавать при помощи CRuntimeClass
        if (pClass->m_pfnCreateObject == NULL)
        {
            TRACE(traceAppMsg, 0,
                _T("Error: Trying to register window class which is not ")
                _T("DECLARE_DYNCREATE \nor DECLARE_SERIAL: %hs.\n"),
                pClass->m_lpszClassName);
            return FALSE;
        }

        // check to see if class already registered
        WNDCLASS wcls = {0};
        const TCHAR * szClass = pClass->m_lpszClassName;
        if (::GetClassInfo(AfxGetInstanceHandle(), szClass, &wcls))
        {
            // name already registered - ok if it was us
            return (wcls.lpfnWndProc == (WNDPROC)WndProcHook);
        }
        // set new values
        wcls.lpfnWndProc = WndProcHook;
        wcls.hInstance = AfxGetInstanceHandle();
        wcls.lpszClassName = szClass;
        return (RegisterClass(&wcls) != 0);
    }
}

#define DECLARE_REGISTERED_WINDOW(class_name, base_class)\
    class class_name : public base_class\
    {\
        DECLARE_SERIAL(class_name)\
    private:\
        class_name() {}\
        virtual void PostNcDestroy() { delete this; }\
    };\
    IMPLEMENT_SERIAL(class_name, base_class, -1)\


Пример использования:
app.cpp

DECLARE_REGISTERED_WINDOW(CCustomCtrl, CWnd);
DECLARE_REGISTERED_WINDOW(CCustomCtrl2, CWnd);
// ...        
DECLARE_REGISTERED_WINDOW(CCustomCtrlN, CWnd);

// CcustomCtrlsApp initialization
BOOL CcustomCtrlsApp::InitInstance()
{
    // InitCommonControls() is required on Windows XP if an application
    // manifest specifies use of ComCtl32.dll version 6 or later to enable
    // visual styles.  Otherwise, any window creation will fail.
    InitCommonControls();

    CWinApp::InitInstance();

    AfxEnableControlContainer();

    // Standard initialization
    // If you are not using these features and wish to reduce the size
    // of your final executable, you should remove from the following
    // the specific initialization routines you do not need
    // Change the registry key under which our settings are stored
    // TODO: You should modify this string to be something appropriate
    // such as the name of your company or organization
    SetRegistryKey(_T("Local AppWizard-Generated Applications"));

    VERIFY (CustomCtrlHlp::Register(RUNTIME_CLASS(CCustomCtrl)));
    VERIFY (CustomCtrlHlp::Register(RUNTIME_CLASS(CCustomCtrl2)));
      // ...
    VERIFY (CustomCtrlHlp::Register(RUNTIME_CLASS(CCustomCtrlN)));

    CcustomCtrlsDlg dlg;
    m_pMainWnd = &dlg;
    INT_PTR nResponse = dlg.DoModal();
    if (nResponse == IDOK)
    {
        // TODO: Place code here to handle when the dialog is
        //  dismissed with OK
    }
    else if (nResponse == IDCANCEL)
    {
        // TODO: Place code here to handle when the dialog is
        //  dismissed with Cancel
    }

    // Since the dialog has been closed, return FALSE so that we exit the
    //  application, rather than start the application's message pump.
    return FALSE;
}


Шаблон диалога:
IDD_CUSTOMCTRLS_DIALOG DIALOGEX 0, 0, 320, 200
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | 
    WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "Testing custom control registration"
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,263,7,50,16
    PUSHBUTTON      "Cancel",IDCANCEL,263,25,50,16
    CTEXT           "TODO: Place dialog controls here.",IDC_STATIC,10,96,300,
                    8
    CONTROL         "Custom1",IDC_CUSTOM1,"CCustomCtrl",WS_BORDER | 
                    WS_TABSTOP,7,7,169,139
END
Re[4]: Custom Control на MFC
От: AcidTheProgrammer Россия https://hts.tv/
Дата: 26.01.05 07:51
Оценка:
Здравствуйте, kmn:

Да круто.... супер нетривиально!!!!! Вот бы сам никогда не догадался сделать такую фабрку класса
И почему Microsoft токой пример не разместила гденьть в нормальном месте. Неужели они думают что этот код мало кому пригодится....
Re[4]: Custom Control на MFC
От: Денис Майдыковский Россия http://www.maydyk.com
Дата: 03.03.05 13:38
Оценка:
Здравствуйте, kmn, Вы писали:


kmn>Это из MSDN-кого примера (Samples\VC\MFC\general\ctrltest\)


Это всё конечно замечательно до тех пор, пока кто-нибудь не захочет отсабклассить такой контрол, например ничтоже сумяшеся вызвать DDX_Control(). Это может произойти если добавить переменную контрола через визард.

А произойдёт вот что.
DDX_Control() вызовёт СWnd::SubclassWindow(). А та первым делом позовёт CWwn::Attach(). Вот тут-то произойдёт облом! CWnd::Attach() надеется что описатель окна не присоеденено ни к одному СWnd в карте окон MFC.
ASSERT(FromHandlePermanent(hWnd) == NULL);


Как-бы не так! Описатель окна уже присоеденён к окну, созданному "фабрикой класса" в WndProcHook()! В результате появляются отладочные окна и утечки памяти.

Резюме. Контрол, написанный подобным образом нельзя использовать "стандартным" образом, через DDX_Control(). Через GetDlgItem() пожалуйста!

Интересно, есть-ли решение означенной проблемы?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.