powered by simpleCommunicator - 2.0.60     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / C++ [игнор отключен] [закрыт для гостей] / Сообщения пользователя
37 сообщений из 37, показаны все 2 страниц
Сообщения пользователя
    #32212869
void_123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Предлагаю дискуссию.
Как организовать обмен пользовательскими «сообщениями»между классами приложения MFC, которые бы отражали общую бизнес-логику программы?
Вопрос не в том, как это вообще можно сделать, а как сделать эффективнее, с точки зрения скорости разработки приложения VC++.

Хотя вопрос касается приложений MFC, но для лучшего его понимания - начну со стиля построения приложений, который я уже лет 6 использую в BC++ Builder.
Удобно для оконной формы BC++ Builder с большим числом контролов (50-100, что характерно для приложений баз данных) определить две функции, например,bool FillControls(unsigned int msg) и bool DisplayControls(unsigned int msg) для обработки «сообщений» пользователя типа CLEAR_CONTROLS, READ_DATABASE, SAVE_DATABASE и т.д:
bool FillControls(unsigned int msg)
{
switch( msg )
{
case STARTUP: …..
case CLEAR_CONTROLS: …..
case READ_DATABASE: …..
}
}
Например, по нажатию кнопки «Открыть», обработчик кнопки делает последовательность вызовов FillControls(READ_DATABASE) и DisplayControls(READ_DATABASE). Это приводит к тому что, соединяемся с DB, читаем данные в контролы и перерисовываем их, контролируя на каждом шаге, что должно быть disable, что enable, что readonly и т.д.
Удобство заключается в том, что всё управление отображением и заполнением контролов (enable, visible и т.д) сосредоточено в одном месте. Вернее сосредоточена общая логика приложения в части управления контролами.
Такую обработку несложно построить в BC++, т.к. все контролы являются членами класса формы приложения. Т.е из указанных выше функций (методов формы) видны все контролы.
Гораздо сложнее это реализовать в VC++. Это связано с тем, что классы, в которых созданы члены-контролы ничего не знают друг о друге, и не видят соответствующих контролов в общем случае. Например, если Вы создали приложение с CTreeView – СFormView, где на форму положили CTabCtrl и для каждой закладки создали по диалогу CPage1,…., CPage20, то для каждого класса!!! придётся писать соответсвующие обработчики bool FillControls(unsigned int msg), bool DisplayControls. При этом необходимо ещё найти приемлемый механизм обмена сообщениями между классами.

У кого есть идеи? Предлагаю для затравки тупой метод решения задачи - использование глобальных указателей на каждый из диалогов. Умные – предложите Вы.

Делаем тупо:
extern CTvView* pTV;
extern CPage1* Page1;

extern CPage20* Page20;

bool FillControls(unsigned int msg)
{
switch( msg )
{
case STARTUP:
pTV->Clear();
Page1->ClearControls();
….
Page20->ClearControls();

}
}
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32213932
maratka
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
imho, (сначала нехотел ничего писать)
к сожалению, настолько сложных программ с БД не создавал, но думаю, на помощь здесь должна прийти архитектура "документ-вид", используемая в МФЦ-приложениях, как альтернатива жесткому каркасу приложения в Delphi/Builder. т.е. всю эту конструкцию удобно переместить в тело имеющегося в проекте CDocument-derived класса. все остальное (и суть кода, если я правильно понял) останется прежним. т.е. что-то типа:

CMyDocument.h:
...
class CMyDocument : public CDocument
{
...
public:
CTvView* m_pTV;
CPage1* m_Page1;
... // все остальные указатели на окна/формы приложения.
...
bool FillControls(unsigned int msg);
}

CMyDocument.cpp:
...
bool CMyDocument::FillControls(unsigned int msg)
{
switch( msg )
{
case STARTUP: …..
case CLEAR_CONTROLS: …..
case READ_DATABASE: …..
}
}

далее нужно "показать" документу все указатели типа m_pTV, это делается таким образом:
CTvView.cpp:
...
int CLeftView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{ // или подобный обработчик в других классах
...
AssertValid(this); // необязательная проверка себя
GetDocument()->m_pTV = this;
return 0;
}
...
как будто все.
в принципе, это стандартный рекомендуемый подход, используемый во многих примерах в MSDN и таким образом из класса Документа можно видеть любой статический объект (кроме CDialog-derived, которые должны создаваться в стеке - типа
CTvView::OnNewRecord(int i)
{
CMyDialog dlgNew;
dlgNew.m_dwValue = 1;
if (dlgNew.DoModal() == ID_OK)
{ // и тд. и т.п.}
}
но насколько я помню, когда программировал на CBuilder, там было еще более серьезное ограничение, типа того что нельзя проинициализировать поля формы (dlgNew.m_dwValue = 1;) до ее отображения на экране (dlgNew.DoModal()) за что его многие ругали.
еще раз имхо - если кого задел.
ps. если не нравится документ, можно использовать MainFrame или класс приложения - как угодно девелоперу.
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32214302
void_123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
maratka!
Спасибо за пост. Чё-то народ не активно обсуждает ВСЁ ЭТО.
В следующем постинге напишу ещё раз про постановку задачи.
Мож не поняли, что хочу? А тебе спасибо!

Теперь, по существу.

>> imho, (сначала нехотел ничего писать)
Почему? :)

>> ... к сожалению, настолько сложных программ с БД не создавал...
Да не сложное там приложение. Просто я хотел обсудить проблему архитектуры приложения. Извечный русский вопрос - как быть? как лучше? и т.д.

>> .... на помощь здесь должна прийти архитектура "документ-вид", ...
Так должна, но не приходит помощь.

Посмотри, по большому счёту, что в документе (члены класса-дочки CDocument) придётся создавать указатели на !!!ВСЕ!!! объекты, что мы их глобальными. Ясно, что спрятанные в качестве членов - симпатичнее.
Но об этом я попробую пропостить в следующем постинге.

class CMyDocument : public CDocument
{
...
public:
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
СМОТРИ ЗДЕСЬ БУДУТ ВСЕ 20-30 УКАЗАТЕЛЕЙ

CTvView* m_pTV;
CPage1* m_Page1;
... // все остальные указатели на окна/формы приложения.
...
bool FillControls(unsigned int msg);
}

Спаведливости ради надо сказать, что, конечно, CDocument придаст приложению некий автоматизм за счёт возможности использования UpdateAllView, но этом слишком маленькое преимущество CDocument (здесь имеется в виду только контекст обсуждаемой задачи).
Но вот подумай! Приложение имеет некоторую кнопку, по которой должен выкатиться некий модальный диалог с большим числом CWnd - объектов (диалогов, дерево тоже есть или листконтрол). Где тогда будем размещать указатели на эти объекты? CDocument уже не подойдёт.

Хрошая идея у тебя, что FillControls(unsigned int msg) инкапсулирована в CDocument, который мы самостийно сделали неким манагером сообщений.
Но об этом в следующем моём постинге.

>>...но насколько я помню, когда программировал на CBuilder, там было еще более серьезное ограничение, типа того что нельзя проинициализировать поля формы (dlgNew.m_dwValue = 1;) до ее отображения на экране (dlgNew.DoModal()) за что его многие ругали.

Ты, наверное, имел в виду ShowModal?
DoModal - это MFC.
А вообще, такого ограничения нет. Креатишь форму, заполняешь всё, что нужно, а затем - ShowModal. Например, такую функцию можно вызвать

//---------------------------------------------------------------------------
bool __fastcall ShowObject(TComponent* Owner,INFOOBJECTS * info,long SubClass,long SubTable)
{
TfrmObjectTables *frmOT;

frmOT = new TfrmObjectTables(Owner);
if (!frmOT) return FALSE;
if ( frmOT -> SetInfoObject(info, SubClass, SubTable))
f rmOT -> ShowModal();
delete frmOT;
return TRUE;
}

>>ps. если не нравится документ, можно использовать MainFrame или класс приложения - как угодно девелоперу.

maratka, не решит это проблему. Какая разница где хранить? Важно, чтобы был к этому хранилищу требуемый доступ. Но самое главное, что с ЭТИМ потом делать.
Пиши.
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32214304
void_123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Поскольку постов в топик на этом и других форумах не очень много, позволю себе подлить масла в огонь. Возможно, Вам не ясна сумбурно написанная постановка задачи. Вначале ещё раз о мотивации, затем отвечу на вопросы и замечания, а уж затем – масло в огонь.

*** МОТИВАЦИЯ ***
Пусть существует приложение MFC для работы, например, с базами данных. Стиль UI – explorer.:
-слева CTVView: public CTreeView
-справа CMainPanel: public CFormView.

На диалоге CMainPanel имеется CTabCtrl, который командует диалогами CPage1 (CPage1:public CDialog), CPage2,…, CPage20. Таким образом, общее число основных «независимых» классов, которые обрабатывают данные в видах справа и слева в CMainFrame – 22-25. Число контролов (кнопки, эдитбоксы, и т.д) для каждого класса от 5 до 10. Таким образом, общее число контролов составляет ~150 – 200.

Замечу, что объекты указанных классов (а значит и соответствующие контролы) «НЕЗАВИСИМЫ» в том смысле, что они почти ничего не знают друг о друге (идеология MFC). Если происходит некое СОБЫТИЕ, которое связано, например, с нажатием на кнопку «Редактировать» (CPage1::CButton m_StartEdit) на закладке CPage1::Page1, то остальные объекты приложения об этом ничего не знают. Обработчики кнопки CPage1::CButton m_StartEdit на закладке CPage1 не могут непосредственно повлиять, например, на состояние чекбоксов или эдитов на закладке CPage2, т.к. она (кнопка) не знает даже указателя на диалог CPage2. При переводе приложения в режим редактирования данных мы не сможем простым путём изменить состояние контролов других, «НЕЗАВИСИМЫХ» объектов, например, сделать их ReadOnly(false). Если бы указатель был известен (например, мы сделали extern CPage2* Page2), то при стандартном стиле программирования это тоже не очень помогает по трём обстоятельствам:

1)Код обработчика клика CPage1::m_StartEdit, типа
(CPage2*)GetDlgItem(IDC_xxx)->SetWindowsText(“…”), будет работать. Однако необходимо менять состояние не одного контрола, а, возможно, сотен, и не только на закладке CPage2, но и на других диалогах и тулбарах. Удовольствие написания соответствующих обработчиков, прямо скажем, не очень…

2)Если мы, упрямые, всё-таки изрядно постарались и сделали шаг 1), то, возможно, наделали ошибок по синхронизации отображения и заполнения данными сотен контролов. Ошибки могут проявиться потом, только не известно когда… Мои наблюдения и практика показывают, что В БОЛЬШИХ ПРОЕКТАХ, КОГДА НЕОБХОДИМО СИНХРОНИЗИРОВАТЬ ПОВЕДЕНИЕ БОЛЬШОГО ЧИСЛА ЭЛЕМЕНТОВ УПРАВЛЕНИЯ, КОНТРОЛЫ НЕ ДОЛЖНЫ ИЗМЕНЯТЬ СОСТОЯНИЕ ДРУГИХ КОНТРОЛОВ !!!НАПРЯМУЮ!!!. Т.е. попытка непосредственно в ТЕЛЕ обработчика нажатия кнопки порулить состоянием других контролов НАПРЯМУЮ почти всегда приведёт к краху в будущем по мере разрастания крупного проекта. Это утверждение, разумеется, не касается случаев, когда проекте имеется весьма обособленный код, например, для обработки 3-4 контролов некого модального диалога. Там контролы могут делать друг с другом напрямую всё, что угодно.

3) А масштабирование? Будет Вам счастяяяяя и удовольствия… См. пункты 1 и 2 выше.

Таким образом, необходимо искать способ управления элементами GUI, а вернее сказать, СТИЛЬ программирования, который прозрачно обеспечивает синхронизацию всего, что происходит на экране, особенно после варварских попыток пользователя понажимать на всё сразу;). Перед тем, как поставить точку в мотивации (плюс к тому, что было в предыдущем посте) скажу, что всё, что происходит на экране (в том числе и с контролами), на мой взгляд должно реагировать НЕ на события связанные с нажатиями пользователя на клавиатуру или мышь, а на СОБЫТИЯ приложения, на СОСТОЯНИЯ задачи (чтение данных, состояние редактирования, состояние создания новой порции данных, состояния сохранения данных и т.д.). И не застенчиво зависеть, время от времени, а постоянно (разумеется, речь идёт только о крупных проектах, и, конечно, не обо всех).

Вот дополнения к мотивации:
а) объекты и контролы приложения, должны реагировать, как правило, на события и состояния БИЗНЕС-ЛОГИКИ приложения. Думаю, достаточно определить не слишком большое число состояний enum app_state {ST_STARTUP, ST_CLOSE, ST_READ, и т.д.} myapp_state.
б) сотояние контролов должно зависеть на уровне логики приложения от app_state, а не от действий пользователя, например, нажатия клавиши мыши. Контрол может менять состояние app_state, и таким образом ОПОСРЕДОВАНО своё состояние. Т.е схема такова – (пользователь)->(нажатие на кнопку)->(изменения app_state)->(изменение состояния контрола, enable? readonly? и.д.)
в) передачу информации между объектами приложения о состоянии app_state разумно, на мой взгляд, выполнять в виде передачи сообщений app_message {UM_STARTUP, UM_CLOSE, UM_READ, и т.д.} myapp_message. Каждому значению app_state можно поставить в соответствие значение app_message.

а),б)в) выше – минимум. По мере реализации всего ЭТОГО!!!, возможно, следует ввести новые требования о возможности получения уведомлений о состояниях, асинхронной обработки сообщений и т.д. Т.е., возможно, дальше из ЭТОГО!!! получится некий паттерн типа медиатора вместе с обжект-объзервером (см., например, ссылку которую мне любезно подсказали форумчане http://ooad.asf.ru/patterns/patterninfo.asp?id=23 ). Но решение должно быть простым и прозрачным, как для тех, кто использует MFC, так и WinAPI при построении GUI. Поэтому последнее, обязательное требование г):

г) всё должно быть просто и понятно без лишней теоретизации и онаучивания на пустом месте. Это не должна быть ещё одна библиотека, довесок к MFC, или некий TSupperPuperActionList BC++ Builder. Это скорее должен быть стиль программирования с использованием очень простых по структуре объектов, которые легко можно воспроизвести в любой C++ среде.

Напоследок в «мотивации» покажу, как хотелось бы, чтобы выглядел С++ код, когда всё получится:

