Мысль о GUI
От: Lonely Dog Россия  
Дата: 07.06.06 22:42
Оценка:
Привет!

Родилась мысль о том, как можно упростить реализацию GUI, конкретнее, хочется заставить систему саму отсжеливать состояния контролов. (enabled/disabled, visible, и пр)
Поясню на примере: пусть у нас есть диалог смены пароля. В этом диалоге есть поле ввода нового пароля и поле его подтверждения.
Кроме того, на диалоге есть кнопки OK и Cancel. Если поле вводе нового пароля пустое, кнопка OK должна быть в состоянии disabled. Если строка введенная в поле ввода нового пароля не равна строке в поле подтверждения, то OK также должна быть в состоянии disabed. Кроме того, пусть на диалоге будет текстовое поле с подсказкой. Если поле нового пароля пустое, подсказка будет отображать строку "пароль не может быть пустым". Если новый пароль и его подтверждение не совпадают, подсказка будет отображать строку "пароли не совпадают." Хочется описывать большую часть этого поведения декларативно, на уровне некой карты поведения диалога. (как карта сообщений в WTL или MFC.) Не очень понятно, как это сделать. Пока я думаю в сторону конечных автоматов, но возможно вы сможете подсказать что-нибудь еще?

Заранее спасибо.

PS: Есть мысль развить эту идею до уровня библиотеки. Сам я пишу на WTL, т.ч. первая версия будет для нее. В дальнейшем, можно будет сделать и для остальных библиотек (и языков).

16.06.06 11:47: Перенесено из 'Алгоритмы'
Re: Мысль о GUI
От: Miroff Россия  
Дата: 08.06.06 04:58
Оценка:
Здравствуйте, Lonely Dog, Вы писали:

LD>Кроме того, на диалоге есть кнопки OK и Cancel. Если поле вводе нового пароля пустое, кнопка OK должна быть в состоянии disabled. Если строка введенная в поле ввода нового пароля не равна строке в поле подтверждения, то OK также должна быть в состоянии disabed. Кроме того, пусть на диалоге будет текстовое поле с подсказкой. Если поле нового пароля пустое, подсказка будет отображать строку "пароль не может быть пустым". Если новый пароль и его подтверждение не совпадают, подсказка будет отображать строку "пароли не совпадают." Хочется описывать большую часть этого поведения декларативно, на уровне некой карты поведения диалога. (как карта сообщений в WTL или MFC.) Не очень понятно, как это сделать. Пока я думаю в сторону конечных автоматов, но возможно вы сможете подсказать что-нибудь еще?


Таблицы решений, например. Для простых диалогов неплохо подходят, для сложных применять не доводилось.
Re: Мысль о GUI
От: Sergey640  
Дата: 08.06.06 07:13
Оценка:
Здравствуйте, Lonely Dog, Вы писали:

LD>Привет!


LD>Родилась мысль о том, как можно упростить реализацию GUI, конкретнее, хочется заставить систему саму отсжеливать состояния контролов. (enabled/disabled, visible, и пр)

LD>Поясню на примере: пусть у нас есть диалог смены пароля. В этом диалоге есть поле ввода нового пароля и поле его подтверждения.
LD>Кроме того, на диалоге есть кнопки OK и Cancel. Если поле вводе нового пароля пустое, кнопка OK должна быть в состоянии disabled. Если строка введенная в поле ввода нового пароля не равна строке в поле подтверждения, то OK также должна быть в состоянии disabed. Кроме того, пусть на диалоге будет текстовое поле с подсказкой. Если поле нового пароля пустое, подсказка будет отображать строку "пароль не может быть пустым". Если новый пароль и его подтверждение не совпадают, подсказка будет отображать строку "пароли не совпадают." Хочется описывать большую часть этого поведения декларативно, на уровне некой карты поведения диалога. (как карта сообщений в WTL или MFC.) Не очень понятно, как это сделать. Пока я думаю в сторону конечных автоматов, но возможно вы сможете подсказать что-нибудь еще?

LD>Заранее спасибо.


