информационная безопасность
без паники и всерьез
 подробно о проектеRambler's Top100
Атака на InternetСетевые кракеры и правда о деле ЛевинаЗа кого нас держат?
BugTraq.Ru
Русский BugTraq
 Анализ криптографических сетевых... 
 Модель надежности двухузлового... 
 Специальные марковские модели надежности... 
 Три миллиона электронных замков... 
 Doom на газонокосилках 
 Умер Никлаус Вирт 
главная обзор RSN блог библиотека закон бред форум dnet о проекте
bugtraq.ru / форум / programming
Имя Пароль
ФОРУМ
все доски
FAQ
IRC
новые сообщения
site updates
guestbook
beginners
sysadmin
programming
operating systems
theory
web building
software
hardware
networking
law
hacking
gadgets
job
dnet
humor
miscellaneous
scrap
регистрация





Легенда:
  новое сообщение
  закрытая нитка
  новое сообщение
  в закрытой нитке
  старое сообщение
  • Напоминаю, что масса вопросов по функционированию форума снимается после прочтения его описания.
  • Новичкам также крайне полезно ознакомиться с данным документом.
[C++] Ого :-) 22.07.03 11:24  Число просмотров: 1573
Автор: amirul <Serge> Статус: The Elderman
<"чистая" ссылка>
> Замечу, что объекты указанных классов (а значит и
> соответствующие контролы) «НЕЗАВИСИМЫ» в том смысле, что
> они почти ничего не знают друг о друге (идеология MFC).
Нет, у MFC другая идеология. В плане абстракции данных он даже не совсем ООП, так как все данные держит открытыми - непозволительная вольность. Я не очень люблю MFC, но совсем за другие вещи.

На самом деле, MFC это библиотека классов, но только самые мелкие из них (контролы, например, или сокеты) используются в приложении в чистом виде. Класс приложения, класс диалога и др., используются как базовые. Поэтому ничто не мешает хранить там что угодно. Кроме того, Class Wizard предоставляет возможность вводить в эти самые создаваемые классы переменные для обращения к объектам (см "Дополнение к дополнению"). Есть один глобальный theApp. В нем можно хранить указатели на диалоги (но насколько я понял диалог один, так что этого не надо). В эти диалоги из ClassWizard-а подобавлять переменных, через которые потом можно будет обращаться к контролам (в частности CTabCtrl).

> чекбоксов или эдитов на закладке CPage2, т.к. она (кнопка)
> не знает даже указателя на диалог CPage2. При переводе
Но может узнать. С CPage-ами, как я уже говорил, немного сложнее, так как о них неизвестно в design time-е и приходится динамически сохранять/изменять эту информацию. Но и это не очень сложно, так как конструкторы/деструкторы создаваемых объектов всегда могут найти своего родителя (через глобальный theApp или это и есть сам theApp в случае диалогов), и изменять там каждый свое поле (указатель). А в случае CPage-ей и это не надо, так как система при добавлении итема в таб контрол позволяет передать один long - вполне достаточно для передачи указателя на объект-страницу. А таб контрол впоследствии в состоянии получить этот указатель.

> Вот дополнения к мотивации:
Мотивация вполне правильная на мой взгляд, но есть и еще одно. Ни один объект не должен ничего делать с объектами, непосредственным владельцем которого он не является. То есть, при нажатии на какую-то кнопку должно измениться состояние. Об этом пока что знает только кнопка. Она сообщает об этом приложению. Приложение переходит в нужное состояние и сообщает своим диалогам (переводит их). Диалоги переводят в свою очередь свои контролы (ну чтоб не наследоваться от CTabView можно сообщать всем страницам). А страницы уже непосредственно меняют состояние контролов у себя.