// стартуем приложение MFC VC++
BOOL CGkmApp::InitInstance()
{
InitCommonControls();
CWinApp::InitInstance();

// в следующих двух вызовах читаем данные, заполняем контролы
// и приводим их в приличный вид визибл-невизибл, энайб-дисайбл и т.д.
xxx.FillControls(UM_STARTUP);
xxx.DisplayControls(UM_STARTUP);

// запрещаем пользователю делать всякие глупости и портить данные,
// пока он сознательно не переведёт приложение в состояние
// ST_EDIT, нажав, например, на кнопку «Редактировать»
xxx.FillControls(UM_READ);
xxx.DisplayControls(UM_READ);

m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
//-------------------------------------------------------------
// Здесь пользователь нажимает кнопку «Редактировать» для перевода
// формы приложения в режим редактирования ST_EDIT
void CPageGeneral::OnBnClickedEditButton()
{
// здесь, до вызовов двух функций ниже
// все 200 контролов ещё недоступны для редактирования
xxx.FillControls(UM_EDIT);
xxx.DisplayControls(UM_EDIT);
// здесь все 200 контролов уже доступны для редактирования
// и пользователь может вводить данные
}
//-------------------------------------------------------------
// Здесь пользователь нажимает кнопку «Сохранить» для перевода
// приложения в режим сохранения результатов редактирования ST_SAVE,
// а затем возврата в состояние просмотра данных ST_READ
void CPageGeneral::OnBnClickedSaveButton()
{
// здесь все 200 контролов доступны для редактирования
// т.к. состояние ещё ВОЗМОЖНО ST_EDIT
xxx.FillControls(UM_SAVE);
xxx.DisplayControls(UM_SAVE);

xxx.FillControls(UM_READ);
xxx.DisplayControls(UM_READ);
// здесь все 200 контролов изменили своё состояние,
// и уже недоступны для редактирования
// Мы в состоянии ST_READ просмотра базы данных
}
//-------------------------------------------------------------

*** МАСЛО В ОГОНЬ ***

Думаю, масло следует подливать понемногу, шаг за шагом. В предыдущем посте предложено тупое решение, когда указатели на все требуемые объекты, например, диалоги Page1,…, Page20 объявлены extern. На мой взгляд, это не такой уж плохой путь для простых по логике GUI. Так, что в простых случаях я так и буду делать, и других наворотов (см. далее шаг 1 и т.д.) мне и не надо.

Шаг 1.
Для более сложных приложений, если мы хотим «большой беспорядок привести к нескольким маленьким беспорядкам», заметим, что двадцать глобальных указателей – прямо скажем, не очень изящно. Ну, добавим все указатели в некий массив. Необходимость в таком массиве, вернее специальном классе CMessanger (язык не поворачивается сказать элементе паттерна) дополнительно обоснована далее, см. шаг 2. В функциях FillControls, DisplayControls нам нужно будет уметь извлекать из массива указатели на диалоги по некоторым символическим идентификаторам, которые мы знаем (сами задаём при добавлении указателя на объект в массив). Поэтому в элементах массива будем хранить указатели вместе с идентификаторами.

///////////////////////////////////////////////////
//file “utilwnd.h”
#include <afxtempl.h>

class CMessanger {
public:
class CWndInfo { // CWnd object info
public:
CWnd* m_pWnd;// pointer to CWnd registered object
UINT ID; // user symb.const. for CWnd registered object public:
CWndInfo() : m_pWnd(NULL), ID(0) {};
CWndInfo(CWnd* pWnd, UINT ID_WND = 0){
m_pWnd = pWnd; ID = ID_WND;
}
~CWndInfo() {};
};
public:
CMessanger() {};

// register CWnd object in CWndInfo array. Save pointer to CWnd object
// and user defined symbolic constant ID_WND for this CWnd object
void Register(CWnd* pWnd, UINT ID_WND)
{
m_aWnd.Add(new CWndInfo(pWnd, ID_WND));
}
// get CWnd* pointer from CWndInfo array by user defined constant ID_WND
CWnd* GetWnd(UINT ID_WND)
{
for (int i = 0; i < m_aWnd.GetSize(); i++)
if ( ID_WND == m_aWnd.GetAt(i)->ID) return m_aWnd.GetAt(i)->m_pWnd;
return NULL;
}
private:
CArray <CWndInfo*, CWndInfo*> m_aWnd;
};
extern CMessanger theMsg;

///////////////////////////////////////////////////
//file “utilwnd.cpp”
#include "StdAfx.h"
#include "utilwnd.h"

CMessanger theMsg;


Добавлять элементы массива theMsg будем при создании или инициализации объектов CWnd при помощи метода Register, например, вот так:

void CTVView::OnInitialUpdate()
{
CTreeView::OnInitialUpdate();

theMsg.Register(this,IDW_TVVIEW);
}

Определение идентификатора IDW_TVVIEW поместим в некий “usermessages.h”, как впрочем и макроопределения для символических констант других объектов (диалогов, видов и т.д)

///////////////////////////////////////////////////
//file “usermessages.h”

//#define IDW_ALL 0 in utilwnd.h

#define TV_VIEW (IDW_ALL + 1000) //for left CTVView: public CTreeView
#define MAIN_VIEW (IDW_ALL + 2000) //for right CMainView: public CView



#define IDW_TVVIEW TV_VIEW // Left TreeView
#define IDW_MAIN_PANEL (MAIN_VIEW + 1)// CMainPanel dialog in CMainView
// Page1 dialog in CMainPanel::m_TabCtrl
#define IDW_PAGE_GENERAL(MAIN_VIEW + 2)
// Page2 dialog in CMainPanel::m_TabCtrl
#define IDW_PAGE2 (MAIN_VIEW + 3)
// Page3 dialog in CMainPanel::m_TabCtrl
#define IDW_PAGE3 (MAIN_VIEW + 4)

Для обработки сообщений потребуется определить эти сообщения и определить глобальные функции обработчиков FillControls, DisplayControls. Сделаем это, дополнив “usermessages.h” (*.cpp)


///////////////////////////////////////////////////
//file “usermessages.h”


#include "utilwnd.h"

#define UM_STARTUP (WM_USER + 100)
#define UM_ERASE (WM_USER + 110)
#define UM_READ (WM_USER + 120)
#define UM_EDIT (WM_USER + 130)
bool FillControls (UINT msg);
bool DisplayControls (UINT msg);


///////////////////////////////////////////////////
//file “ usermessages.cpp”
#include "StdAfx.h"
#include "Gkm.h" // our main header for the application
#include "TVView.h" // treeview
#include "Pages.h" // dialogs

#include "usermessages.h"

CMessanger theMsg;

#define TV ((CTVView*)(theMsg.GetWnd(IDW_TVVIEW)))
#define MAIN_PANEL ((CMainPanel*)(theMsg.GetWnd(IDW_MAIN_PANEL)))
#define PAGE_GENERAL ((CPageGeneral*)(theMsg.GetWnd(IDW_PAGE_GENERAL)))
#define PAGE2 ((CPage2*)(theMsg.GetWnd(IDW_PAGE2)))
#define PAGE3 ((CPage3*)(theMsg.GetWnd(IDW_PAGE3)))

//------------------------------------------------------------
void FillControls(UINT msg)
{
switch(msg)
{
case UM_STARTUP:
FillControls(UM_ERASE);
FillControls(UM_READ);

break;
case UM_ERASE:
TV->ClearTree();
MAINPANEL->ClearControls();
PAGE_GENERAL->ClearControls();
PAGE2->ClearControls();


break;
case UM_READ:
TV->ReadData();
MAINPANEL->ReadData();
PAGE_GENERAL-> ReadData();
PAGE2->ReadData();
PAGE3->ReadData();

break;



}
return true;
}
//------------------------------------------------------------
void DisplayControls(UINT msg)
{
// Совершенно аналогично FillControls
}

Ну, вот и всё для шага 1. Теперь в первом приближении можно делать то, что я хотел в начале.

void CPageGeneral::OnBnClickedEditButton()
{
FillControls(UM_EDIT);
DisplayControls(UM_EDIT);
}

Шаг 2.
На шаге 1 причина добавления указателей в массив была чисто косметической. Дескать, так стильнее по сравнению отдельными глобальными указателями на диалоги и другие CWnd объекты. Действительно, пока что шаг 1 не добавляет ничего нового, кроме лишних хлопот.
Однако часто объекты проекта по ЛОГИКЕ ПРИЛОЖЕНИЯ представляют независимые ГРУППЫ, сообщения которых должны обрабатывать различные обработчики FillControls, DisplayControls. Например, диалоги и виды, описанные выше – это одна группа объектов. Если по нажатию некой кнопки создаётся модальный диалог, насыщенный другими боксами, барами и диалогами, то совокупность последних, очевидно представляет другую независимую группу. Под независимостью здесь понимается то обстоятельство, что объекты различных групп не пересекаются в части обработки сообщений. Таким образом, различные группы должны иметь различные обработчики FillControls, DisplayControls. Возвращаясь к BC++ Builder, – там это реализуется «автоматически», т.к. указанные функции являются членами классов различных форм (там обработчики я пишу для каждой навороченной по числу контролов формы). В MFC - копировать этот подход от BC бессмысленно.
Если мы не эстеты, то, конечно, можно для каждой из групп определить свою пару глобальных функций {FillControls_1, DisplayControls_1}, {FillControls_2, DisplayControls_2}, …, {FillControls_n, DisplayControls_n}. Работать будет… Однако гораздо удобнее в класс CMessanger добавить соответствующие виртуальные методы. Тогда CWnd-объекты каждой из групп будем регистрировать в своём глобальном месанджере. Более серьёзная причина использования для различных групп объектов различных CMessanger_XXX : public CMessanger приведена на шаге 3. Ну, а здесь, думаю, будут понятны изменения в определении CMessanger и в коде регистрации объектов разных групп и так.

class CMessanger {
public:

// ****** main message handling ******
virtual bool FillControls (UINT message, UINT ID_WND = IDW_ALL) {return true;};
virtual bool DisplayControls(UINT message,UINT ID_WND = IDW_ALL) {return true;};

};

Тогда для CWnd-объектов первой группы наследуем свой манагер сообщений:
class CMsg1: public CMessanger {
public:
bool FillControls(UINT message, UINT ID_WND = 0);
bool DisplayControls(UINT message, UINT ID_WND = 0);
};
extern CMsg1 Msg1;
//---------------------------------------------

И объекты первой группы регистрируем в Msg1:
Register(&Msg1, this, IDW_PAGE_GENERAL);
//---------------------------------------------

Для объектов второй группы наследуем свой манагер сообщений:
class CMsg2: public CMessanger {
public:
bool FillControls(UINT message, UINT ID_WND = 0);
bool DisplayControls(UINT message, UINT ID_WND = 0);
};
extern CMsg2 Msg2;
//---------------------------------------------
….
И объекты второй группы регистрируем в Msg2:
Register(&Msg2, this, IDW_XXXXXXX);
//---------------------------------------------

Шаг 3.
Завтра.


На сегодня для меня хватит. Пальцы уже болят. Остальное - (шаг 3, …, шаг N) завтра. Вначале хочется услышать Ваше мнение.

PS.
Ещё раз благодарю всех тех, кто откликнулся. Обещаю, что дальше не буду писать столь пространно и столь длинные посты. Это было необходимо в настоящем постинге, т.к. далее я буду только ссылаться на предыдущие свои постинги. Если, конечно, дискуссия состоится.
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32214537
Фотография JibSkeart
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
может я что то и не понял ,
но нелзя ли просто
посылать какое то свое сообщение ,тем же самым бональным PostMessage or
SendMessage ???

сразу скажу что до конца я не читал ...
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32214641
maratka
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
>> imho, (сначала нехотел ничего писать)
> Почему? :)
слишком большой пост получается - отвлекает от работы...
но там видно будет, мож действительно нужная весчь..
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32215539
void_123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Маратка!
Ну, мож хоть ты шо сказашь? Покритикуешь?
Если все будут молчать, то про следующие 100 "шагов" так ничего и не узнаете. Не буду писать.

*** ШАГ 3 ***

На предыдущем шаге 2 для различных групп объектов CWnd (диалогов, видов) предложено использовать различные экземпляры классов-потомков CMessanger c переопределёнными виртуальными методами FillControls,DisplayControls. Причиной было названо удобство организации передачи сообщений в пределах КАЖДОЙ ИЗ ГРУПП. Однако всё это опять только «косметика»…

Но есть причины разделения объектов на группы с различными месанджерами и функционального характера. В ряде случаев необходимо уметь посылать широковещательные сообщения всем членам группы. До сих пор сообщения, например, UM_ERASE обрабатывались только в теле FillControls,DisplayControls в том порядке, в котором мы определили:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
	switch(msg)
	{
	case UM_ERASE:
		TV->ClearTree();
		MAINPANEL->ClearControls();
		PAGE_GENERAL->ClearControls();
		PAGE2->ClearControls();
	…
	}

Если порядок обработки указанного сообщения для нас не важен (что вначале – TV, или MAINPANEL? см. врезку выше), то можно просто широковещательно рассылать сообщение UM_ERASE, например, при помощи функции SendMessage API, или одноимённого метода CWnd*. Сделаем это, определив для CMessanger функцию-член CMessanger:: SendMessage:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
///////////////////////////////////////////////////
//
//   file “utilwnd.h”
//
///////////////////////////////////////////////////

#include <afxtempl.h>

class CMessanger {
public:

	class CWndInfo {	// CWnd object info
	public:
		CWnd*		m_pWnd;// pointer to CWnd registered object 
		UINT		ID;	 // user symb.const. for CWnd registered object	public:
		CWndInfo() : m_pWnd(NULL), ID( 0 ) {};
		CWndInfo(CWnd* pWnd, UINT ID_WND =  0 ){
			m_pWnd = pWnd; ID = ID_WND;
		}
		~CWndInfo() {};
	};

public:

	CMessanger() {};

	// register CWnd object in CWndInfo array. Save pointer to CWnd object
	// and user defined symbolic constant ID_WND for this CWnd object
	void	Register(CWnd* pWnd, UINT ID_WND)
	{
		m_aWnd.Add(new CWndInfo(pWnd, ID_WND)); 
	}

	// get CWnd* pointer from CWndInfo array by user defined constant ID_WND
	CWnd*	GetWnd(UINT ID_WND)
	{
	for (int i =  0 ; i < m_aWnd.GetSize(); i++)
	if ( ID_WND == m_aWnd.GetAt(i)->ID)	return m_aWnd.GetAt(i)->m_pWnd;
	return NULL;
	}

	// ******  main message handling ******
	virtual bool FillControls	(UINT message,UINT ID_WND = IDW_ALL) {return true;};
	virtual bool DisplayControls(UINT message,UINT ID_WND = IDW_ALL) {return true;};

	// ******  additional message handling ******
	// broadcast (ID_WND == IDW_ALL) or direct message handling  
	void SendMessage(UINT message , UINT ID_WND = IDW_ALL,
       WPARAM wParam =  0 , LPARAM lParam =  0 );

private:
	CArray <CWndInfo*, CWndInfo*> m_aWnd;
};

///////////////////////////////////////////////////
//
//  file “utilwnd.cpp”
//
///////////////////////////////////////////////////
#include  "StdAfx.h" 
#include  "utilwnd.h" 

// broadcast (ID_WND == IDW_ALL) or direct message handling  
void CMessanger::SendMessage(UINT message , UINT ID_WND, WPARAM wParam, LPARAM lParam)
{
	for (int i =  0 ; i < m_aWnd.GetSize(); i++) {								CWnd* pWnd = m_aWnd.GetAt(i)->m_pWnd;
		if ( ID_WND == IDW_ALL || ID_WND == m_aWnd.GetAt(i)->ID)
			pWnd->SendMessage(message, wParam, lParam);
	}
}
//-------------------------------------------------------------



Для того, чтобы СWnd объект реагировал на событие UM_XXX, которое послано всем объектам группы (ID_WND == IDW_ALL), или только ему, необходимо для его класса определить соответствующий обработчик:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
//////////////////////////////////////////////////////////////////////////
//
// CPageGeneral dialog declaration
//
/////////////////////////////////////////////////////////////////////////

class CPageGeneral : public Cdialog {
…
protected:
	virtual void DoDataExchange(CDataExchange* pDX);   // DDX/DDV support
	DECLARE_MESSAGE_MAP()

public:
…
	afx_msg LRESULT OnSendMessage(WPARAM wParam, LPARAM lParam)
	{
		// Do something
		return  0 ;
	}
	
	bool StartUp( void ); 
	bool ClearControls( void ); 
	bool Read ( void ); 
	…
	…
};


//////////////////////////////////////////////////////////////////////////
//	
// CPageGeneral dialog implementation
//
/////////////////////////////////////////////////////////////////////////
…
BEGIN_MESSAGE_MAP(CPageGeneral, CDialog)
	…
	ON_MESSAGE(UM_NOTIFY, OnSendMessage)