LD>PS: Есть мысль развить эту идею до уровня библиотеки. Сам я пишу на WTL, т.ч. первая версия будет для нее. В дальнейшем, можно будет сделать и для остальных библиотек (и языков).


1. Думаю, Ваш пример для автомата с одним состоянием (состояния controls я считаю внешними воздействиями).

2. Есть опасность появления циклов в графе состояний автомата. Это может привести к зависанию.
Re: Мысль о GUI
От: Кодт Россия  
Дата: 08.06.06 09:21
Оценка:
Здравствуйте, Lonely Dog, Вы писали:

LD>Родилась мысль о том, как можно упростить реализацию GUI, конкретнее, хочется заставить систему саму отсжеливать состояния контролов. (enabled/disabled, visible, и пр)


В MFC для тулбаров (в том числе с произвольными контролами) и меню эта штука уже существует — смотри ON_UPDATE_COMMAND_UI.
Теоретически, можно прикрутить его и к диалогам. (Не пробовал).
Перекуём баги на фичи!
Re[2]: Мысль о GUI
От: Lonely Dog Россия  
Дата: 08.06.06 11:28
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, Lonely Dog, Вы писали:


К>В MFC для тулбаров (в том числе с произвольными контролами) и меню эта штука уже существует — смотри ON_UPDATE_COMMAND_UI.

К>Теоретически, можно прикрутить его и к диалогам. (Не пробовал).
Правильно ли я понимаю, что в этом случае, в обработчике ON_UPDATE_COMMAND_UI для кнопки OK я должен написать примерно следующее:

if (strNewPassword.GetLength() && (strNewPassword == strNewPasswordConfirm))
  wndOK.EnableWindow(TRUE);
else
  wndOK.EnableWindow(FALSE);


Мне бы хотелось бы несколько другого поведения. Например, зачастую кнопка OK должна быть запрещена, пока не будут введены все данные. Следовательно, можно сказать, что кнопку OK мы разрешаем, когда все edit-box диалога содержат какие-то данные. По идее, можно привязать изменения всех полей ввода к установке неких флагов, а потом сказать, что если все флаги равны TRUE, то кнопку OKразрешаем. Моя идея заключалась в том, чтобы выделить те ситуации, когда некие контролы изменяют свое состояние в ответ на изменение состояние неких других контролов диалога. Возможно, для каждой ситуации можно написать некий универсальный кусок кода, который можно будет использовать в дальнейшем.
Re[2]: Мысль о GUI
От: Lonely Dog Россия  
Дата: 08.06.06 11:31
Оценка:
Здравствуйте, Sergey640, Вы писали:

S>1. Думаю, Ваш пример для автомата с одним состоянием (состояния controls я считаю внешними воздействиями).

Почему? Насколько я понимаю, есть три состояния:
1. пароль не введен
2. пароль введен, но не равен подтверждению.
3. пароль введен и равен подтверждению.
В первом и втором состоянии кнопка OK должна быть запрещена. В третьем разрешена.

S>2. Есть опасность появления циклов в графе состояний автомата. Это может привести к зависанию.

Да, наверное вы правы. Это надо будет как-то учитывать.
Re[2]: Мысль о GUI
От: Lonely Dog Россия  
Дата: 08.06.06 11:32
Оценка:
Здравствуйте, Miroff, Вы писали:

M>Таблицы решений, например. Для простых диалогов неплохо подходят, для сложных применять не доводилось.

А не могли бы вы рассказать, что это такое? Или где про это можно почитать?
Re[3]: Мысль о GUI
От: Miroff Россия  
Дата: 08.06.06 11:54
Оценка: 2 (1)
Здравствуйте, Lonely Dog, Вы писали:

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


M>>Таблицы решений, например. Для простых диалогов неплохо подходят, для сложных применять не доводилось.

LD>А не могли бы вы рассказать, что это такое? Или где про это можно почитать?

http://en.wikipedia.org/wiki/Decision_table
Re[3]: Мысль о GUI
От: Аноним  
Дата: 08.06.06 13:29
Оценка: +1
Здравствуйте, Lonely Dog, Вы писали:


В итоге все выльется в ON_UPDATE_COMMAND_UI или аналоги, которые универсальные,
но все равно требуют ручной записи условий.
Проблема в том, что условия то как раз совсем не уверсальные.
Для кнопки OK при вводе пароля нужны другие условия, чем для той же кнопки на другом диалоге.
Re[3]: Мысль о GUI
От: Sergey640  
Дата: 08.06.06 13:46
Оценка:
Здравствуйте, Lonely Dog, Вы писали:

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


S>>1. Думаю, Ваш пример для автомата с одним состоянием (состояния controls я считаю внешними воздействиями).

LD>Почему? Насколько я понимаю, есть три состояния:
LD>1. пароль не введен
LD>2. пароль введен, но не равен подтверждению.
LD>3. пароль введен и равен подтверждению.
LD>В первом и втором состоянии кнопка OK должна быть запрещена. В третьем разрешена.

Для автомата Мура — правда Ваша , для автомата Мили — моя.
Все дело в реализации. Флажок "Disabled" я склонен рассматривать как внешний, т.е. Какая-то ON_CHANGE function вызывается , проверяет состояние Controls устанавливает флажок и все. Дополнительных переменных состояния нет. Функция оценки и изменения флажка — одна для всех. Мне так кажется.

Автомат Мура будет дублировать в своих состояниях состояние флажка: добавляется переменная состояния, появляются функции переходов — это более громоздко для данного примера. Но для более сложных случаев, когда требуется помнить историю действий, это возможно и подойдет.
Правда автомат типа Мили обычно всегда меньше — я исходил из этого.
Re[4]: Мысль о GUI
От: Sergey640  
Дата: 08.06.06 13:53
Оценка: 2 (1)
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, Lonely Dog, Вы писали:



А>В итоге все выльется в ON_UPDATE_COMMAND_UI или аналоги, которые универсальные,

А>но все равно требуют ручной записи условий.
А>Проблема в том, что условия то как раз совсем не уверсальные.
А>Для кнопки OK при вводе пароля нужны другие условия, чем для той же кнопки на другом диалоге.

Резонно и серьезно, настолько , что может похоронить идею на корню. Лучше начать с проработки условий и способов их простого задания. Например установления связей между контролами и определения их характеристик типа "когда первый пустой — второй disabled". "когда первый равен второму — третий показывает prompt1" — Что-то сложно получается.
Re[3]: Мысль о GUI
От: Кодт Россия  
Дата: 08.06.06 13:55
Оценка: 2 (2)
Здравствуйте, Lonely Dog, Вы писали:

LD>Правильно ли я понимаю, что в этом случае, в обработчике ON_UPDATE_COMMAND_UI для кнопки OK я должен написать примерно следующее:

LD>if (strNewPassword.GetLength() && (strNewPassword == strNewPasswordConfirm))
LD>  wndOK.EnableWindow(TRUE);
LD>else
LD>  wndOK.EnableWindow(FALSE);

Достаточно
wndOK.EnableWindow( !strNewPassword.IsEmpty() && strNewPassword == strNewPasswordConfirm );


LD>Мне бы хотелось бы несколько другого поведения. Например, зачастую кнопка OK должна быть запрещена, пока не будут введены все данные. Следовательно, можно сказать, что кнопку OK мы разрешаем, когда все edit-box диалога содержат какие-то данные. По идее, можно привязать изменения всех полей ввода к установке неких флагов, а потом сказать, что если все флаги равны TRUE, то кнопку OKразрешаем. Моя идея заключалась в том, чтобы выделить те ситуации, когда некие контролы изменяют свое состояние в ответ на изменение состояние неких других контролов диалога. Возможно, для каждой ситуации можно написать некий универсальный кусок кода, который можно будет использовать в дальнейшем.


По сути, мы имеем функцию U: (набор значений V -> набор состояний S контролов). Правильно?

Подход с UpdateCmdUI сводится к тому, что эта функция раскладывается на набор функций u[k] : (V -> состояние контрола s[k]). Эти функции реализуются в обработчиках сообщений WM_UPDATE_COMMAND_UI.
Причём моменты вычисления (и, соответственно, обновления состояний) произвольны. Можно в любой момент сказать, какое сейчас должно быть состояние, вне зависимости от истории. И в любой момент инициировать обновление.