И кстати, если состояний не так много, не так уж нужно вводить функцию-диспетчер всех состояний. Гораздо прозрачнее и понятнее ввести функции OnStartup, OnClose, OnRead и т.д. (эти названия нужно понимать как: "действия, которые необходимо выполнить при переходе в такое-то состояние"), ну можно и так OnStartupState, OnCloseState, OnReadState - кому как понятнее. Они должны быть виртуальными. А следовательно можно будет немного переписать бизнес-логику просто отнаследовавшись и переопределив виртуальную функцию (в случае с громадным case-ом это не так просто, особенно если эти case будут иерархически распределены, как я сказал выше).

> xxx.FillControls(UM_STARTUP);
> xxx.DisplayControls(UM_STARTUP);
Я кстати не совсем понял, зачем эти две функции разделены, если они всегда идут парой.


Мой вариант:
// стартуем приложение MFC VC++ 
BOOL CGkmApp::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 просмотра базы данных 
} 

---


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

> Думаю, масло следует подливать понемногу, шаг за шагом.
> В предыдущем посте предложено тупое решение, когда
> указатели на все требуемые объекты, например, диалоги
> Page1,…, Page20 объявлены extern. На мой взгляд, это не
> такой уж плохой путь для простых по логике GUI. Так, что в
> простых случаях я так и буду делать, и других наворотов
> (см. далее шаг 1 и т.д.) мне и не надо.
>
> Шаг 1.
> Для более сложных приложений, если мы хотим «большой
> беспорядок привести к нескольким маленьким беспорядкам»,
> заметим, что двадцать глобальных указателей – прямо скажем,
> не очень изящно. Ну, добавим все указатели в некий массив.
> Необходимость в таком массиве, вернее специальном классе
> CMessanger (язык не поворачивается сказать элементе
> паттерна) дополнительно обоснована далее, см. шаг 2. В
> функциях FillControls, DisplayControls нам нужно будет
> уметь извлекать из массива указатели на диалоги по
> некоторым символическим идентификаторам, которые мы знаем
> (сами задаём при добавлении указателя на объект в массив).
> Поэтому в элементах массива будем хранить указатели вместе
> с идентификаторами.
[skipped]
ИЗВРАТ!!! %-) :-)

Я же говорю. С CPage-ами надо поступать проще:

class CPage: public CDialog {
public:
virtual void OnStartup() = 0;
virtual void OnRead() = 0;
virtual void OnEdit() = 0;
// ...
}

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

class CPage2: 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();
    }
// ...
}

// В сгенеренный ClassWizard-е класс CGkmApp вручную добавить диалог
// Или найти способ получать этот диалог динамически - я не слишком люблю
// MFC, поэтому не сильно с ним разбирался
class CGkmApp: public CWinApp {
// ...
CMainDialog *m_pMainDlg;
//...
}

// Все остальное в том же духе. Даже если в MFC нет абстракции данных,
// ее можно ввести искусственно - просто не использовать чужие данные напрямую.

---

> Однако часто объекты проекта по ЛОГИКЕ ПРИЛОЖЕНИЯ
> представляют независимые ГРУППЫ, сообщения которых должны
> обрабатывать различные обработчики FillControls,
> DisplayControls. Например, диалоги и виды, описанные выше –
Вот поэтому, нужно чтоб объект делал только то что знает как делать точно. То бишь приложение переводит в новое состояние своих детей, диалог - своих, а страницы просто переводят контролы в нужное состояние. А использование виртуальных функций при этом помогает отделить интерфейс от реализации.

> Ещё раз благодарю всех тех, кто откликнулся. Обещаю, что
> дальше не буду писать столь пространно и столь длинные
> посты. Это было необходимо в настоящем постинге, т.к. далее
> я буду только ссылаться на предыдущие свои постинги. Если,
> конечно, дискуссия состоится.
Да ничего. Модераторов тут много. Если что - амнистируем :-)
<programming> Поиск 






Rambler's Top100
Рейтинг@Mail.ru


  Copyright © 2001-2024 Dmitry Leonov   Page build time: 0 s   Design: Vadim Derkach