END_MESSAGE_MAP()
…


Понятно, что при помощи анализа комбинации параметров wParam, lParam обработчика сообщения UM_NOTIFY можно легко разделить конкретные сообщения UM_STARTUP, UM_ERASE и т.д., и вызвать соответствующие обработчики-члены класса объекта, который получил сообщения. Например,

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
	afx_msg LRESULT OnSendMessage(WPARAM wParam, LPARAM lParam)
	{
		if (wParam==UN_STARTUP)
			return StartUp(); // см. выше определение класса
		else if (wParam==UN_ERASE)
			return ClearControls();// см. выше определение класса
		…
		…
		return  0 ;
	}


Как это можно использовать? Например, так:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
void CPageGeneral::OnBnClickedEditButton()
{
// «одним махом» переводим все контролы в состояние ST_EDIT
// широковещательной рассылкой без использования 
// функций методов FillControls, DisplayControls класса
// CMessanger
xxx.SendMessage(UM_NOTIFY, IDW_ALL, UM_EDIT,  0 );
}


**** PS к ШАГ 3 ****

1) Можно посылать широковещательные сообщения всем объектам сразу, без использования FillControls, DisplayControls, если последовательность обработки сообщений для всех объектов НЕ ВАЖНА. Не стоит быть наивным, полагая, что это всегда так и есть. Моя практика программирования ЭТОГО!!! показывает, что в СЛОЖНЫХ проектах не редки случаи, когда последовательность ВАЖНА. Не буду спорить о том, что зависимость от порядка обработки сообщений может быть вызвана только моим неумением программирования. По крайней мере, я об этом знаю и учитываю. Выбираю принцип такой – если, для ряда сообщений последовательность важна, то обработку выполняю, контролируя последовательность вызовов в FillControls, DisplayControls; если – нет, то обрабатываем широковещательно. Естественно, что одно не исключает другого, и можно комбинировать способы рассылки сообщений.

2) Если необходимо послать сообщение конкретному объекту, а не широковещательно всем, то в параметрах SendMessage заменяем макро IDW_ALL макроопределением IDW_XXX идентификатора требуемого объекта CWnd.

3) Может сложиться впечатление, что рассылка сообщений при помощи SendMessage приближает стиль программирования к парадигмам ООП, в отличие от использования FillControls, DisplayControls. Это было бы ложным утверждением. И SendMessage, и FillControls/DisplayControls в примерах выше вызывают ОДНИ И ТЕЖЕ МЕТОДЫ объектов CWnd (OnStartUp(), ClearControls(), см. определение диалога выше).

4) Я бы не противопоставлял возможность обработки сообщений при помощи FillControls/DisplayControls «альтернативному» методу доставки и анализа сообщений при помощи SendMessage. Я бы комбинировал одно и другое. У FillControls/DisplayControls есть одно преимущество – они позволяют прозрачно контролировать ОБЩУЮ общую логику событий приложения.

5) Забегая вперёд - о всё большей и большей схожести всего ЭТОГО!!! с медиатором и прочими музыкальными принадлежностями предлагаю высказаться другим. Мож просто медиатор взять, а остальное забыть?

*******************************************************
ШАГ 4. Бродкасты
*******************************************************

На шаге 3 было показано, что в некоторых случаях, когда НЕ ВАЖНА последовательность обработки однотипных сообщений, весьма эффективны бодкасты при помощи механизма передачи сообщений API или соответствующих методов MFC, инкапсулирующих соответствующие вызовы API.

Однако для выполнения реальной работы с CWnd, например, с диалогом, который уловил UM_NOTIFY, в его обработчике UM_NOTIFY всё равно придётся анализировать WPARAM wParam, LPARAM lParam при помощи switch, или if. Согласитесь, немного всё продолжает оставаться корявым. И даже ещё в большей степени. То мы в FillControls/DisplayControls забубенили один свич, - то (что ещё хуже) до__бубенили ещё один свич в MFC-обработчик ON_MESSAGE(UM_NOTIFY, xxx). Да, всё плохо…

Тогда, для того, чтобы ввести в заблуждение доцента, преподающего ООП, и получить хоть тройку, скажем, что, дескать, наша модель ещё немножко и будет «почти ОО»; скажем, что, дескать, мы-то делаем конкретный проект, и у нас конкретные состояния приложения - enum app_state {ST_STARTUP, ST_CLOSE, ST_READ, и т.д.} myapp_state. Тогда решение на поверхности – для каждого из этих состояний приложения делаем свой бродкаст-обработчик EnableControls, ClearControls, ReadControls и др.:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
///////////////////////////////////////////////////
//
//  file “utilwnd.cpp”
//
///////////////////////////////////////////////////
class CMessanger {
…
…
public:
	// ******  main message handling ******
	virtual bool FillControls	(UINT message,UINT ID_WND = IDW_ALL) {return true;};
	virtual bool DisplayControls(UINT message,UINT ID_WND = IDW_ALL) {return true;};

	// ******  additional message handling ******
	// broadcast (ID_WND == IDW_ALL) or direct message handling  
	void SendMessage(UINT message , UINT ID_WND = IDW_ALL,
       WPARAM wParam =  0 , LPARAM lParam =  0 );

	// *** special message handling ***
	// ---------------------------------------
 
	void EnableControls(bool status, UINT ID_WND = IDW_ALL)
{
		SendMessage(UM_ENABLE_CONTROLS, ID_WND, status);
}
	// ---------------------------------------
 
	void ClearControls(UINT ID_WND = IDW_ALL)
{
		SendMessage(UM_CLEAR_CONTROLS, ID_WND);
}
	// ---------------------------------------
 
	void ReadControls(UINT ID_WND = IDW_ALL)
{
		SendMessage(UM_READ, ID_WND);
}
	// ---------------------------------------
 
};


Для того, что бы наш гипотетический диалог реагировал на эти бродкасты, редактируем месадж-мап его класса и определяем соответствующие afx_msg функции-члены:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
//////////////////////////////////////////////////////////////////////////
//
// CPageGeneral dialog declaration
//
/////////////////////////////////////////////////////////////////////////

class CPageGeneral : public Cdialog {
…
protected:
	virtual void DoDataExchange(CDataExchange* pDX);   // DDX/DDV support
	DECLARE_MESSAGE_MAP()

public:
…
	afx_msg LRESULT OnSendMessage(WPARAM wParam, LPARAM lParam);
	afx_msg LRESULT OnEnableControlMessage(WPARAM wParam, LPARAM lParam);
	afx_msg LRESULT OnClearControlMessage(WPARAM wParam, LPARAM lParam);
	afx_msg LRESULT OnReadControlMessage(WPARAM wParam, LPARAM lParam);
…
	bool StartUp( void ); 
	bool ClearControls( void ); 
	bool Read ( void ); 
	…
	…
};


//////////////////////////////////////////////////////////////////////////
//	
// CPageGeneral dialog implementation
//
/////////////////////////////////////////////////////////////////////////
…
BEGIN_MESSAGE_MAP(CPageGeneral, CDialog)
	…
	ON_MESSAGE(UM_NOTIFY, OnSendMessage)
	ON_MESSAGE(UM_ ENABLE_CONTROLS, OnEnableControlMessage)
	ON_MESSAGE(UM_CLEAR_CONTROLS, OnClearControlMessage)
	ON_MESSAGE(UM_READ, OnReadControlMessage)
END_MESSAGE_MAP()
…



Как это можно использовать? Например, так:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
void CPageGeneral::OnBnClickedButton1()
{
// очистить все контролы в группе theMsgGroup1
theMsgGroup1.ClearControls();

// очистить контролы ОДНОГО диалога  IDW_XXXXX
// в группе theMsgGroup2
theMsgGroup2.ClearControls(IDW_XXXXX);
}


*** ШАГ 5 ***

Завтра. Пост и так здоровый получился. Пишите.
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32215808
YuriAM
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
помоему Вы хотите применить здесь несхожее понятие.
У MFC достаточно своих . Может сами попробуете раскапать ?
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32216374
void_123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
to YuriAm

Спасибо за ответ. Если честно, не совсем понял, кроме MFC, о чём Вы?

>>помоему Вы хотите применить здесь несхожее понятие.
Что вы имеете в виду?

>>У MFC достаточно своих .

Пока в МФЦ ничего не нашёл, но буду копать. Хотя, на мой взгляд, надежд маловато, т.к. МФЦ - это только фаундейшен классес. Там про обработку событий "бизнес-логики" ничего просто не может быть, имхо.

С дугой стороны, МФЦ - только одна из либов. Завтра мелкософт предложит другую архитектуру и либу (вернее, уже предложила windows forms для С++ в последнем релизе дотНЕТ 2003, кажется, апрельском).
Хотя примеры я приводил для МФЦ, хотелось выйти на обсуждение проблемы из "границ" обсуждения ПРИМЕРОВ, якобы, для МФЦ в моих постингах.


>> Может сами попробуете раскапать ?

Быду пытаться. На форумах мне, балбесу часто удаётся найти очень хорошие идеи, когда масло в голове заканчивается.

PS. Однако, Вы правы в неком "кровосмешении" с моих постингах (если только я правильно понял Ваш пост).
В конце, когда мы доедем до новомодных паттернов и прочих пугающих онаученных понятий на ровном месте, а попытаюсь показать, что многие ООП решения от лукавого, когда доходит до их практического применения.
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32216412
Mik Prokoshin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Стандартным для работы с БД в MFC является стиль Doc/View. Данные хранятся в ДОКУМЕНТЕ, а не в представлении. Так что просто надо определиться, где у Вас документ, а где представления. Каждая закладка - это отдельный view ? (разные представления одного документа). Или закладка соответствует отдельному документу ?
1) Каждая закладка - отдельный документ. Можно использовать отдельный документ - список документов и он уже должен по своим поддокументам рассылать сообщения Update о перечитывании информации (то, что у Вас - FillControls), далее документы отдают сообщения UpdateAllViews (у Вас - DisplayControls)
2) закладки - просто FormViews одного документа. Вы c любой страницы делаете GetDocument()->UpdateAllViews.
3) весь TabControl у Вас - это один View одного документа. Можно сделать Update метод для TabCtrl, в свою очередь он будет вызывать методы Update для своих контролов страниц (посылать им сообщения) и т.д.
Сами же контролы (или их родительская форма) должны определять по состоянию своего документа - как им отображаться - пустыми, редактируемыми, или как-то по-своему...
Загонять все контролы в один громадный список - фактически организовывать один документ с массой полей. Подумайте, рационально ли это.
Я изобразил подход Doc/View, как я его понимаю и как использовал в аналогичных приложениях. Постарался отделить данные и бизнес-логику от представления на экране полей. И попытайтесь показать, что это - "от лукавого" :-) А Вы пытаетесь все данные просто загнать в контролы и управлять ими всеми и "сверху" и "горизонтально".
Ну, а как сказал maratka - с точки зрения экономии ресурсов при единственном View - можно, конечно, хранить данные документа и в самих контролах, учитывая усложнение доступа к ним из документа (усложнение массовой обработки данных для бизнес-логики - там, где в документе был бы массив переменных (Items), будут отдельные члены-контролы в отдельных классах и т.д.)
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32218933
void_123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
*** ШАГ 5. Получился Медиатор. Слон родил мыша. ***

На шаге 4 мы придумали не такой уж плохой механизм рассылки и обработки широковещательных сообщений. Я говорю мы, поскольку, на форумах кивали в сторону возможности использования SendMessage для обработки сообщений. При этом вызов методов CMessanger, например, CMessanger::EnableControls, CMessanger::ClearControls удобнее, чем рассылка соответствующих сообщений при помощи «универсального» метода CMessanger::SendMessage. В последнем случае необходимо по условному переходу ещё передать управление конкретному обработчику в зависимости WPARAM wParam, LPARAM lParam.
Однако здесь привлекательна, лишь форма (использование theMsg.EnableControls вместо theMsg.SendMessage(UM_NOTIFY, IDW_ALL, UM_ERASE, 0)). По существу CMessanger::EnableControls всё равно используется метод CMessanger::SendMessage, а тот в свою очередь использует метод рассылки сообщений MFC CWnd-объектам или API, см. врезку кода выше. Да, погнавшись за внешней стороной дела – замоделировались. Как справедливо, заметил один из участников форума, так мы «едем из Москвы в Подмосковье через Колыму». Надо сказать, что это участник форума весьма вежливый человек, и выразился мягко, поскольку едем не только через Колыму, но … как мудрёно! А сколько «технического» кода придётся написать? То, что надо написать строчки кода для каждого из методов ИНТЕРФЕЙСА наших команд в CMessanger (EnableControls, ClearControls и т.д) – это понятно, как бы мы не делали. Неизбежные накладные расходы, если необходим броадкастинг. Но то, что для каждого CWnd-класса придётся ещё и дополнительные макросы писать – последняя капля. Макросы месадж-мапа простые, но их придётся писать для каждого класса CWnd-объектов, которые участвуют в широковещательной рассылке. Ну и сами методы CWnd-объектов. Где уж там до useful programming style... Всё в топку!

Для того, чтобы отказаться от MS-SendMessage, надо сделать так, чтобы объекты, участвующие в оповещениях о событиях приложения, имели соответствующие методы, которые они наследовали бы от некого объекта CMsgItem, уже имеющего соответствующий командный «интерфейс». Для вызова виртуальных методов этого «интерфейса» CMessаnger должен знать адрес vtb CMsgItem, а не указатель на сам CWnd-объект. Расширим определение CWndInfo, добавив CMsgItem* m_pMsgItem, и немного упростим мембер-функции CMessаnger:


///////////////////////////////////////////////////
//
// file “utilwnd.h”
//
///////////////////////////////////////////////////

#include <afxtempl.h>

#define IDW_ALL 0 // send message to all CWnd objects

class CMsgItem; // forward declaration

////////////////////////////////////////////////////
// CMessanger class

class CMessanger {
public:
class CWndInfo { // object info class
public:
void* m_pWnd;// pointer to the object registered
UINT ID; // user symb.const. for the object registered
CMsgItem* m_pMsgItem;
public:
CWndInfo() : m_pWnd(NULL), m_pMsgItem(NULL), ID(0) {};
CWndInfo(void* pWnd, CMsgItem* pMsgItem = NULL, UINT ID_WND = 0)
{
m_pWnd = pWnd; m_pMsgItem = pMsgItem; ID = ID_WND;
}
~CWndInfo() {};
};
public:
CMessanger() : status(0) {};

// register object in CWndInfo array. Save pointer to the object,
// command inteface and user defined symbolic constant
// ID_WND for this object
void Register(void* pWnd, CMsgItem* pMsgItem, UINT ID_WND)
{
m_aWnd.Add(new CWndInfo(pWnd, pMsgItem, ID_WND));
}

// get CWnd* pointer from CWndInfo array by user defined constant ID_WND
CWnd* GetWnd(UINT ID_WND)
{
for (int i = 0; i < m_aWnd.GetSize(); i++)
if ( ID_WND == m_aWnd.GetAt(i)->ID)
return (CWnd*) (m_aWnd.GetAt(i)->m_pWnd);
return NULL;
}
// complete status of message
UINT GetStatus(void) { return status; }
void SetStatus(UINT _status) { status = _status; }

// ****** main message handling ******
virtual bool FillControls (UINT message, UINT ID_WND = IDW_ALL) {return true;};
virtual bool DisplayControls(UINT message,UINT ID_WND = IDW_ALL) {return true;};

// ****** additional message handling ******
// broadcast (ID_WND == IDW_ALL) or direct message handling
void SendMessage(UINT message, UINT ID_WND = IDW_ALL,
WPARAM wParam = 0,LPARAM lParam = 0);

// special message handling
void EnableControls(bool Еnable, UINT ID_WND = IDW_ALL);
void ClearControls(UINT ID_WND = IDW_ALL);

private:
CArray <CWndInfo*, CWndInfo*> m_aWnd;
UINT status;
};