А твоя идея (автомат переходов), как я понимаю, состоит в нахождении инкрементной функции dU: (dV -> dS), раскладываемой по изменениям значений: du[k]: (изменение одного параметра dv[k] -> смена состояний dS).

Сложность здесь в том, что
1) Операции изменения должны образовать коммутативную группу, т.е. правило ромба:
S0 -(dv1)-> S1 -(dv2)-> S12
S0 -(dv2)-> S2 -(dv1)-> S21 // исправил очепятку
S1 == S2
Отдаст ли программист этот долг — зависит от кривизны его рук. Есть риск ошибиться.
2) Одновременное изменение нескольких значений сразу (например, обновление данных по таймеру) нужно или самостоятельно обработать (предусмотрев все комбинации), причём непротиворечиво с элементарными переходами, или же разложить на последовательность элементарных переходов.
В последнем случае есть шанс для мелькания (например, кнопка видима, если включено чётное число чекбоксов).

Так что, ИМХО, проще написать функцию состояния U(V->S) и отрабатывать её в моменты переходов.
Перекуём баги на фичи!
Re[5]: Мысль о GUI
От: Lonely Dog Россия  
Дата: 13.06.06 14:07
Оценка:
Здравствуйте, Sergey640, Вы писали:

S>Резонно и серьезно, настолько , что может похоронить идею на корню. Лучше начать с проработки условий и способов их простого задания.

Вот-вот. Именно это я и хочу сделать.
Re[4]: Мысль о GUI
От: Lonely Dog Россия  
Дата: 13.06.06 14:11
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Достаточно

К>
К>wndOK.EnableWindow( !strNewPassword.IsEmpty() && strNewPassword == strNewPasswordConfirm );
К>

Это не принципиально. Я предпочитаю в случае сложных условий использовать if/else.

LD>>Мне бы хотелось бы несколько другого поведения. Например, зачастую кнопка OK должна быть запрещена, пока не будут введены все данные. Следовательно, можно сказать, что кнопку OK мы разрешаем, когда все edit-box диалога содержат какие-то данные. По идее, можно привязать изменения всех полей ввода к установке неких флагов, а потом сказать, что если все флаги равны TRUE, то кнопку OKразрешаем. Моя идея заключалась в том, чтобы выделить те ситуации, когда некие контролы изменяют свое состояние в ответ на изменение состояние неких других контролов диалога. Возможно, для каждой ситуации можно написать некий универсальный кусок кода, который можно будет использовать в дальнейшем.


К>По сути, мы имеем функцию U: (набор значений V -> набор состояний S контролов). Правильно?


К>Подход с UpdateCmdUI сводится к тому, что эта функция раскладывается на набор функций u[k] : (V -> состояние контрола s[k]). Эти функции реализуются в обработчиках сообщений WM_UPDATE_COMMAND_UI.

К>Причём моменты вычисления (и, соответственно, обновления состояний) произвольны. Можно в любой момент сказать, какое сейчас должно быть состояние, вне зависимости от истории. И в любой момент инициировать обновление.
Да, логично.

К>А твоя идея (автомат переходов), как я понимаю, состоит в нахождении инкрементной функции dU: (dV -> dS), раскладываемой по изменениям значений: du[k]: (изменение одного параметра dv[k] -> смена состояний dS).

Класс!!!. Очень точная формулировка.

К>Сложность здесь в том, что

К>1) Операции изменения должны образовать коммутативную группу, т.е. правило ромба:
К>S0 -(dv1)-> S1 -(dv2)-> S12
К>S0 -(dv2)-> S2 -(dv1)-> S21 // исправил очепятку
К>S1 == S2
К>Отдаст ли программист этот долг — зависит от кривизны его рук. Есть риск ошибиться.
Не очень понял, что здесь имеется ввиду. Ты имеешь ввиду, что независимо от порядка применения правил, результат должен быть один и тот же?
К>2) Одновременное изменение нескольких значений сразу (например, обновление данных по таймеру) нужно или самостоятельно обработать (предусмотрев все комбинации), причём непротиворечиво с элементарными переходами, или же разложить на последовательность элементарных переходов.
К>В последнем случае есть шанс для мелькания (например, кнопка видима, если включено чётное число чекбоксов).
Я что-то не помню, чтобы мне приходилось писать что-то подобное. Но конечно, в этом случае, надо будет что-то придумать.
Re[5]: Мысль о GUI
От: Кодт Россия  
Дата: 14.06.06 08:30
Оценка:
Здравствуйте, Lonely Dog, Вы писали:

К>>Сложность здесь в том, что

К>>1) Операции изменения должны образовать коммутативную группу, т.е. правило ромба:
К>>S0 -(dv1)-> S1 -(dv2)-> S12
К>>S0 -(dv2)-> S2 -(dv1)-> S21 // исправил очепятку
К>>S1 == S2
К>>Отдаст ли программист этот долг — зависит от кривизны его рук. Есть риск ошибиться.

LD>Не очень понял, что здесь имеется ввиду. Ты имеешь ввиду, что независимо от порядка применения правил, результат должен быть один и тот же?


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

К>>2) Одновременное изменение нескольких значений сразу (например, обновление данных по таймеру) нужно или самостоятельно обработать (предусмотрев все комбинации), причём непротиворечиво с элементарными переходами, или же разложить на последовательность элементарных переходов.

К>>В последнем случае есть шанс для мелькания (например, кнопка видима, если включено чётное число чекбоксов).

LD>Я что-то не помню, чтобы мне приходилось писать что-то подобное. Но конечно, в этом случае, надо будет что-то придумать.


Значит, повезло
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Перекуём баги на фичи!
Re: Мысль о GUI
От: Lonely Dog Россия  
Дата: 15.06.06 20:18
Оценка:
Здравствуйте, Lonely Dog, Вы писали:

Родилась еще одна мысль по поводу представления карты поведения диалога. Можно вместо конечного автомата диалога указывать набор его инвариантов. Т.е., вместо постулирования "когда в текстовое поле будет что-то введено, кнопка OK должна стать Enabled.", мы говорим, что "Если в поле ничего не введено, то OK должна быть Disabled." В этом случае, можно ввести отношения "Если условие X, то контрол Y должен быть в состоянии Z" или "Контрол Y находится в состоянии Z тогда и только тогда когда условие X выполнено."
Далее останется написать только набор стандартных условий вида "Поле пусто", "В списке что-то выбрано", и набор функций, меняющих состояние объекта. Как вам такая формулировка?
Re[2]: Мысль о GUI
От: Кодт Россия  
Дата: 16.06.06 06:25
Оценка:
Здравствуйте, Lonely Dog, Вы писали:

LD>Здравствуйте, Lonely Dog, Вы писали:


LD>Родилась еще одна мысль по поводу представления карты поведения диалога. Можно вместо конечного автомата диалога указывать набор его инвариантов. Т.е., вместо постулирования "когда в текстовое поле будет что-то введено, кнопка OK должна стать Enabled.", мы говорим, что "Если в поле ничего не введено, то OK должна быть Disabled." В этом случае, можно ввести отношения "Если условие X, то контрол Y должен быть в состоянии Z" или "Контрол Y находится в состоянии Z тогда и только тогда когда условие X выполнено."

LD>Далее останется написать только набор стандартных условий вида "Поле пусто", "В списке что-то выбрано", и набор функций, меняющих состояние объекта. Как вам такая формулировка?

"Когда" — это переходы. "Если" — функции.
Ну а выбрать прямые или инверсные предикаты ("enabled если текст непуст" vs. "disabled если текст пуст") — дело хозяйское.
В одной GUI-библиотеке (Zinc) у контролов было свойство Disabled, по умолчанию false. Там удобнее было задавать условия от противного.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Перекуём баги на фичи!
Re: Мысль о GUI
От: Аноним  
Дата: 16.06.06 11:21
Оценка: 12 (1)
Здравствуйте, Lonely Dog, Вы писали:

LD>Привет!


LD>Родилась мысль о том, как можно упростить реализацию GUI, конкретнее, хочется

