Нужно сделать калькулятор и препод требует сделать, чтобы все кнопки на онклик запускали одну прцедуру... И вот у меня проблема как узнать на какую именно кнопку нажал пользователь??
Здравствуйте Аноним, Вы писали:
А>Нужно сделать калькулятор и препод требует сделать, чтобы все кнопки на онклик запускали одну прцедуру... И вот у меня проблема как узнать на какую именно кнопку нажал пользователь??
1) вопрос не в тот форум. Наверное, тебе более подходит WinAPI
2) какими средствами ты пишешь этот калькулятор:
— консольное досовское приложение (а вдруг?)
— приложение на Windows API
— Visual C и библиотека MFC
— Borland C Builder
— ...
от этого много зависит.
Например, с чистым WinAPI, все события проходят через одну процедуру (обработчик событий окна).
На MFC — есть несколько способов (назначение общего обработчика для нескольких событий, субклассирование кнопок и т.д.)
Здравствуйте Кодт, Вы писали:
К>Здравствуйте Аноним, Вы писали:
К>1) вопрос не в тот форум. Наверное, тебе более подходит WinAPI
Почему winApi? В процедуру же предается Sender через него можно как-то узнать.... но как?
А компилятор Borland С Builder
К>от этого много зависит. К>Например, с чистым WinAPI, все события проходят через одну процедуру (обработчик событий окна). К>На MFC — есть несколько способов (назначение общего обработчика для нескольких событий, субклассирование кнопок и т.д.)
По-моему ты усложняешь... в дельфи такое пишется в одну строчку :
Label1.Caption := (Sender as TButton).Name;
Здравствуйте Cyber_Girl, Вы писали:
К>>1) вопрос не в тот форум. Наверное, тебе более подходит WinAPI CG>Почему winApi? В процедуру же предается Sender через него можно как-то узнать.... но как?
CG>А компилятор Borland С Builder
Ага! Это был BCB! Я зналь, я зналь!
Тогда — прямая дорога в форум "Delphi & Builder"
К>>от этого много зависит. К>>Например, с чистым WinAPI, все события проходят через одну процедуру (обработчик событий окна). К>>На MFC — есть несколько способов (назначение общего обработчика для нескольких событий, субклассирование кнопок и т.д.) CG>По-моему ты усложняешь... в дельфи такое пишется в одну строчку :
CG>Label1.Caption := (Sender as TButton).Name;
CG>Как такое сделать в си????
Сделай различие между языком C++, программным интерфейсом виндов (WinAPI) и средой программирования BCB.
К сожалению, по Билдеру я не специалист (в Дельфях еще более-менее... а в Билдере просто не знаю тонкостей).
Ставлю на нашей беседе бомбочку "Перевести в форум Дельфи & Билдер"
Вот ответ из FAQ который я написал для конференции http://www.softforum.ru/cbuilder
Что-то у него сегодня проблемы с коннектами прямой сейчас поэтому закину сюда.
Ответ на твой вопрос гдето в середине.
Через пол часа можешь залесть вот сюда http://www.softforum.ru/cbuilder.faq и прочитать остальные, тебе поможет.
Q: Как динамически создать кнопку и поставить ее на панель?
TButton* B=new TButton(this);
B->Parent=Panel1;
Q: Нужно ли ее потом удалять самостоятельно?
В данном примере кнопка будет удалена автоматически это сделает
либо Owner либо Parent(кто первый успеет) В данном примере в
качестве Owner кнопки передан this который указывает на форму
в чьем методе создается кнопка, а в качестве Parent присвоен
Panel1. Когда один из этих объектов будет удален он удалит
вместе с собой кнопку.
Q: Все ли объекты удаляются автоматически?
Нет. VCL умеет удалять удаляет только потомков TComponent.
Для этого им при конструировании надо передать в качестве
параметра Owner тот объект который будет ответственен за их
удаление. Все остальные объекты надо удалять руками.
Кроме того иногда бывает полезно удалить объект когда он больше
не нужен, а не ждать когда это сделает VCL
Q: Как удалить объект?
Надо воспользоваться оператором delete. Для того чтобы удалить
объект надо иметь указатель на него. В первом примере указатель
хранится в переменной B. И теряется при выходе из процедуры.
Для того чтобы нормально удалить объект надо сохранить указатель
в какой-нибудь переменной.
TButton* B;
void __fastcall Form1::ButtonCreateClick(TObject* Sender)
{
B=new TButton(this);
B->Parent=Panel1;
}
void __fastcall Form1::ButtonDestroyClick(TObject* Sender)
{
delete B;
}
Q: Как в обработчике различать для какого компонента он был вызван
и как работать с этим компонентом?
Например есть кнопка, у нее обработчик OnClick в обработчик кнопка
приходит как TObject. Но на самом деле это есть указатель на кнопку.
Просто компилятор не знает об этом. Поэтому его надо в этом убедить.
Простейший метод:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
((TButton*)Sender)->Caption="qwe";
((TButton*)Sender)->Tag=3;
((TButton*)Sender)->Name="asd";
}
Если обработчик будет вызван не кнопкой а чем нибудь еще то будет
Access Violation. Чтобы этого избежать нуобходимо уметь проверять
является обработчик кнопкой(чем нибудь другим) или нет.
Если у нас вполне конечное кол-во объектов можно просто сравнивать
указатели
if (Sender==TForm1->Button12)
{
((TButton*)Sender)->Caption="qwe";
}
Если у нас не вполне понятное количество компонентов то этот метод
неподходит. Необходимо научиться определять настоящий тип объектов.
Первое что приходит в голову новичкам это проверить ClassName
if (Sender->ClassName()="TButton")
{
((TButton*)Sender)->Caption="qwe";
}
Метод работает но у него есть несколько недостатоков:
1) Медленно работает т.к. создаются и сравниваются строки
2) Работает только с потомками TObject(в Delphi все классы
наслендуются от TObject, а в C++ Builder нет)
3) Не подходит для определения промежуточных классов.
Например мы хотим изменить у Sender свойство Tag. Tag есть у всех
TComponent А Sender может быть, например, TButton, TBitBtn, TPanel итп
(в более более тяжелом случае мы можем, например что это визуальный
компонент лежащий на форме(т.е это TControl) и т.к. он наследуется
от TComponent св-во Tag у него есть, но это все что мы про него знаем)
В этом случае нам поможет RTTI(RunTime Type Identification).
Дело в том что в Объектах содержится указатель на, так называемую,
VTBL (таблица виртуальных методов). На самом деле VTBL есть не у
всех объектов, а только у тех у которых есть виртуальные функции
(т.е. полиморфных классов) и их потомков(TObject полиморфный тип)
Для этого есть ключевое слово dynamic_cast.
синтаксис выглядит так:
dynamic_cast<CastType>(Pointer)
если приведение типа удалось dynamic_cast возвратит ненулевой
указатель того типа, которого нужно, если не удалось нулевой.
Например(как в предыдущем примере) мы хотим изменить(или посмотреть) Tag.
Делаем так:
if (TComponent* C=dynamic_cast<TComponent*>(Sender))
{
switch(C->Tag)
{
...
}
}
else
{
throw Exception("В наш обработчик пришел не TComponent");
}
Q: Как назначать/отрывать обработчики событий в Runtime?
Событие это просто указатель на функцию, специального типа.
Его можно присваивать и отрывать в Runtime.
Button1->OnClick=0;
больше событие не выполнится, т.к. оно теперь не назначено.
Button1->OnClick=Button2Click;
теперь первая кнопка при нажатии вызывает событие которое должна
вызывать вторая кнопка.
Если ты хочешь присвоить события компонентам созданным в
динамике - вперед. С непривычки бывает сначала сложно описать
функцию с правильными параметрами Я рекомендую сделать так:
например есть массив кнопок им всем надо присвоить OnClick.
1) Ставим на форму кнопку
2) Делаем ей событие OnClick, переименовываем функцию как надо.
3) Пишем внутрь какой-нибудь комментарий(т.к. Builder удаляет
пустые обработчики)
4) Удаляем саму кнопку
Теперь у нас есть правильно описанный обработчик события который
можно присвоить как
ArrayOfTButton[i]->OnClick=...
Любая проблема дизайна может быть решена введением дополнительного абстрактного слоя, за исключением проблемы слишком большого количества дополнительных абстрактных слоев
Здравствуйте Аноним, Вы писали:
А>Нужно сделать калькулятор и препод требует сделать, чтобы все кнопки на онклик запускали одну прцедуру... И вот у меня проблема как узнать на какую именно кнопку нажал пользователь??
Я бы сделал примерно так.
void __fastcall Form1::Button1Click(TObject* Sender)
{
TButton *B=dynamic_cast<TButton *>(Sender);
if (!B) return;
switch (B->Tag) {
case 1:....
};
B=NULL;
}
Можно конечто проверять и B->Name, но лучше немного потрудиться и каждой кнопке добавить свой tag
Здравствуйте Аноним, Вы писали:
А>Нужно сделать калькулятор и препод требует сделать, чтобы все кнопки на онклик запускали одну прцедуру... И вот у меня проблема как узнать на какую именно кнопку нажал пользователь??
Создать 4 кнопки (Button1,Button2,Button3,Button4).
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Button1->Tag=1;
Button2->Tag=2;
Button3->Tag=3;
Button4->Tag=4;
Button1->OnClick=Button1Click;
Button2->OnClick=Button1Click;
Button3->OnClick=Button1Click;
Button4->OnClick=Button1Click;
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TButton *a;
a=(TButton *)Sender;
switch(a->Tag)
{
case 1:ShowMessage("Button #1");break;
case 2:ShowMessage("Button #2");break;
case 3:ShowMessage("Button #3");break;
case 4:ShowMessage("Button #4");break;
};
}