extern CMessanger theMsg;


////////////////////////////////////////////////////
// CMsgItem class

class CMsgItem {
public:
void Register(CMessanger* pMsg, void* pWnd, UINT ID_WND)
{
pMsg->Register( pWnd , this, ID_WND);
};
// ****** additional message handling ******
virtual void SendMessage(UINT message, WPARAM wParamMsg=0, LPARAM lParam=0){}

// special message handling
virtual void EnableControls(bool Enable){}
virtual void ClearControls (void) {}
};
//-----------------------------------------------------



///////////////////////////////////////////////////
//
// file “utilwnd.cpp”
//
///////////////////////////////////////////////////
#include "StdAfx.h"
#include "utilwnd.h"

CMessanger theMsg;

// general broadcast (ID_WND == IDW_ALL) or direct message handling
void CMessanger::SendMessage(UINT message , UINT ID_WND, WPARAM wParam, LPARAM lParam)
{
for (int i = 0; i < m_aWnd.GetSize(); i++)
if ( ID_WND == IDW_ALL || ID_WND == m_aWnd.GetAt(i)->ID )
m_aWnd.GetAt(i)->m_pMsgItem->SendMessage(message, wParam, lParam);
}
//-------------------------------------------------------------
// special message handling
void CMessanger::EnableControls(bool Enable, UINT ID_WND)
{
for (int i = 0; i < m_aWnd.GetSize(); i++)
if ( ID_WND == IDW_ALL || ID_WND == m_aWnd.GetAt(i)->ID )
m_aWnd.GetAt(i)->m_pMsgItem->EnableControls( Enable );
}
//-------------------------------------------------------------
void CMessanger::ClearControls( UINT ID_WND )
{
for (int i = 0; i < m_aWnd.GetSize(); i++)
if ( ID_WND == IDW_ALL || ID_WND == m_aWnd.GetAt(i)->ID )
m_aWnd.GetAt(i)->m_pMsgItem->ClearControls();
}
//-------------------------------------------------------------


Как видно, класс CMsgItem представляет некий почти «абстрактный» командный интерфейс, и состоит только из виртуальных методов и дополнительной функции Register (одноимённая функция есть у CMessanger), которая введена косметически, для удобства регистрации. Здесь перечисляются все команды, которые необходимо выполнять согласно введённому раннее enum app_state. Выше для примера приведены только два метода. Дополнительно оставлен метод SendMessage, который теперь не имеет ничего общего с MFC или API(одноименную функцию обрабатывает CMessanger исключительно сам). Это сделано на «всякий пожарный», если во всех классах CWnd-объектов, наследующих, от CMsgItem, приемлемо ограничиться обработкой сообщений-команд только в SendMessage, например, в свиче, вместо переопределения большого числа мембер-функции EnableControls, ReadControls, и т.д в классе, derived от CMsgItem . Если кому-то нравится это, то последние можно удалить из определения CMsgItem и CMessanger. Кстати, может стоит это сделать? Последнее, что занудно уточню, и что и так всем понятно - методы CMsgItem никогда не вызываются напрямую при передаче сообщений. Это делает CMessanger (выступая от имени того, кто вызвал его соответствующий метод), вызывая переопределённый метод derived CWnd-класса(кого здесь стошнило – извините).

В классе CMessanger осталось всё без изменений, за исключение тех, что связаны с появлением на свет CMsgItem, и GetStatus(void), SetStatus(UINT _status). Об этих двух методах, как и о набивших всем оскомину функциях FillControls/DisplayControls – чуть ниже в резюме. Кроме того, в CWndInfo указатель на оконный объект, который пока И НЕ НУЖЕН, переделан на void*. Если потребуется этот CWnd-объект (а так, в принципе любого класса), то для использования его методов в теле FillControls/DisplayControls можно вызвать немного переделанную функцию GetWnd.

Как этим всем пользоваться? Например, если диалог CMainDialog будем наследовать и от CMsgItem, то так:

///////////////////////////////////////////////////////////////////
//
// CMainDialog dialog declaration
//
///////////////////////////////////////////////////////////////////

class CMainDialog : public CDialog, CMsgItem
{
// CDialog, CMainDialog declarations


public:

//////////////////////////////////////////////////
// CMsgItem interface general method
void SendMessage(UINT message, WPARAM wParamMsg, LPARAM lParam)
{
switch (message)
{
case UM_READ:
// Do something
break;
case UM_ERASE:
// Do something
break;
};
}

// CMsgItem interface special methods
void EnableControls(bool Enable)
{
// Do something
}
void ClearControls (void)
{
// Do something
}

};


///////////////////////////////////////////////////////////////////////
//
// CMainDialog dialog implementation
//
///////////////////////////////////////////////////////////////////////

//--------------------------------------------------------------------
BOOL CMainDialog::OnInitDialog()
{
CDialog::OnInitDialog();

// Inherited CMsgItem metod
Register(&theMsg, this, IDW_MAIN_DIALOG);

return TRUE;
}
//--------------------------------------------------------------------
void CMainDialog::OnBnClickedEditButton()
{
// «одним махом», широковещательной рассылкой очищаем все контролы
// тех объектов, о которых известно theMsg
theMsg.ClearControls();
}
//--------------------------------------------------------------------

В приведённом выше коде не хватает некого метода CMessanger – UnRegister. Не то, что бы я его не показал на форуме. Я просто про него забыл!!! из-за своего балбесянства. Последствия, которые могут быть, ясны – крах. Про него мне подсказал пост одного из участников форумов. Ниже, без персоналий привожу ЕГО фрагмент кода, где такой метод void CMediator::UnregisterRecipient(CCommandInterface *Rec) присутствует. Для тех, кто заинтересовался, и кто ещё не смотрел подобную проблематику, ооооочень рекомендую посмотреть код ЭТОГО человека. Прошу модераторов простить за перепост. Там же комментарии. Код чище, стильнее, чем у меня. Так, что некоторые вещи своего «рабочего» класса CMessanger я наглым образом перепишу, как У НЕГО:

//--------------------------------------------------------------------
//H-файл

class CCommandInterface // у меня это CMsgItem
{
public:
virtual void Execute(CCommand *cmd) = 0;
};

class CMediator // у меня это CMessanger
{
public:
static CMediator* Instance();

void RegisterRecipient(CCommandInterface *newRec);
void UnregisterRecipient(CCommandInterface *Rec);
void SendMessage(CCommand *cmd);
void SendMessage(CCommandInterface *whom, CCommand *cmd);

protected:
CMediator();
private:
static CMediator* m_instance;
CArray<CCommandInterface*, CCommandInterface*> m_recipients;
};

//--------------------------------------------------------------------
//CPP-файл

CMediator* CMediator::m_instance = NULL;

CMediator::CMediator()
{
}

CMediator* CMediator::Instance()
{
if (m_instance == NULL)
{
m_instance = new CMediator;
ASSERT(m_instance);
}
return m_instance;
}

void CMediator::RegisterRecipient(CCommandInterface *newRec)
{
for (int i = 0; i < m_recipients.GetSize(); i++)
{
if (newRec == m_recipients )
{
return;
}
}
m_recipients.Add(newRec);
}

void CMediator::UnregisterRecipient(CCommandInterface *Rec)
{
for (int i = 0; i < m_recipients.GetSize(); i++)
{
if (Rec == m_recipients)
{
m_recipients.RemoveAt(i);
return;
}
}
}

void CMediator::SendMessage(CCommand *cmd)
{
for (int i = 0; i < m_recipients.GetSize(); i++)
{
m_recipients->Execute(cmd);
}
}

void CMediator::SendMessage(CCommandInterface *whom, CCommand *cmd)
{
for (int i = 0; i < m_recipients.GetSize(); i++)
{
if (whom == m_recipients)
{
m_recipients->Execute(cmd);
}
}
}

//--------------------------------------------------------------------
//CPP- где что-то делаем

любое заинтересованное окно получает указатель на медиатор таким и только таким образом (конструктор медиатора защищенный):
CMediator *mediator = CMediator::Instance();

и регистрирует себя, скажем в своем конструкторе
mediator->RegisterRecipient((CCommandInterface*)this);

В деструкторе, соответственно разрегистрирует
CMediator *mediator = CMediator::Instance();
mediator->UnregisterRecipient((CCommandInterface*)this);

//--------------------------------------------------------------------

Человек, который написал этот код, не участвует в данном форуме. Поэтому любая критика в его адрес была бы неуместной. Это моя просьба НЕ к тем, кто уже участвует в топике данного форума, т.к. это люди воспитанные и интеллигентные. В части построения GUI - критика тоже была бы не уместна, поскольку человек решает несколько иные задачи. Насколько я понял, это те задачи, для которых собственно и были придуманы паттерны mediator и observer. Так, что этот человек НЕ ЯВЛЯЕТСЯ ДАЖЕ МОИМ СТОРОННИКОМ. Он как бы сказал: «Посмотри. Может сгодится…». Мне сгодилось.


**** РЕЗЮМЕ И ДОПОЛНЕНИЯ К ШАГ 5 ****

1) Можно посылать широковещательные сообщения всем объектам сразу, или в отдельности при помощи медиатора CMessanger, выполняя рассылку без использования SendMessage MFC или API.

2) То, что нет SendMessage MFC или API – это хорошо. Не сложно увидеть, что наряду с CWnd–объектами (derived видов и диалогов и т.д.), для которых выше я и «шагал», CMessanger умеет рассылать команды о состояниях enum app_states ЛЮБЫМ объектам. Создайте не CWnd-класс, например, некий CDatabaseOperations, который будет наследовать от CMgItem, и он тоже может быть включён в общую цепочку обработки сообщений.

3) В CMessanger оставлены виртуальные методы FillControls/DisplayControls. Простая широковещательная рассылка в ряде случаев может привести к «нежелательным эффектам», т.к. иногда необходимо контролировать ПОСЛЕДОВАТЕЛЬНОСТЬ, в которой объекты-подписчики будут выполнять команды CMessanger и РЕЗУЛЬТАТЫ этой обработки. Посредник CMessanger - совсем простой и нечего такого не умеет делать. Приведу цитату общего характера относительно паттерна Observer, где тоже присутствует броадкаст:

«…
неожиданные обновления. Поскольку наблюдатели не располагают информацией друг о друге, им неизвестно и о том, во что обходится изменение субъекта. Безобидная, на первый взгляд, операция над субъектом может вызвать целый ряд обновлений наблюдателей и зависящих от них объектов. Более того, нечетко определенные или плохо поддерживаемые критерии зависимости могут стать причиной непредвиденных обновлений, отследить которые очень сложно.
Эта проблема усугубляется еще и тем, что простой протокол обновления не содержит никаких сведений о том, что именно изменилось в субъекте. Без дополнительного протокола, помогающего выяснить характер изменений, наблюдатели будут вынуждены проделать сложную работу для косвенного получения такой информации.
…»

Бороться с негативом ясно, как. Есть следующие возможности:
А) для бродкаст-манагера «моделировать» асинхронную обработку подписчиками. Сложность решения возрастает неимоверно.

Б) делать умных подписчисиков, запрашивающих стэйт и другое, пока даже не знаю, что…
В) дробить сообщения на более мелкие, например, UM_READ - на UM_READ и UM_XXX_READ, что также не универсально и может перегружать логику приложения событиями
Г) делать что-нибудь нууууууууууу, совсем уж простое. Это я и делаю, сохраняя FillControls и DisplayControls в качестве виртуальных методов «медиатора». Как можно использовать FillControls и DisplayControls для группы подписчиков??? тоже понятно – если можно, то обработать мессагу бродкастом. Если необходим анализ очерёдности, или ещё чего (о чём можно знать только реализуя это в дочке CMessanger конкретного проекта) – то сделаем это. Короче говоря, FillControls и DisplayControls – это дополнительные посредники, вернее дополнительное средство анализа в посреднике (медиаторе). Если так не делать, то прямая дорога к п. А)Б)В), имхо. Например, можно было бы делать такой вызов theMsg.FillControls(UM_READ_DB):

//---------------------------------------------
// h-file
class CMyMsg: public CMessanger
{
public:
bool FillControls (UINT message);
};
extern CMyMsg theMsg;


//----------------------------------------------------
// cpp-file
bool CMyMsg::FillControls(UINT message)
{
switch (message)
{
case UM_ERASE:
ClearControls();


break;
case UM_READ_DB:
FillControls(UM_ERASE);
if( GetStatus() == ST_XXXX )
((Cxxx*) GetWnd(IDW_XXXX))->XXXX();


break;
}
return true;
}
//----------------------------------------------------

PS.
Ещё раз благодарю всех тех, кто откликнулся. Уже не обещаю, что дальше не буду писать столь пространно и столь длинные посты, т.к., думаю сделать завтра последний пост типа «ИТОГИ. КАК БЫТЬ?».
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32219246
Фотография vdimas
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Тема, конечно, интересная. Только не надо было столько писать кода - можно отпугнуть потенциальных помощников.

Итак резюме:
На "чистом" MFC без дополнительного труда невозможно ничего писать для обработки именно данных.
Есть еще момент, о котором тут не упомянули. Часто на формах с данными необходимо узнать, что юзер уже собрался переходить к другому контролу и текущий контрол должен записать свои данные на форму. В Бейсиках для этого есть очень удобное событие BeforeUpdate, в котором можно проверить данные. С MFC все гораздо сложнее - ты не можешь узнать (без дополнительный ритуальных приседаний), что тебе надо вызвать валидацию, т.к. если у тебя на форме есть вкладки, представляющие собой разные классы-диалоги, то как отличить переход по форме (по вкладкам), от перехода на другую форму? (В последнем случае валидацию вызывать не надо).

В моей задаче есть разработанный и проверенный годичной эксплуатацией енжин. Могу поделиться.
Смысл простой: я завел иерархию классов: Control, ControlContainer, DataControl, Form, DataForm и т.д.
Отнаследовал все контролы по работе с данными от DataControl.
DataForm хранит несколько наборов данных. Есть удобные средства для заполнения данных на форме и для синхронизации с базой. Кстати, если контрол (окно) лежит на вкладке, то переменную, его представляющую, можно смело ложить на главную форму, только пользоваться надо не DDX а SubclassDlgItem. Дальше, для DataControl определены несколько событий, которые можно перехватывать ГДЕ УГОДНО, хоть в глобальных функциях, а нетолько в том же классе, где переменная используется. Вся эта разработка делается мною по заказу Philips для их учетной системы, но большую часть этого я разработал до них, поэтому могу с чистой совестью поделиться.

Библиотека включает в себя:

- отлаженную объектную модель DataForm->DataControl, причем форма может состоять из множества вложенных как матрешка TabControl-ов и вложенных диалогов.

- CFormView не использую, CDocument тоже. Научился отображать диалоги во всяких MDI-фреймах. Теперь экземпляры одной и той же формы можно показывать как модальные диалоги, немодальные диалоги, MDI формы.

- Полностью прозрачный Data Engine: каждая форма уже имеет от базового класса несколько наборов данных: те, что пришли при начальном обновлении, текущие редактируемые, данные по-умолчанию. Можно сколько угодно редактировать данные контролами, на сервер уйдут только те данные, которые были реально изменены. Тоже самое касается каждого DataControl-а, можно сколь угодно долго играть с его значением, но когда наступит время переходить к другому контролу, BeforeUpdate() вызовется только при m_value!=m_oldvalue.

- Продвинутые и интересные TabControl, ComboBox, DateTimeEdit и т.д.

- Полный событийный фреймворк, в стиле C#, т.е. события можно перехватывать где угодно (кроме шуток).