LD>...
LD>PS: Есть мысль развить эту идею до уровня библиотеки. Сам я пишу на WTL, т.ч. первая версия будет для нее. В дальнейшем, можно будет сделать и для остальных библиотек (и языков).

глянь на Adam и Eve в ASL здесь
Re: Мысль о GUI
От: vgrigor  
Дата: 19.06.06 07:13
Оценка:
Можно так:

делаете контрол который работает в Design time,
при этом он собирает ссылки на другие контролы в форме,
находит клавиши Ok, и еще что хотите,

и предоставляет набор свойств,
выставляя которые вы задаете поведение этого валидатора в Run-time.

Так вы зададите визуально вашу таблицу настроек.
Винтовку добудешь в бою!
Re: Мысль о GUI
От: Vector Россия  
Дата: 19.06.06 08:47
Оценка: 6 (1)
Здравствуйте, Lonely Dog, Вы писали:

LD>Родилась мысль о том, как можно упростить реализацию GUI, конкретнее, хочется заставить систему саму отсжеливать состояния контролов. (enabled/disabled, visible, и пр)


Похожую мысль и реализацию родил недавно...
Очень удобно разделять документ от вида.

пример использования:


class CMyDlg : 
    public CDialogImpl<CMyDlg>,
    public CBindingImpl
{
public:
    enum { IDD = IDD_MY };

    DECLARE_BIND_MAP()
    BEGIN_MSG_MAP(CMyDlg)
        CHAIN_MSG_MAP(CBindingImpl)
    END_MSG_MAP()

CIntProperty m_prop;
};

// Эта карта делает так, что IDC_TEXT1 доступен, когда чекнут IDC_RADIO1 и IDC_TEXT2 доступен, когда чекнут IDC_RADIO2
BEGIN_BIND_MAP(CMyDlg)
    BIND_INT_RADIO(IDC_RADIO1,    m_prop,    1)
    BIND_INT_RADIO(IDC_RADIO2,    m_prop,    2)
    BIND_INT_ENABLED(IDC_TEXT1,    m_prop,    1)
    BIND_INT_ENABLED(IDC_TEXT2,    m_prop,    2)
END_BIND_MAP()

// Ну и общая концепция:

/*********************************************************************************\
    CProperty - базовый класс для всех пропертей
\*********************************************************************************/
class CProperty
{
public:
    CProperty();
    CProperty(const CProperty& prop);
    virtual ~CProperty();

    CProperty& operator = (const CProperty& prop);

    CEvent OnChange;
};

/*********************************************************************************\
    CPropertyBind - базовый класс для всех биндов (связь между свойством и элементом управления)
\*********************************************************************************/
class CPropertyBind
{
public:
    CPropertyBind(HWND hWnd, CProperty& prop) :
        m_hWnd(hWnd)
    {
        m_hander.init(prop.OnChange, &CPropertyBind::OnChangeProp, this);
    }

    void OnChangeProp(){ OnChangePropImpl(); }
    virtual void OnCommand(UINT nCode) = 0;

protected:
    virtual void OnChangePropImpl() = 0;

protected:
    HWND            m_hWnd;
    CHandler        m_hander;
};