- Ошизенные smart-pointers, кто в этом хоть немного понимает - свалиться со стула, STL и Boost отдыхают. CComPtr от Microsoft я не использую - слабоваты :)

- Ассинхронные и синхронные объекты - tasks, которые можно кидать соседним threads.

- Stream-библиотека, мне кажется гораздо интереснее, чем от STL.

- Поддержка CDhtmlDialog от MFC 7.0. У меня задний фон многих диалогов и форм - HTML. От всяких Static и Group я давно отказался - все это на HTML, а работаешь с ними как с любыми другими контролами, все эти объекты-связки наследованы от Control и DataControl. В принципе, можно вообще обойтись только HTML контролами, но мне не хватает их функциональности. Хотя при желании нужно добавить в существующий код чуть-чуть, и можно вообще отказаться от ресурсов форм - все на HTML. (А сами HTML хранить рядом в CHM-файлах :) наверно так и сделаю в ближайшее время)

- Автоматический layout форм при изменении размера: кидаю на HTML-страницу, представляющую задний фон, невидимые и видимые таблицы и DIV-ы, а свои контролы просто "связываю" c ячейками таблиц или элементами HTML подложки. Связывание - одна короткая строка кода. Проблема smart-ресайза решена раз и навсегда.

Если интересно - давай общаться. В принципе, можно опен-соурс проджект сбацать. А то одному тянуть как-то неинтересно. Хотя 80% работы уже сделано.
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32219511
gardenman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вопрос хочу задать всем присутствующим.
Не приходило ли вам когда-нибудь в голову, что все те компоненты и элементы управления, которыми нас снабжает Microsoft по дефолту -
полная лажа, и на самом деле чтоб сделать что-то путевое, все приходится переписывать.
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32219655
Фотография vdimas
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Дык, правильно делает, дает место под солнцем для других. А ее MFC - одно название говорит за себя, фундашион, тонкая типа объектно, типа ориентированная типа прослойка.
Повторяю суть моего предыдложения:
есть отлаженный енжин для работы с ланными и соотв. контролы для него. Контролы - на MFC писанные, енжин - вполне абстракный, на MFC ему начхать.
Надо кому или нет?
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32219719
ggg
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
может void_123 заинтересуется?
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32220070
void_123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
to vdimas
Да, интересно. Давай общаться, хотя а что делаем мы?

to ALL
Про "компоненты" MFC - нет там компонент. Компоненты есть у борланда.

to ggg
>>может void_123 заинтересуется.
Что имеешь в виду?
Вообще мне интересно всё, что на кэш. Без этого как?
А с моей, около-программерской колокольни, интересно всё, что связано с общими, концептуальными вопросами.
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32220361
void_123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
*** ИТОГ ***

(Везде, где есть сильные утверждения – разумеется ИМХО. По тексту не везде писал, но это так)
Подведём черту. Общее заключение о том, как надо и как не_надо, я не сделал для себя. Можно с определённостью повторить только то, что заметил Ktirf в одном из самых первых ответов, имея в виду тему его поста - «Не слишком благодарное дело - тянуть подход из библиотеки с одной идеологией в библиотеку с другой». Это точно. Хотя задачу мы и развивали так, чтобы выработать некое ОБЩЕЕ решение, полезное и в средах «рисования-программирования», и в средах типа VC (++ MFC), но всё же - «Что борланду хорошо, то мелкософту - смерть». Заметил я про борланд, т.к. это было в начале топика.


** BCB++ **

В отличие от VC, IDE BCB вставляет строительные кирпичики–компоненты непосредственно в определение формы. Для программеров, кто не сталкивался с рисовалками борланда - это выглядит, приблизительно так:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
class TfrmMonitor : public TForm
{
__published:	// IDE-managed Components
        TPanel *PanelMain;
        TTreeView *TV;
	…..	// ~  100  - 200  компонент контролов, включая табы, вякого рода 
	…..	// тул-, кул-бары, меню, ст. диалоги, имидж-листы
	…… 	// и т.д.. Т.е. это ВСЁ то, что программер VC пишет руками.
		// Как в Visual Basic, в коде мы просто обращаемся к хорошо написанным
		// методам и пропертям этих компонент

   private:	// User declarations
	….
	…..
        void    DisplayControls( int _mode);
        void    FillControls( int _mode);
public:		// User declarations
        __fastcall TfrmMonitor(TComponent* Owner);
};

Для КАЖДОЙ, маломальски навороченной формы (их в проекте может быть 15-20) я пишу приват-обработчики FillControls/DisplayControls, аналогичные тем, что выше. Эти обработчики обращаются !!!ТОЛЬКО К СВОИМ!!! компонентам. Я НИКОГДА не обращаюсь к ДРУГИМ формам из этих обработчиков. Да это и невозможно, в силу их «всё-приват», в силу того, что «наружу» смотрит только конструктор, и + пара перезагруженных методов, чтобы удобнее создавать форму в ран-тайм SDI-приложения. Это хоть и косметика, но такая, что делает программирование ОДНОЙ ЭТОЙ ФОРМЫ более прозрачным для меня. Это, думаю, полезно только для BCB++ и только для SDI, и только если формы - МОДАЛЬНЫЕ. Наличие FillControls/DisplayControls не делает в этом случае приложение противоречивым. В этих функциях повсеместно использую рекурсию (чем её больше, тем лучше), для большей прозрачности обработки событий, см. ниже:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
void  TfrmMonitor::FillControls( int _mode)
{
    switch (_mode) {
    case _STARTUP :
            TVClear(TV);
            FillControls(_ERASE);
            TVStartUp(TV);
		…
            break;
    case _READ:
            FillControls(_READ_DB);
		…
            break;
    case _ERASE:
            LVClear(LV);
		…
            break;
    case _READ_DB:
		// TODO	
		…
           break;
    case _READ_FOLDER:
		// TODO	
		…
            break;
    case _READ_OBJECT:
            break;
		…
		…
    }
}


Заменять этот свич вызовами соответствующих методов формы, или нет – непринципиально, дело вкуса. Однако это ликвидирует эффект концентрированности управления логикой событий в ОДНОЙ форме, имхо. То, что в перечисленных выше условиях ЭТО работает железно – проверено многолетней практикой.


** ЧИСТО ООП **

Здесь про классический ООП подход, который amirul, изложил очень лаконично в своих постах. Поскольку его важные замечания могли остаться незамеченными, то сделаю их перепост, вернее выдержки. Перепост связан и с тем, что на разных форумах предлагали, дескать, «а мож так?» (в смысле, как amirul). Кроме того перепост необходим, поскольку дальше будут ссылки на этот подраздел. Если вкратце, то:

1) Ни каких горизонталей. Вся система представляет строго иерархическую модель. «…Если сделать нормальную иерархию объектов, то рассылать нужно сверху-вниз слева-направо и тогда порядок всегда будет жестко задан - вот от него и отталкиваться впоследвие.»

2) Глобальные обработчики не имеют права на жизнь. Любая обработка только внутри самого класса. «…Ни в коем случае нельзя вводить глобальных обработчиков (даже для каких то групп). Нужно чтобы каждый КОНКРЕТНЫЙ объект САМ знал, как реагировать на тот или иной вызов. Обработчик должен быть методом класса (если речь об абстрактном классе, то виртуальным методом)… При переходе приложения в какое-то состояние, оно должно перевести только своих непосредственных детей. А те уж сами о себе позаботятся.»

Проще, наверное, сделать перепост кода, чтобы было понятно не только «в принципе», но и как?:

ВАРИАНТ amirul:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
// стартуем приложение MFC VC++ 
BOOL CMyApp::InitInstance() 
{ 
	InitCommonControls(); 
	CWinApp::InitInstance(); 
	… 
// в следующих двух вызовах читаем данные, заполняем контролы 
	// и приводим их в приличный вид визибл-невизибл, энайб-дисайбл и т.д. 
	OnStartup();
	// запрещаем пользователю делать всякие глупости и портить данные, 
	// пока он сознательно не переведёт приложение в состояние 
	// ST_EDIT, нажав, например, на кнопку «Редактировать» 
	OnRead();

m_pMainWnd = &dlg;
	int nResponse = dlg.DoModal();
	// Обработка возвращенного значения.
	//...
return TRUE; 
} 

// ------------------------------------------------------------- 
 
// Здесь пользователь нажимает кнопку «Редактировать» для перевода 
// формы приложения в режим редактирования ST_EDIT 
void CPageGeneral::OnBnClickedEditButton() 
{ 
	// здесь, до вызовов двух функций ниже 
	// все  200  контролов ещё недоступны для редактирования 

	theApp.OnEdit();

	// Кнопка сообщает приложению, приложение-диалогу, диалог-страницам,
	// страницы-контролам. Причем сообщает можно понимать как угодно.
	// Например, страница прямо переводит контролы в нужное состояниею
	// А все остальные просто вызывают нужную мембер-функцию дочернего объекта.
	// здесь все  200  контролов уже доступны для редактирования 
	// и пользователь может вводить данные 
}
// ------------------------------------------------------------- 
 
// Здесь пользователь нажимает кнопку «Сохранить» для перевода 
// приложения в режим сохранения результатов редактирования ST_SAVE, 
// а затем возврата в состояние просмотра данных ST_READ 
void CPageGeneral::OnBnClickedSaveButton() 
{ 
	// здесь все  200  контролов доступны для редактирования 
	// т.к. состояние ещё ВОЗМОЖНО ST_EDIT 

	theApp.OnSave();
theApp.OnRead();

// В данном контексте, лучше бы назвать эти функции просто Save и ReadOnly
	// Хотя для сохранения сквозного стиля можно оставить и так
	// здесь все  200  контролов изменили своё состояние, 
	// и уже недоступны для редактирования 
	// Мы в состоянии ST_READ просмотра базы данных 
} 


Все объекты, в том числе и theApp, должны быть реализациями классов, для которых определены методы OnSave, OnRead и т.д. Можно их наследовать от «интерфейсных заготовок» с виртуальными или чисто виртуальными (что лучше) методами, соответствующими enum app_states. Дело вкуса. Например:

КОД amirul:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
class CPage: public CDialog {
public:
virtual void OnStartup() =  0 ;
virtual void OnRead() =  0 ;
virtual void OnEdit() =  0 ;
// ...
}

class CPage1: public CPage {
// переопределение виртуальных функций
}
//...
class CPage20: public CPage {
// переопределение виртуальных функций
}

// -----------------------------------------------------------
 
// При добавлении CPage:
CMainDialog::SomeFunction() {
    TCITEM tci;

    tci.mask = TCIF_PARAM | TCIF_TEXT;
    tci.pszText =  "Page 1 ";
    tci.lParam = reinterpret_cast<LPARAM>(new CPage1);
    if (tci.lParam ==  0 ) { // ошибка, хотя можно использовать и try-catch
    }
    // m_pTabCtrl был добавлен в ClassWizard-е
    m_pTabCtrl->InsertItem( 0 , &tci);
    // И так далее для остальных табов
}

// -----------------------------------------------------------
 
// Работать с этим так:
void CMainDialog::OnStartup() {
// ...

    for (int i =  0 ; i < m_pTabCtrl->GetItemCount(); i++) {
        TCITEM tci;
        tci.mask = TCIF_PARAM;
        m_pTabCtrl->GetItem(i, &tci);
        ((CPage *)tci.lParam)->OnStartup();
    }
// ...
}


Я бы здесь отметил, что построенное таким образом приложение будет работать всегда. Единственно, что необходимо сделать – это разработать иерархию, о которой выше. Может появиться много нудного, однотипного кода. Но в этом и фокус, чтобы делать одну и ту же работу, пускай и нудную. Вероятность, что доедим до конца проекта -100%, хоть и не «экспрессом». А «нудный» код, о котором я, например, при очистке содержимого контролов таков:

НУДНЫЙ КОД

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
void CMyApp:: ClearControls()
{
m_pMainDialog.ClearControls();
m_pTreeView.ClearControls();

m_pParent1.ClearControls();
//…
//…
m_pParentXXX.ClearControls();
//и т.д. 
}
// А затем КАЖДЫЙ из родителей перечисляет
void CParent1:: ClearControls()
{
	m_pChield1. ClearControls();
	m_pChield2. ClearControls();
	//…
	//…
	m_pChieldXXX. ClearControls();
//и т.д. 
}


В этом примере есть некоторая натяжка, связанная с тем, что в листе дерева на каждом уровне иерархии не будет уж очень большого числа Children. В этом подходе есть огромное преимущество перед использованием CMessanger. Оно в том, что мы легко и всегда контролируем последовательность и результаты (если необходимо) каждого вызова OnRead, OnEdit и т.д. на каждом уровне иерархии. Так что подход железный, в том числе и при построении GUI.


** ОБЩЕЕ ПРО СMessanger для GUI**

Основной идеей было НE создание около-медиатора CMessanger, а попытка опереться только и только на логику приложения вне зависимости от модели взаимодействия CWnd-объектов приложения (диалогов, видов, баров и т.д.). Если кому-то, нравятся сравнения с сетевыми технологиями, то это попытка получить «наложенное» решение, например, приватлан поверх существующей сетевой инфраструктуры . CMessanger – это пример (даже не реализация) одного из инструментов такого решения. Если и развивать это направление, то только при удовлетворении следующих условий, имхо:

1) от иерархической модели управления состояниями enum app_states следует отказаться в том смысле, что методы для перевода приложения в одно из состояний app_states должны быть определены не для всего дерева (см. выше «ЧИСТО ООП»), а только для ряда ключевых листьев (node) этого дерева. Если в «ЧИСТО ООП» переход в новое состояние всегда выполняется, начиная с рут (например, theApp), как было сказано «слева направо и сверху вниз», то здесь, путь короче – реагируют на сообщения только те подписчики, которым это необходимо. Если этого не сделать, то при введении CMessanger параллельно будут жить ДВЕ логики управления app_states.

2) исключением из п.1 могут быть случаи, имхо, когда ООП–иерархия управления состояниями app_states присутствует только для некоторых ветвей иерархического дерева, для некоторых групп объектов, например, пейджей и им подобным, см. выше. Во взаимодействии этих объектов ветки (CMainDialog, CTabCtrl, CPage1, … , CPage20) ОСТАВЛЯЕМ ИМ ИХ ОБЪЕКТНО-ОРИЕНТИРОВАННУЮ ЖИЗНЬ. Другими словами, если необходимо перевести приложение в состояние ST_READ, то будем вызывать только один метод CMainDialog::OnRead() (далее слева направо и снизу вниз по схеме выше «ЧИЧТО ООП»). Отличие от «ЧИСТО ООП» в том, начинать «оповещения» будем не с корня theApp, а с theMsg, где мы делаем подписчиками наиболее значимые на наш взгляд объекты. В данном случае один из таких значимых объектов – CmainDialog, но не пейджи.

3) Демо-CMessanger необходимо довести до ума. Во-первых, «разрегистрация». Во-вторых, можно попытаться сделать модель более чистой, убив там виртуальные методы FillControls/DisplayControls, но дав что-то взамен для управления последовательностью и для контроля результатов выполнения переопределённых виртуальных методов CMsgItem. На одном из форумов было, например, предложение ввести приоритеты выполнения команд. Мелочь – заменить виртуальные методы CMsgItem чисто виртуальными. Ну и последнее - помнить о том ЗЛЕ и граблях, о которых ниже.


** ГОРИЗОНТАЛИ И ДРУГОЕ ЗЛО **

1) Если мы оставим в приложении модель «ЧИСТО ООП» и дополнительно введём СMessanger, то не создав ничего, мы сломаем модель ООП, поскольку тем самым введём избыточные горизонтальные связи между листьями (node) ООП иерархической модели. Состояние приложения должно меняться или по одной модели, или по другой. Надо что-то одно.

2) amirul при обсуждении в одном постов проблем модернизации правильно сказал, что в общем случае «число» изменений в коде при модернизации приложения, построенного на плоской модели CMessanger будет квадратично зависеть от числа подписчиков N. Это действительно так, если различать сообщения A->B и B->A и кроме того A->A. Я бы ослабил формулировку до «может зависеть…», имея в виду реальные ситуации, а не модель, но заметил бы другое зло. С некоторой натяжкой можно говорить о N*N независимых способах передачи сообщений состояниях системы в простом мессангере, как о N*N степенях свободы. Если провести дальше аналогию в механику, или теорию надёжности, то мы получим катастрофическое снижение и того и другого даже при малой вероятности краха приложения при передаче только одного сообщения (здесь имеется ввиду ошибка программера). Модель становится «неустойчивой». Все эти рассуждения носят налёт теоретизации, возможно и излишней. Но не трудно увидеть, что, включая в мессангер минимально необходимое число объектов, а не всё, что попало, мы будем также «квадратично приближаться» к дереву ООП. Т.е. в манагер необходимо включать, только главных фигурантов приложения. А там уж пусть они объектно-ориентированно плодятся и размножаются.

3) Если мы уж включили объект в манагер, то обращаться к объекту следует только через посредник-манагер. Иначе конфликт логик – неизбежен, рано или поздно. Т.е. модель в некотором смысле становится очень зажатой.

4) плохая идея хранить где-то ещё указатели на объекты, которые мы уже включили в манагер.


** А ЧТО ТОГДА ХОРОШЕГО ВО ВСЁМ ЭТОМ? **

1) Хорошее есть в применении похожих паттернов проектирования для решения более сложных задач. Но у нас другой, относительно простой случай-всего лишь GUI. Так, что к нам «их хорошее» не относится.

2) Если есть «безобидные» состояния и сообщения, например, ClearControls, то для последних можно сделать только один вызов, а не десять, как в «ЧИСТО ООП» (см. НУДНЫЙ КОД выше):

Код: plaintext
1.
2.
3.
4.
// Броадкаст рассылка
void CXXXXX:: ClearControls()
{
Messanger.ClearControls();
}


3) В цепочку обработки сообщений легко включить любые объекты, а не только CWnd, см., шаг 5.

4) Не то, что хорошее, но обнадёживает. Всё, что я постил в «шагах» работает.


** ЧТО ЕЩЁ СОВЕТОВАЛИ НА ФОРУМАХ? **

Здесь приведу только некоторые советы, которые мелькали не один раз у различных форумчан.

1) Делать рассылку мессаг при помощи SendMessage API или MFC. Мы это убили на «ШАГЕ 5». Надеюсь, что понятно почему.
2) Использовать возможности архитектуры Документ-Вид, для того, чтобы «оповещать» виды о любых изменениях. Идея, я думаю, понятна всем: получаем документ -> изменяем там, чего нам надо -> просим документ сказать CDocument::UpdateAllViews. Далее для всех наших видов переопределяем CView::OnUpdate, где вначале анализируем app_state, а затем уж в зависимости от состояния приложения перерисовываемся, как надо.
Думаю, это будет работать. Не пробовал. Но это MFC, от специфики которой хотелось уйти. Допускаю, что в этом случае можно с небольшими накладными расходами получить контроль за состояниями приложения, хотя мелкософт и делал эту архитектуру несколько для иного, уж не для управления app_states – это точно. Я даже сомневаюсь, что сами мелкософтовцы её пользуются.
3) Было много советов по поводу того, как работать с пейджами, где хранить указатели на CWnd-объекты (в аппе, майнфрейме и т.д.), за что большое спасибо. Посмотреть код других на форуме, думаю всем полезно.


PS.
Не судите строго, люди добрые, ибо я вообще не Программист, а eMbedded.
Благодарю всех, кто уже высказался и кто ещё выскажется.
Предлагаю подвести здесь черту. Хотелось бы услышать Ваше мнение по любым вопросам, связанным с темой топика. Стоит дальше этим заниматься, или нет?
В общем, интересно всё, что Вы думаете по этому поводу.
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32220852
Mik Prokoshin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
IMHO идея топика - навьючить идеологию VCL на MFC и посмотреть - как круто получилось :-)
Представление о разработке большого сложного проекта авторам чуждо...
(хотя я просто могу предположить, что работая с embedded systems, void_123 вряд ли сталкивался вплотную с подобными проектами - сложных технически там хватает, а вот больших и сложно связанных...).

Идея разделения Doc/View просто необходима тогда, когда нужно четко разделить проект по частям.
Вот документ, состоящий из несколько частей (объектов) - раздаем по группам разработчиков. Для каждой части надо бы свой view - раздали по группам, связав их с группами разработки соотв. частей документа. Надо бы по несколько разных view делать, да в одном чтоб были три части документа, а в другом - две... Раздали, связав с соотв. группами разработки документа. И так же, даже если делает один человек - он может не думать о посторонних взаимосвязях разрабатываемой формы с другими формами отображения документа.
При этом ослабляется сцепление между визуальными формами (макро объектами GUI) (говоря формальным языком).

При предлагаемом в топике подходе мы имеем жесткую связь между экранными формами через CMessanger, который не хранит данные, а служит лишь для того, чтобы реализовывать это сцепление. В чем практические минусы и плюсы ?
Да, упростилось групповое управление контролами с данными. Модальные диалоги - все отлично ! Но если диалоги немодальны, да еще и допускают неоднократное открытие (а именно это подразумевает стандартный стиль Win-compatible программы со стандартным меню сверху), вот тут легко понять, что данная архитектура неработоспособна в принципе. Ибо мы определяем динамически сложную иерархическую или плоскую структуру Messanger'ов. При этом мы должны учитывать взаимосвязи как между частями одной формы, так и между разными формами, содержащими одни данные (да бог с ним, с дублированием информации в этих формах...). Сложность разработки увеличивается весьма и весьма, надежность (где гарантия, что мы какие-то связи не забыли - группу мессенджеров не создали) снижается.

Альтернатива, как правильно заметил void_123 :
>2) Использовать возможности архитектуры Документ-Вид, для того, чтобы «оповещать» виды о любых изменениях.
И MS этим подходом пользуется по полной программе. Примеры : Word, Excel, SQL EM и QA (О, это к вопросу о работе с базами данных).
Пользуется этим не только MS - и Corel и Adobe Photoshop и тьма других приложений использует данный подход. Ах, мы про работу с базами говорим... Значит пользователю необязательно иметь несколько окон на экране - ему бы форму со 100 контролами, куда он со скоростью пулемета будет данные забивать. Так ведь не надо юзера с оператором путать, кроме операторов в любой системе есть юзеры, у которых потребности многогранны и разносторонни. И желают видеть они массу окон (документов) на экране, а то, как на кассе - пока не обслужил одного - дальше никто не пойдет.
Вот и наблюдал я недавно, когда обслуживает менеджер одного клиента, а тот сказал : "Ща, отложите пока то, что мы с Вами набрали, я пойду подумаю, чего мне еще взять...". И ушел. А менеджер для работы на соседний комп пересел (благо их несколько было и были свободные). Се ля ви !

Так что недаром в свежих версиях дельфи появилось понятие DataModule, которое является аналогом CDocument :-)

Вывод - описанный метод применим при разработке небольших приложений с использованием модальных диалогов либо (немодальные) отсутствием взаимосвязей (логики) между формами документов (документами) (RAD-подход).

Уф, целый день понемногу писал ... :-)

P.S. Я вот сейчас для небольшой системки использую для простоты и скорости понятие системы связанных master-detail датасетов в качестве документа и вот TDataSource в качестве прослойки передатчика данных ни к черту, скажу я вам. Совершенно об кастомизабельности этого механизма не думали авторы. А то ведь до простого - хочу показать юзеру закрыт документ для редактирования или нет - контролы Enabled или нет. Так ведь так приходится извращаться !!! Сказали авторы VCL - только Read-only у контролов устанавливать и нефиг выпендриваться :-) Вот и извращаемся, как автор топика... помаленьку...
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32221594
olk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Читал пэйджер... долго думал
Как мне кажется проблема не в том, как обработать сообщения и откуда их получать, в принципе и Борланд и VC++ (+MFC), позволяют (при достаточно
глубоком знании реализовать любую концепцию ... при этом (IMHO) борланд хуже (если не писать свои специлизированные VCL).
Для меня , как практикуещего программиста в области БД, например гораздо
важнее (интереснее) написание него-ко универсально АПИ (инжина) для работы с БД.
При этом в качестве конечного инструмента в идеале я представляю
некоторый визард, создающий некоторый ресурс (причем динамический настраиваемый пользователем из приложения, в виде некоторых метаданных - определенных на пространстве рабочей станции+эккаунта с некоторыми предопределенным значением (причем в иделе это можно хранить или в регистах или в файле или даже в СУБД) с описанием формы (вьюшки), диалога (совокупности диалогов) .
В общем случае (что бы было понятно о чем я говорю) для примера ,
приложение привязано к некоторой (некоторым) CDatabase (ODBC,DAO ...),
форма привязано к некторому анонимному CRecordset (т.е. SQL - запрос берется из метаданных) и далее все контролы, гриды, комббобоксы и т.д. (их расположение (количество полей (размеры),расположение в гридах) и привязка берется так же из метаданных)... ну это в идеале ...
Сей час же мне для работы не хватает "адекватного" грида и нормального механизма обработки ref relation на формах ...
Ну и в поддержку некоторых из предыдущих "ораторов" те кто проникся идеологией Document-View, никогда не скажет что это некторый излишек, пожалуй (IMHO) это одна из наиболее удачных "находок" MFC ...
Сорри если не в топик ...
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32221633
void_123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Постил я для того, чтобы получилась дискуссия. Поэтому отвечаю. Хотя теперь интереснее слушать других.

to Mik Prokoshin
Отвечаю сразу на два Ваших поста.

******
>>Загонять все контролы в один громадный список - фактически >>организовывать один документ с массой полей. Подумайте, рационально ли >>это.

Там этого нет. С чего Вы?


******
>> ... И попытайтесь показать, что это - "от лукавого" :-)

Не стану пытаться потому, что это , как известно, работающая хорошая архитектура. См. мой пост.
Но кто сказал, что эта архитектура единственная?


******
>> ... А Вы пытаетесь все данные просто загнать в контролы и управлять ими всеми и "сверху" и "горизонтально".

Повторюсь, что этого нет в моих постах в части собственно контролов. С чего Вы?
Передозировка в решениях, где присутствует две логики управлений - и "сверху" и "горизонтально" (если под "сверху" Вы имеете в виду обход древовидной модели "слева направо и сверху вниз") может привести к краху. Если Вы об этом, то правильно. Разве не об этом по большому счёту пост? Да, и чтобы было уж окончательно понятно, извините за нудность, никто про управление НЕПОСРЕДСТВЕННО контролами и не говорит.

******
>> ... можно, конечно, хранить данные документа и в самих контролах,

Это скорее плохой стиль, чем "экономия", о которой Вы пишите, имхо. Не сердитесь.

******
>>IMHO идея топика - навьючить идеологию VCL на MFC и посмотреть - как круто получилось :-)

а) "навьючить" - невозможно, поскольку это просто разное. Впрочем, выше про то был мой пост (в самом начале "ИТОГИ").
б) "круто получилось". Здесь не стану отвечать, хотя скажу, что я уже взрослый.

******
>>Идея разделения Doc/View просто необходима тогда, когда нужно четко разделить проект по частям. ....

Я уже понял, что Вы активно используете эту архитектуру. Повторюсь, никто и не говорит, что это не работает или плохо работает, в том числе при "корпоративном" проектировании приложений документ-вид.
Да Вы ж сами чуть ниже, замечаете это. "....Альтернатива, как правильно заметил void_123 : ..."


******
>>При этом ослабляется сцепление между визуальными формами (макро >>объектами GUI) (говоря формальным языком).
>>При предлагаемом в топике подходе мы имеем жесткую связь между >>экранными формами через CMessanger, который не хранит данные, а служит >>лишь для того, чтобы реализовывать это сцепление. В чем практические >>минусы и плюсы ?

С чего Вы взяли, что связи ( на самом деле и не очень приятные из-за "горизонтальности" ), которые вводит CMessanger более жесткие, чем в стандартной архитектуре.
Может я плохо объяснил, что подписчиками медиатора CMessanger должны быть только главные фигуранты. Например, отдельный вид. Все объекты, включённые в медиатор должны общаться ТОЛЬКО при помощи мессаг с ДРУГИМИ наболюдателями медиатора, например, с другими видами, ТОЛЬКО ПРИ ПОМОЩИ ВЫЗОВОВ МЕТОДОВ МЕДИАТОРА. Связи очень даже слабые.

******
>> ... Но если диалоги немодальны, да еще и допускают неоднократное >> открытие (а именно это подразумевает стандартный стиль Win-compatible >> >> программы со стандартным меню сверху), вот тут легко понять, что данная >> архитектура неработоспособна в принципе.

Аргументы в студию...
Посмотрите так называемые паттерны проектирования, например, Observer.


******
>>Ибо мы определяем динамически сложную иерархическую или плоскую структуру Messanger'ов.

Об этом, собственно и топик. Или - или.
А живёт ли это всё вместе для несложных задач, а не тех, для которых ОООчень не глупые люди придумали соответствующие паттерны проектирования?


******
>> ... куда он со скоростью пулемета будет данные забивать. Так ведь не >>надо юзера с оператором путать, кроме операторов в любой системе есть >>юзеры, у которых потребности многогранны и разносторонни. И желают >>видеть они массу окон (документов) на экране, а то, как на кассе - пока не >>обслужил одного - дальше никто не пойдет.

Я бы не обобщал полезности SDI или MDI, если Вы об этом. Бывают разные задачи. Программисту хочется сделать нечто хорошее, когда для реальных потребностей некого производства достаточно иметь "вбивалку" данных. Сколько угодно. Это офтопик.


******
>>Так что недаром в свежих версиях дельфи появилось понятие DataModule, >>которое является аналогом CDocument :-)

Недавно? Вы давно знакомы с Делфи? Я с v3.0. Хотя это тоже офтопик.


******
>>Вывод - описанный метод применим при разработке небольших приложений >>с использованием модальных диалогов либо (немодальные) отсутствием >>взаимосвязей (логики) между формами документов (документами) (RAD->>подход).

Принимаю к сведению, т.к. для меня полезны все без исключения Ваши замечания. Но не могу здесь не возразить, т.к. для простых приложений ВСЁ ЭТО просто НЕ НАДО. Это будет вредным. Простые приложения следует реализовывать простыми средствами. Достаточно МФЦ и имеющихся архитектур.


******
>>устанавливать и нефиг выпендриваться :-) Вот и извращаемся, как автор топика... помаленьку...

Я не извращаюсь. Помимо реальной работы, которую я делаю каждый день "стандартными" способами, мне интересен вопрос КОНЦЕПТУАЛЬНО. Ну, как бы сказать. Если кому интересно, а как можно, не в стандартной архитектуре? Почему Вы думаете, что у участников форума не может быть идей не хуже мелкософта в части построения архитектуры приложений.
Мы ведь не навязываем своих идей - "делай как я". Мы исследуем разумность поиска других нестандартных решений.
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32221843
gardenman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
мысли по поводу того, как писать GUI для приложений к базам данных.
1) В этом варианте архитектура Doc/View - нафиг не нужна. ИМХО Doc/View
была не для этого придумана. Она заточена на операции с файловыми документами. конечно можно ее прикрутить и к базам данных - но выглядит это уродливо.
2) должно быть так -> Форма ввода данных для выполнения транзакции -> выполнение транзакции.
3) С этой стороны абсолютно без разницы какое это приложение - MDI или SDI интерфейсом.
4) Каждый элемент управления должен самостоянельно обрабатывать и сохранять данные ввода в буферных переменных, и DDX - нафиг не нужен.
5) В порме ввода часто встречаются зависимае поля. Например не выбрав валюту, глупо вводить курс, или не выбрав отдел - невозможно выбрать человека в отделе. С этой точки зрения активация полей в форме должна проходить в строго определенном порядке. А то, что предлагает Windows - для таких целей не подходит.
Вывод, если хочешь чтоб всё работало как надо - делай все сам.
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32221993
void_123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Да, gardeman, Вы правы