/*********************************************************************************\
    CIntProperty - свойство типа int
\*********************************************************************************/
class CIntProperty : 
    public CProperty
{
public:
    class CBind : public CPropertyBind
    {
    public:
        CBind(HWND hWnd, CIntProperty& prop) :
            CPropertyBind(hWnd, prop),
            m_prop(prop),
            m_fSelfUpdate(FALSE){}
    
        virtual void OnCommand(UINT nCode);

    protected:
        virtual void OnChangePropImpl();

    protected:
        CIntProperty&    m_prop;
        BOOL            m_fSelfUpdate;
    };

    class CBindRadio : public CPropertyBind
    {
    public:
        CBindRadio(HWND hWnd, CIntProperty& prop, int nFlags) :
            CPropertyBind(hWnd, prop),
            m_prop(prop),
            m_nFlags(nFlags),
            m_fSelfUpdate(FALSE){}

        virtual void OnCommand(UINT nCode);

    protected:
        virtual void OnChangePropImpl();

    protected:
        CIntProperty&    m_prop;
        int                m_nFlags;
        BOOL            m_fSelfUpdate;
    };

    class CBindCheck : public CPropertyBind
    {
    public:
        CBindCheck(HWND hWnd, CIntProperty& prop, int nFlags) :
            CPropertyBind(hWnd, prop),
            m_prop(prop),
            m_nFlags(nFlags),
            m_fSelfUpdate(FALSE){}

        virtual void OnCommand(UINT nCode);

    protected:
        virtual void OnChangePropImpl();

    protected:
        CIntProperty&    m_prop;
        int                m_nFlags;
        BOOL            m_fSelfUpdate;
    };

    class CBindEnabled : public CPropertyBind
    {
    public:
        CBindEnabled(HWND hWnd, CIntProperty& prop, int nFlags, BOOL fAsFlag = FALSE) :
            CPropertyBind(hWnd, prop),
            m_prop(prop),
            m_nFlags(nFlags),
            m_fAsFlag(fAsFlag){}

        virtual void OnCommand(UINT nCode);

    protected:
        virtual void OnChangePropImpl();

    protected:
        CIntProperty&    m_prop;
        int                m_nFlags;
        BOOL            m_fAsFlag;
    };
    
    CIntProperty(int value = 0) : m_nValue(value){}

    operator int() { return m_nValue; }
    CIntProperty& operator = (int nValue){ SetValue(nValue); return *this; }

    int GetValue()const{ return m_nValue; }
    void SetValue(int value){ m_nValue = value; OnChange.raise(); }

private:
    int        m_nValue;
};

typedef std::multimap<UINT, CPropertyBind*>    CPropertyBindMap;
typedef CPropertyBindMap::iterator            CPropertyBindIter;
    
/*********************************************************************************\
    CBindingImpl - "связыватель" свойств с контролами (от него наследовать диалог) 
        не забывать про CHAIN_MSG_MAP(CBindingImpl)
\*********************************************************************************/
class CBindingImpl
{
public:
    CBindingImpl();
    virtual ~CBindingImpl();

    void ClearBindings();
    
    BEGIN_MSG_MAP(CBindingImpl)
        MESSAGE_HANDLER(WM_COMMAND, OnCommand)
        MESSAGE_HANDLER(WM_NOTIFY,    OnNotify)
    END_MSG_MAP()

private:
// Handler prototypes (uncomment arguments if needed):
//    LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
//    LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
//    LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/);

    LRESULT OnCommand(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
    LRESULT OnNotify(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);

    void ProcessCommand(UINT nID, UINT nCode);

protected:
    CPropertyBindMap    m_mapBindProp;
};

#define DECLARE_BIND_MAP()    void InitBinding();

#define BEGIN_BIND_MAP(theClass)    \
    void theClass::InitBinding()    \
    {

#define BIND_INT(id, prop)        \
    m_mapBindProp.insert(std::make_pair(id, new CIntProperty::CBind(GetDlgItem(id), prop)));            \
    prop.OnChange.raise();

#define BIND_INT_RADIO(id, prop, flags)        \
    m_mapBindProp.insert(std::make_pair(id, new CIntProperty::CBindRadio(GetDlgItem(id), prop, flags)));\
    prop.OnChange.raise();

#define BIND_INT_CHECK(id, prop, flags)        \
    m_mapBindProp.insert(std::make_pair(id, new CIntProperty::CBindCheck(GetDlgItem(id), prop, flags)));    \
    prop.OnChange.raise();

#define BIND_INT_ENABLED(id, prop, flags)        \
    m_mapBindProp.insert(std::make_pair(id, new CIntProperty::CBindEnabled(GetDlgItem(id), prop, flags)));    \
    prop.OnChange.raise();

#define BIND_INT_ENABLED_FLAG(id, prop, flags)        \
    m_mapBindProp.insert(std::make_pair(id, new CIntProperty::CBindEnabled(GetDlgItem(id), prop, flags, TRUE)));    \
    prop.OnChange.raise();



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