*************
>>1) В этом варианте архитектура Doc/View - нафиг не нужна.

Но некоторым нравится.

*************
>>конечно можно ее прикрутить и к базам данных - но выглядит это уродливо.

А Вы видели в МФЦ, что-нибудь неуродливое? :))

*************
>> 2) должно быть так -> Форма ввода данных для выполнения транзакции -> выполнение транзакции.

Согласен. Прикол - обсуждаем БД, а про это и слова никто не сказал раньше. Если посмотреть для чего делался CMessanger, то там для этого и введены введены понятия состояний app_states для того, чтобы определённо знать, что в каждый момент делаем. Например, в моих приложениях нельзя, как в Аксесе, печатать буквы, и это СРАЗУ будет вливаться в базу при переходе на другой контрол. Вливается, вернее делается попытка влить транзакцией, ТОЛЬКО по нажатию "Сохранить". И не из контролов - а из буферов (не DDX-мембер переменные ). Упрощённо схема такова: пока редактируем - изменяются только содержимое контролов. Когда нажали "Сохранить", из контролов переливается в буферы (не DDX), затем пытаемся из буферов транзакциями влить в базу. Если не получается (кабель в этот момент уборщица укусила), откатываем, что cможем (конечно, желательно всё) без больших накладных расходов.
Хотел бы я посмотреть на тех, кто использует документ-вид, когда необходимо откатить транзакции. А ведь она (транзакция) и не одна может быть.


*************
>>4) Каждый элемент управления должен самостоянельно обрабатывать и сохранять данные ввода в буферных переменных, и DDX - нафиг не нужен.

Это ты на пятёрку сказал.
Кстати, прикол, ты первый за 10 дней (пока висит топик на разных форумах), кто сказал про это. Да-с.

*************
>>Вывод, если хочешь чтоб всё работало как надо - делай все сам.

Делаю.;))
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32222009
olk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
1) В этом варианте архитектура Doc/View - нафиг не нужна. ИМХО Doc/View
была не для этого придумана. Она заточена на операции с файловыми документами. конечно можно ее прикрутить и к базам данных - но выглядит это уродливо.

Вообще то всегда считал, что Doc/View - это дальнейшее абстагирование ООП
на уровне приложения, т.е. Doc- Содержание - View - представление и ни коим образом не привязано к файловым документам ...

2) должно быть так -> Форма ввода данных для выполнения транзакции -> выполнение транзакции.

хочу заметить что приложения для работы с БД состоят не только из одних форм ввода ... впрочем и для форм ввода в этом случае можно найти применение ... например есть документ который "содержит в себе" данные из справочника ... оринтируясь на этот "документ" я могу иметь к нему несколько "видов" - например вид - реад онли, вид в виде грида, вид в виде диалога ... наконец какой либо графический вид и все это основываясь на едином "документе"


3) С этой стороны абсолютно без разницы какое это приложение - MDI или SDI интерфейсом.

4) Каждый элемент управления должен самостоянельно обрабатывать и сохранять данные ввода в буферных переменных, и DDX - нафиг не нужен.


кхм и как-же он должен его обрабатывать ? а если я нажал кнопку отмена, контрол поля ввода сам должен вернуть предыдущее значение в свою буферную переменную ?
и чем вам ддх не угодил ? это же просто механизм (просто более универсальный) сохранения контролами своих значений в буферных переменных ...

5) В порме ввода часто встречаются зависимае поля. Например не выбрав валюту, глупо вводить курс, или не выбрав отдел - невозможно выбрать человека в отделе. С этой точки зрения активация полей в форме должна проходить в строго определенном порядке. А то, что предлагает Windows - для таких целей не подходит.
как раз тут и может помочь "документ" - на те или иные действия пользователя изменяется состояние "документа" и посылается UpdateAllView - "вид" соответсвено считывая текущее состояние документа может енэйблить или дизайблить соответсвующие контролы

Вывод, если хочешь чтоб всё работало как надо - делай все сам.

Здесь возражений не имею
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32222123
Mik Prokoshin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2void_123:
>Постил я для того, чтобы получилась дискуссия. Поэтому отвечаю. Хотя теперь интереснее слушать других.
Более результативно IMHO было бы оформить результат в виде готовых компонент/библиотеки классов и закинуть на какой-нибудь torry.net или codeproject (смотря для какой среды). Вот и посмотрели бы на оценки :-)

>>Загонять все контролы в один громадный список - фактически >>организовывать один документ с массой полей. Подумайте, рационально ли >>это.
>Там этого нет. С чего Вы?
Я изначально понял идею создания подобного взаимодействия, как желание "рулить" всеми контролами "горизонтально" (одновременно сиречь держать и управлять ими одним списком). Вчитался далее, признаю свою частичную неправоту вследствие последующего изменения направления применения.
Из Вашего первого поста :
>Удобство заключается в том, что всё управление отображением и заполнением контролов (enable, visible и т.д) сосредоточено в одном месте. Вернее сосредоточена общая логика приложения в части управления контролами.

******
>> ... можно, конечно, хранить данные документа и в самих контролах,
>Это скорее плохой стиль, чем "экономия", о которой Вы пишите, имхо. Не сердитесь.
Согласен, но для определенных условий этот подход также имеет право на жизнь.

******
>Может я плохо объяснил, что подписчиками медиатора CMessanger должны быть только главные фигуранты. Например, отдельный вид. Все объекты, включённые в медиатор должны общаться ТОЛЬКО при помощи мессаг с ДРУГИМИ наболюдателями медиатора, например, с другими видами, ТОЛЬКО ПРИ ПОМОЩИ ВЫЗОВОВ МЕТОДОВ МЕДИАТОРА. Связи очень даже слабые.

Очевидно, целей реализации данных классов. Как образец реализации шаблона publish-subscribe вполне нормально выглядит. Просто надо было сразу декларировать четко цели.

******
>> ... Но если диалоги немодальны, да еще и допускают неоднократное >> открытие (а именно это подразумевает стандартный стиль Win-compatible >> >> программы со стандартным меню сверху), вот тут легко понять, что данная >> архитектура неработоспособна в принципе.

>Аргументы в студию...
Посмотрите так называемые паттерны проектирования, например, Observer.

Применение данных классов целесообразно для управления определенными группами понятий (документы, части документов, виды, контролы) в определенных рамках. Типичный же пример когда это применять не стоит (из-за чего я и "завелся") - когда мы хотим сделать MDI интерфейс и должны учитывать связи между окнами. Есть два окна, содержащие общую часть данных. Подтверждая транзакцию в одном окне, мы должны уведомить второе о необходимости перечитать данные. Но данный механизм здесь будет реально усложнять задачу, потому что нам придется делать связи между всеми открытыми окнами, объединяя их в группы (по-моему примерно так это задумывалось сначала :-).
Постепенно же по-моему приходим к той же самой реализации doc/view архитектуры - есть список управления первого уровня (по идее, он должен быть включен в документ и содержать список частей документа), далее списки views для каждой части, далее группы контролов и т.д. В общем мы приходим к тому, что данный подход сможет нам помочь и в рамках Doc/View архитектуры. Просто там нет большой нужды в подобных шаблонах, поскольку это может быть реализовано как части общей архитектуры (скажем FillControls у View, который вызывает FillControls у своих детей и т.д.). Если мне надо передать сообщение для группы контролов - я передаю сообщение их общему родителю, который и обрабатывает его, модифицируя детишек. Это можно выносить в отдельный шаблон, как предложено, но IMHO объем работы практически сопоставим.


******
>Я бы не обобщал полезности SDI или MDI, если Вы об этом. Бывают разные задачи. Программисту хочется сделать нечто хорошее, когда для реальных потребностей некого производства достаточно иметь "вбивалку" данных. Сколько угодно. Это офтопик.
Надо сразу четко оговаривать область применения. А то - сделали что-то и стали решать "вещь получилась хорошая, но куда прикрутить никак не поймем" :-)


******
>>Так что недаром в свежих версиях дельфи появилось понятие DataModule, >>которое является аналогом CDocument :-)
>Недавно? Вы давно знакомы с Делфи? Я с v3.0. Хотя это тоже офтопик.
Хм, тоже извиняюсь, уточнил, в v.3 оно уже было, просто где-то прочитал неверную информацию и не проверил. Я вплотную работаю только начиная с v6.

******
>Принимаю к сведению, т.к. для меня полезны все без исключения Ваши замечания. Но не могу здесь не возразить, т.к. для простых приложений ВСЁ ЭТО просто НЕ НАДО. Это будет вредным. Простые приложения следует реализовывать простыми средствами. Достаточно МФЦ и имеющихся архитектур.
Ну... если этим уже научился пользоваться, то нет разницы, в какое приложение вставлять готовые библиотеки. А если только начинаешь, так все равно надо смотреть разные подходы, библиотеки... В общем тут уж все зависит скорее от разработчика, чем от проекта.

******
>Почему Вы думаете, что у участников форума не может быть идей не хуже мелкософта в части построения архитектуры приложений.

Просто эти идеи (в данном случае) - реализация старых шаблонов. (без обид) Единственное, что необходимо - натолкнуть на идею правильного использования. Надеюсь, что нам с вами удалось прийти к разумному компромиссу. А вообще рекомендую почитать про шаблоны, например, Крэга Лармана. Мне вот очень помогло IMHO.
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32222128
gardenman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2 olk
>как раз тут и может помочь "документ" - на те или иные действия
>пользователя изменяется состояние "документа" и посылается UpdateAllView -
> "вид" соответсвено считывая текущее состояние документа может
>енэйблить или дизайблить соответсвующие контролы

Аха...я тоже так раньше делал.
Но уж очень длинный и некрасивый код получается.
Можно конечно пытаться прикрутить всё что напридумывал мелкософт,
но конечная инстанция - это мнение юзера (как часто он тебя беспокоит),
и количество геморроя при сопровождении кода на форму.

В любом случае - делайте как знаете..перефразирую- как умеете (как привыкли).
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32222153
gardenman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
И ещё...
Если )) в MDI интерфейсе мы
1)одну и ту же запись открываем в двух окнах..
2)начинаем редактировать - то в одном окне, то в другом окне...

Вопросы:
1) А вообще стоит ли юзеру позволять делать это? (чего он там навводит интересно? и кто потом будет это разгребать?)
2) Если в две формы ввода привязаны к одной группе буферных переменных,
то изменения в обоих вормах по идее должны происходить синхронно вне зависимости от того, в какую из форм осуществляется ввод.
Это конечно можно реализовать, но стоит ли усложнять код ради этого?
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32222196
Mik Prokoshin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2gardenman:
>1) В этом варианте архитектура Doc/View - нафиг не нужна. ИМХО Doc/View
была не для этого придумана. Она заточена на операции с файловыми документами. конечно можно ее прикрутить и к базам данных - но выглядит это уродливо.

А как Вы проектируете базу ? Не опираясь на документы (объективные понятия предметной отрасли) ?

>2) должно быть так -> Форма ввода данных для выполнения транзакции -> выполнение транзакции.

И чем это несогласуется с doc/view ? Примитивно говоря - случай с одним view. Но как правльно сказал olk это - всего лишь простейший случай.

>3) С этой стороны абсолютно без разницы какое это приложение - MDI или SDI интерфейсом.

Ну, я вроде привел пример из жизни в посте выше... Что еще сказать...

>4) Каждый элемент управления должен самостоянельно обрабатывать и сохранять данные ввода в буферных переменных, и DDX - нафиг не нужен.

olk уже ответил, хотя мне непонятно, я сколько видел примеров работы с БД на VC - всюду DDX использовали и не жаловались.

>5) В порме ввода часто встречаются зависимае поля. Например не выбрав валюту, глупо вводить курс, или не выбрав отдел - невозможно выбрать человека в отделе. С этой точки зрения активация полей в форме должна проходить в строго определенном порядке. А то, что предлагает Windows - для таких целей не подходит.

Порядок - это переход фокуса. Контрол же просто выбирает/позволяет ввести данные с учетом определенных условий (Курс валют - можно ввести, например, сначала, а потом установить вид валюты - это проверка уровня формы, а человека в отделе когда выбираешь - формируется список людей в установленном отделе. Нет отдела - нет списка). Это то, что предлагает windows.

>Вывод, если хочешь чтоб всё работало как надо - делай все сам.

Угу. И MFC и VCL и .NET - это от лукавого. И вообще СУБД свою надо рисовать...
Надо просто соображать что где и когда применять.
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32222226
Mik Prokoshin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
И уж сразу еще вдогонку 2gardenman по свежим постам:
>> "вид" соответсвено считывая текущее состояние документа может
>>енэйблить или дизайблить соответсвующие контролы
>Аха...я тоже так раньше делал.
>Но уж очень длинный и некрасивый код получается.

Чем длинный и некрасивый ? В форме ? Так ведь все равно где-то придется писать код открытия/закрытия контролов. Примеры - в студию...

> И ещё...
>Если )) в MDI интерфейсе мы
>1)одну и ту же запись открываем в двух окнах..
>2)начинаем редактировать - то в одном окне, то в другом окне...

Обычно это делается - не для случая двойного редактирования одной записи. Можно этот момент запрещать в программе, можно - нет. Вы никогда не видели ситуации, когда менеджеру надо заниматься 3-10 делами сразу ?

>Вопросы:
>1) А вообще стоит ли юзеру позволять делать это? (чего он там навводит интересно? и кто потом будет это разгребать?)
В журнале отмечен оператор/юзер, редактировавший запись ? Кто еще разгребать должен ? Для каждого уровня пользователя - свой интерфейс и Вы говорите о базовом уровне, а мы с olk - о более продвинутом.

>2) Если в две формы ввода привязаны к одной группе буферных переменных,
то изменения в обоих вормах по идее должны происходить синхронно вне зависимости от того, в какую из форм осуществляется ввод.
Это конечно можно реализовать, но стоит ли усложнять код ради этого?
Это - один документ и несколько view. В чем усложнение кода ? Все делается автоматом.
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32222261
gardenman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну, на счет того, как я проектирую базы даннных))
я иду не от документов а от сущностей)
В одном документе может быь много сущностей,
а документ - устанавливает связи между ними.
Одна сущность - может входить в несколько документов.
И оперировать в базе данных нужно )) ИМХО не документами,
а сущностями (понятиями) С этой стороны одна форма ввода - одна сущность.
Изменение одной сущности - одна транзакция и т.д.
И документ должен достаточно жестко устанавливать связи между сущностями
(расшировываю - ссылочная/декларативная целостность)

А порядок во вводе формы - должен быть)
1) чтоб юзер не забыл ввести что-нить важное
2) чтоб уменьшить количество "тупых" запросов к базе например:
если не указан отдел, может показать все 25 тысяч сотрудников организации?

Вот действительно, к примеру, отдел кадров:
25 тысяч работающих. Куча отделов, департаментов, филиалов...
Постоянные перемещения сотрудников, совмещение должностей,
прием на работу-увольнение...
Скажите мне пожалуйста, где тут документ? а где его представление?
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32222266
olk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Тоже добавлю
Как я убедился на практике, архитектуру Doc/View часто недооценивают,
даже во многих "умных" книжках, всю обработку и хранение данных
предпочитают делать в "виде" , оставля документ практически пустым
(... ну типа просто его визард сгенерил - а так вроде он и нафиг не нужен .... только что бы темплейт можно было зарегестрить),
попробуйте все же перенести
хранение данных документа и его состояние на CDocument -
и я уверен вам понравиться и логика приложения на самом деле окажется проще и изменять код будет куда легче ...
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32222297
olk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2 gardenman:
о пока писал еще пост появился

Не надо понимать так буквально, что "документ" - это аналог какого либо реального документа ... это может быть и ваша сущность ...

Вот действительно, к примеру, отдел кадров:
25 тысяч работающих. Куча отделов, департаментов, филиалов...
Постоянные перемещения сотрудников, совмещение должностей,
прием на работу-увольнение...
Скажите мне пожалуйста, где тут документ? а где его представление?


Стандартная задача, нормально вписывающаяся в концепцию Doc/View
Навскидку :

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
 
CShtatDoc : public CDocument // штатное расписание
CDepatmentDoc : public CDocument // Справочник подразделений (возможно  "деревянный" )
CPersonDoc : public CDocument // Справочник работников
СDoc: public CDocument          // документы изменения по кадрам (приказы на перемещение, принятия на раб., увольнение)
CPr1: public CDoc // приказ о приеме
....
  ну и т.д. 
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32222310
gardenman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
)))Уважаемый olk
Скажите пожалуйста, и сильно вам помогает CDocument в вашем случае?
Вы же максимум 5% от всех зашитых в него возможностей используете!
А остальные 95 - дописываете сами)
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32222315
gardenman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Да, и еще замечание,а как же связи между документами?
Появился приказ о назначении - изменился состав подразделения...
Получается должны быть какие-то отношения между документами....
а как их реализовать?
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32222352
Mik Prokoshin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вообще-то отношения между документами зависят от конкртного случая, но стандартный вариант отношения один-ко-многим это просто ссылка на другой документ (FK-PK связка). View, в свою очередь, по данному полю FK может выбирать, скажем для lookup'а, значения по связи FK нашего документа->PKId справочника и брать из справочника Number+Name для примера... Все стандартно.
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32223038
Фотография vdimas
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
to void_123:
это шутка????!!!
цитирую:

void CMyApp:: ClearControls()
{
m_pMainDialog.ClearControls();
m_pTreeView.ClearControls();

m_pParent1.ClearControls();
//…
//…
m_pParentXXX.ClearControls();
//и т.д.
}
// А затем КАЖДЫЙ из родителей перечисляет
void CParent1:: ClearControls()
{
m_pChield1. ClearControls();
m_pChield2. ClearControls();
//…
//…
m_pChieldXXX. ClearControls();
//и т.д.
}
У меня в енжине есть один виртуальный метод - OnBinding()

типа:
<code>
void CEqmManager::OnBinding()
{
ctl_lastEntered = new CWSEdit("LASTENTEREDCONTROL_NU", IDC_LASTENTERED_CTRLNU, IDC_CAP_LASTENTERED_CTRLNU, this);
ctl_maximum = new CWSEdit( "MAXIMUMCONTROL_NU", IDC_MAXIMUM_CTRLNU, IDC_CAP_MAXIMUM_CTRLNU, this);
ctl_ctlnu = new CWSMaskEdit("CONTROL_NU", IDC_CONTROL_NU, IDC_CAP_CONTROL_NU, this);
ctl_serial = new CWSMaskEdit("SERIALNO", IDC_SERIAL_NUMBER, IDC_CAP_SERIAL_NUMBER, this);
ctl_manuf = new CWSComboEdit("MANUFACTURER", IDC_MANUFACTURER, IDC_CAP_MANUFACTURER, this);

...
}
</code>

Практически все! Форма с данными полноценно работает, потому как весь механизм реализуется в базовых классах. Все эти заполнения, прочистки, возврат предыдущего значения по undo, выделение цветом значений, отличающихся цветом от дефолтовых и еще миллион мелочей реализованы в енжине!!! На формах с данными я занимаюсь только логической частью - типа прореагировать на клик на кнопке, или дополнительно проверить пару контролов перед сохранением. Никакой такой ручной муторней я не могу себе позволить заниматься, т.к. в проекте уже около 250 форм, в среднем по 50-70 дата-контролов на каждой.

То, что тут обсуждается - это как лучше "ручками" все делать. (Я чуть не упал, когда читал, тут ваще кто-нить более-менее большие проекты делал?)
Берешь, делаешь чисто ООП-енжин, наследуешь все свои дата-формы от некоторой базовой, используешь контролы, которые знают, как с формой "правильно" работать. Единственно что требуется - это в момент создания формы "положить" соотв. елементы управления на шаблон диалога или view, это делается в переопределенном OnBinding(). Если пойти чуть дальше, и не пользоваться ресурсами диалогов, а написать свою "рисовалку" дата-форм, и сохранять это как свой custom-ресурс, то вообще и OnBinding не нужен - просто можно было бы указать ресурс и все. Все! А на самой дата-форме, вместо того, что бы заниматься "чернорабочими" делами, заняться бы лучше чем-нить полезным.

Если использовать BCB, то такую фигню надо было сделать в виде компонента - контейнера, представляющего из себя дата-форму, и полностью инкапсулирующего все системные алгоритмы. И плюс - компоненты дата-контролы, которые можно бросать на эти дата-формы, и которые умеют "правильно" с ней работать.

А наследоваться от TForm - ... Ничем не лучше, чем от TObject в данном случае...
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32223091
olk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сильно напрягла работа ... поэтому не было время для ответов ...

Ветка ушла далеко от первоночального топика но все же осталась достаточно интересной ....
Давйте посмотрим что мы имеем ...

1. "Горизонтальный" подход (подход void_123)- когда некотое "представление" само за себя отвечает и вроде как имеет способы общения через контролы общаться с БД и другими представления ... т.е. есть некая форма которая посредство инкапсулируемых (и/или наследуемых методов) очищает , заполняет поля, верифицирует, порождает обмен с БД и т.д ...
2. Подход некоего енжина (подход vdimas) - т.е. универсального "компонента"
реализованого видимо в некоторой ирархии классов , наследуя от этих классов получем некоторый механизм работы с формами (правда при этом маленько непонятно где хранятся обрабатываемые данные ? ну это вопрос реализации (кстати все же наверное интесно было бы взглянуть, емайл в профиле) ...
3. Традиционный - мухи отдельно .. катлеты отдельно - ну это я про себя
с использованием уже годами наработанного механизмов МФС Док/Вью

В принципе не кто наверное не против, что любой подход имеет право на жизнь, при этом я предпологаю, что уровень сложности разрабоки наверное будет примерно одинаковый (я имею в виду разработку начальных "компонент",механизмов, ирархии класов + отладка (поиск багов ,несоответствий (полная переработка ирархии ) и т.д. + само приложение) ..., опять-же наверное любо программист согласится, что разрабатывать приложение на своей ирархии классов, гораздо проще (просто вследствии того, что ты всегда можешь (скрипя зубами ) ,что то в этой ирархии классов изменить в угоду текущему приложению ... ну и конечно потому что ты ее знаешь досконально ...

Основной критерий (ИМХО) сорость и качество разработки приложения ...
и как раз в этом по моему основное противоречие ...
Например VC очень много кода приходиться писать руками (скорсоть -, качество + )
Builder C++ - быстрая разработка, качество - , иногда очень сложно получить имено то чего хочется (может это плохое знание инструмента - вполне может быть ... хотя сидел на нем более 2-х лет)

Скажу сразу в МФС-е меня тоже не все устраивает ...
Что бы не быть голословным скажу что опыт программировния на Плюсатом у меня (дай бог памяти) с 85 года !
При этом были периоды использования
Watcom C++
IBM Visual Age C++
Borland C++
Builder C++
VC++

в идеале, что я вижу для себя (кстати немного похоже на подход vdimas) я уже где то постил

При этом в качестве конечного инструмента в идеале я представляю
некоторый визард, создающий некоторый ресурс (причем динамический настраиваемый пользователем из приложения, в виде некоторых метаданных - определенных на пространстве рабочей станции+эккаунта с некоторыми предопределенным значением (причем в иделе это можно хранить или в регистах или в файле или даже в СУБД) с описанием формы (вьюшки), диалога (совокупности диалогов) .
В общем случае (что бы было понятно о чем я говорю) для примера ,
приложение привязано к некоторой (некоторым) CDatabase (ODBC,DAO ...),
форма привязано к некторому анонимному CRecordset (т.е. SQL - запрос берется из метаданных) и далее все контролы, гриды, комббобоксы и т.д. (их расположение (количество полей (размеры),расположение в гридах) и привязка берется так же из метаданных)... ну это в идеале


понятно что я не задал ни каких вопросов ... но с другой стороны я не совсем понмаю зачем изобретать велосипед ? для меня например уже давно стало однозначно ... хочешь быстро и красиво - испоьзуй VCL - Инпрайс (Борланд), хочешь дотошно и качествено VC+MFC, хочешь гимороя - начинай с нуля

может я кого то конечно не правильно понял ??
ну так что продолжим ... ?
...
Рейтинг: 0 / 0
Сообщения пользователя
    #32223582
void_123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Прошу меня извинить, что отвечаю по порядку всем сразу.

//////////////

to olk

>> Вообще то всегда считал, что Doc/View - это дальнейшее абстагирование ООП

Я бы сказал попытка мелкософт. Продукт коммерческий. Ну должны ж они были хоть шото дать визардовое да ещё с «абстракциями». ИМХО тоже касается DDX.
>> кхм и как-же он должен его обрабатывать ? а если я нажал кнопку отмена, контрол поля ввода >>сам должен вернуть предыдущее значение в свою буферную переменную ?

Сам программер должен написать код для обмена данными между гляделко-печаталками и контролами и буферами.


>> и чем вам ддх не угодил ? это же просто механизм (просто более универсальный) сохранения контролами своих значений в буферных переменных ...

Потому, что в реальных приложениях БД структура буферов значительно сложнее одной переменной или их простой совокупности (списка или перечисления). Как врач говорю, а не как практикующий программер БД.

>> посылается UpdateAllView - "вид" соответсвено считывая текущее состояние документа может енэйблить или дизайблить соответсвующие контролы

Эта архитектура не единственная. Очень прязана к МФЦ. Что делать, если программируем с использованием гольного API?

///////////////////////////////////////////////////////////////////////////////////////////////


To Mik Prokoshin

>> …оформить результат в виде готовых компонент/библиотеки классов и закинуть на какой-нибудь…

Если речь и не идёт о разработке архитектуры, то идёт об отказе от стандартной архитектуры, в том числе и от документ-вид, и о том, как это сделать с меньшими «нарушениями правилами уличного движения ООП».

>>Из Вашего первого поста :
>Удобство заключается в том, что всё управление отображением и заполнением контролов (enable, visible и т.д) сосредоточено в одном месте. Вернее сосредоточена общая логика приложения в части управления контролами

Да это было в посте. Там это для затравки, масло в огонь.


>> … Просто надо было сразу декларировать четко цели.

Вы правы.

>>….Если мне надо передать сообщение для группы контролов - я передаю сообщение их общему >>родителю, который и обрабатывает его, модифицируя детишек. Это можно выносить в >>отдельный шаблон, как предложено, но IMHO объем работы практически сопоставим

Вы правы, если мы обсуждаем одно и тоже. Впрочем, Вы можете быть правы в любом случае. На одном из форумов в развитие этой архитектуры предлагается отнаследоваться всем заинтересованным классам, в том числе аппликации, от месангера. Тогда, такой класс можно рассматривать в качестве контейнера-посредника (медиатора), который будет рассылать мессаги объектам-подписчикам. Тупой пример, хотя там это и не надо, - CTabPageControl:CTabCtrl:CMessanger. В этом примере диалоги пейжей могут быть подписчиками медиатора CTabPageControl. Пример тупой, т.к. для табов это решается проще. Вы об этом? Если, да то я об этом думаю.

>> Надо сразу четко оговаривать область применения. А то - сделали что-то и стали решать "вещь получилась хорошая, но куда прикрутить никак не поймем" :-)

Я уже выше попросил меня простить.
>>Просто эти идеи (в данном случае) - реализация старых шаблонов. (без обид)

Без обид. Тем более, что верно. Это было реализовано, правда в более могутном масштабе, на plain-C в начале 90. Не помню когда точно. Тогда не было виндовз, а не то чтобы хотелось, просто досовы подзадачи требовали GUI (делали самопальный ГИС). Но там было всё сложнее, поскольку тулз создания абстакций приходилось моделировать на С вместо использования языковых средств С++ описания абстракций.

///////////////////////////////////////////////////////////////////////////////////////////////

To gardenman

>>1)одну и ту же запись открываем в двух окнах..
>>2)начинаем редактировать - то в одном окне, то в другом окне...

Что здесь сказать…

>> Это конечно можно реализовать, но стоит ли усложнять код ради этого?

Задачи бывают разные. Но усложнять, думаю, не стоит. Это (выше) не усложнение, а прямой путь к краху проекта при его модернизации, наращивании и т.д., ИМХО.


///////////////////////////////////////////////////////////////////////////////////////////////

To Mik Prokoshin

>>А как Вы проектируете базу ? Не опираясь на документы (объективные понятия предметной отрасли) ?

Для данных создаётся свой класс, который умеет читать, писать и т.д. Объект такого класса и есть реализация буфера.


>>olk уже ответил, хотя мне непонятно, я сколько видел примеров работы с БД на VC - всюду DDX использовали и не жаловались.

ИМХО просто это лишнее, т.к., например, по нажатию кнопки «Сохранить» всё равно придётся «переносить» данные в буфер, возможно сложной структуры, а только потом вливать из БУФЕРА в базу. Если этого не сделать, то затруднительно нормально обработать серию транзакций.


///////////////////////////////////////////////////////////////////////////////////////////////

To gardenman


>>я иду не от документов а от сущностей)

Прально гришь.


>>Скажите пожалуйста, и сильно вам помогает CDocument в вашем случае?
>>Вы же максимум 5% от всех зашитых в него возможностей используете!
>>А остальные 95 - дописываете сами)

У меня другой вопрос. Какие вообще есть методы класса CDocument для работы с БД.
Что там «хорошо» на 1%? То что специализированный класс, да ещё умеет говорит UpdateAllViews? А свой класс, чем будет хуже? Тем, что не умеет говорить UpdateAllViews? И всё???

///////////////////////////////////////////////////////////////////////////////////////////////


To vdimas

>>to void_123:
>>это шутка????!!!
>>цитирую:

>>void CMyApp:: ClearControls()
>>{
>>m_pMainDialog.ClearControls();
>>m_pTreeView.ClearControls();

Это гипербола. Пример несколько теоретизированно-натянут.
Если класс контейнер умеет перечислять «детей», как например, CTabCtrl, то он сделает это.


///////////////////////////////////////////////////////////////////////////////////////////////


to olk


>>1. "Горизонтальный" подход (подход void_123)- когда некотое "представление" само за себя отвечает и вроде как имеет способы общения через контролы общаться с БД и другими представления ... т.е. есть некая форма которая посредство инкапсулируемых (и/или наследуемых методов) очищает , заполняет поля, верифицирует, порождает обмен с БД и т.д ...


Не сердитесь, но это очень поверхностный взгляд. Неверна даже терминология. Я понимаю, что мой стиль изложения оооооочень нудный. И оооочень тяжело прочитать. Даже мне самому;)))
...
Рейтинг: 0 / 0
37 сообщений из 37, показаны все 2 страниц
Форумы / C++ [игнор отключен] [закрыт для гостей] / Сообщения пользователя
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]