Задача — показать UI настройки плагина (dll) — TPanel с набором размещённых на ней контролов.
Решаю задачу таким образом: в dll размещена невидимая форма, на ней панель с контролами; когда нужно показать UI настройки, приложение меняет свойство Parent панели на свою форму, по завершении работы юзера с UI свойство Parent панели меняется обратно на форму в dll.
Баги:
1) контролы не получают фокус клавишей Tab;
2) при попытке передать фокус компонентам TStringGrid, TDrawGrid, TListView выдаётся ошибка "Cannot focus disabled or invisible window".
Подскажите плз, как исправить ситуацию — или же корректный способ сделать то же самое, не прибегая к размещению плагинов в пакетах.
Цитаты из кода плагина: инициализация, показ/скрытие UI, выгрузка
exports
InitPlugin, //вызывается при загрузке плагина
ShutdownPlugin, //вызывается до выгрузки DLL
AttachConfig, //показать панель настроек
DetachConfig; //скрыть панель настроекvar//через эту структуру плагину передаётся ссылка на экземпляр Application и Screen приложения
SharedData:PSharedData;
//сохраняемые Application и Screen плагина
AppSv:TApplication;
ScrSv:TScreen;
//показать контрол и дочерниеprocedure RecurseShowControls(ctrl: TWinControl);
var i:integer;
begin
ctrl.Show;
ShowWindow(ctrl.Handle, SW_SHOW);
for i:=0 to ctrl.ControlCount-1 do
if ctrl.Controls[i] is TWinControl then
RecurseShowControls(ctrl.Controls[i] as TWinControl)
end;
//показать панель настроек поверх контролаprocedure TForm1.ShowOver(ctrl: TControl);
begin
windows.SetParent(Panel1.Handle, ctrl.Parent.Handle);
Panel1.Left:=ctrl.Left;
Panel1.Top :=ctrl.Top;
Panel1.BringToFront;
RecurseShowControls(Panel1 as TWinControl);
end;
//скрыть панель настроекprocedure TForm1.Reset;
begin
windows.SetParent(Panel1.Handle, Handle);
Panel1.Hide;
end;
///////////////////////////////////////////////////////////////////procedure DLLHandler(Reason: Integer);
begin
case Reason of
DLL_PROCESS_DETACH: begin
if Form1<>nil then begin
Form1.Reset;
FreeAndNil(Form1);
Application:=AppSv;
Screen:=ScrSv;
end;
end;
end;//caseend;
///////////////////////////////////////////////////////////////////procedure InitPlugin(const data:PSharedData);stdcall;
begin
SharedData:=data;
AppSv:=Application;
ScrSv:=Screen;
Application:=SharedData.app;
Screen:=SharedData.scr;
DLLProc:=@DLLHandler;
Application.CreateForm(TForm1, Form1);
end;
procedure ShutdownPlugin;stdcall;
begin
if Form1<>nil then begin
Form1.Reset;
FreeAndNil(Form1);
Application:=AppSv;
Screen:=ScrSv;
end
end;
procedure AttachConfig(ctrl:TControl);stdcall;
begin
Form1.ShowOver(ctrl);
end;
procedure DetachConfig;stdcall;
begin
Form1.Reset;
end;
Здравствуйте, RWolf, Вы писали:
RW>Задача — показать UI настройки плагина (dll) — TPanel с набором размещённых на ней контролов. RW>Решаю задачу таким образом: в dll размещена невидимая форма, на ней панель с контролами; когда нужно показать UI настройки, приложение меняет свойство Parent панели на свою форму, по завершении работы юзера с UI свойство Parent панели меняется обратно на форму в dll.
А что мешает в плагине хранить обычную видимую форму,а не заниматься непонятными мынипуляциями с Parent?
Procedure OpenPluginForm(AppHandle:THandle);
var F:TFormInPlugin;
begin
Application.Handle:=AppHandle;
F:=TFormInPlugin.Create(Application);
try
if F.ShowModal=mrOK then SomeDo; // .....finally F.Free; end;
end;
и форму плагина звать по кнопке на тулбаре или из меню....
Здравствуйте, DarkMaster, Вы писали:
DM>А что мешает в плагине хранить обычную видимую форму,а не заниматься непонятными мынипуляциями с Parent?
В моём случае UI должен быть построен примерно так: форма "Настройки", на ней слева — список плагинов, по выбору одного из них на этой же форме появляется панель настроек выбранного плагина.
Т.е. настройки плагина должны быть доступны без открытия лишних окон.
Здравствуйте, RWolf, Вы писали:
DM>>А что мешает в плагине хранить обычную видимую форму,а не заниматься непонятными мынипуляциями с Parent?
RW>В моём случае UI должен быть построен примерно так: форма "Настройки", на ней слева — список плагинов, по выбору одного из них на этой же форме появляется панель настроек выбранного плагина. RW>Т.е. настройки плагина должны быть доступны без открытия лишних окон.
Ну сделай видимую форму (та которая в ДЛЛ), потом присвой ей Border=bsNone, Align=alClient и сделай DLLForm.Parent:=ConfigDialog.PanelForControls...
Примерно так.
Здравствуйте, RWolf, Вы писали:
DM>>А что мешает в плагине хранить обычную видимую форму,а не заниматься непонятными мынипуляциями с Parent?
RW>В моём случае UI должен быть построен примерно так: форма "Настройки", на ней слева — список плагинов, по выбору одного из них на этой же форме появляется панель настроек выбранного плагина. RW>Т.е. настройки плагина должны быть доступны без открытия лишних окон.
Что-то подобное должно получится:
function DoOpenForm(ParentWin:HWND):HWND; stdcall;
var F: TDLLForm;
begin
try
F:=TDLLForm.CreateParented(ParentWin);
Application.Handle:=ParentWin;
F.Show;
Application.InsertComponent(F);
Result:=F.Handle;
except// oops... error!
Result:=0;
end;
end;
Где ParentWin == ConfigDialog.PanelForPluginControls.Handle....
Ошибся, действительно, контролы стали отображаться правильно. Перемещение фокуса табуляцией не работает в пределах дочерней формы, впрочем, это не критично.
Здравствуйте, RWolf, Вы писали:
RW>Ошибся, действительно, контролы стали отображаться правильно. Перемещение фокуса табуляцией не работает в пределах дочерней формы, впрочем, это не критично.
Попробуй сделать еще такой "финт ушами" (это действительно финт!): в обработчике TApplication.OnMessage() зови дополнительно еще процедуру, которая будет транслировать сообщение в ДЛЛ а в ДЛЛ у формы вызывай Perform() принудительно (для тех сообщений, которые до твоей формы не доходят). И хорошо бы перехватить хуком разрушение приложения, т.к. объект TApplication в ДЛЛ момент разрушения основного приложения скорее всего оставит без внимания со всеми вытекающими.
Здравствуйте, Leonid Troyanovsky, Вы писали:
RW>>Задача — показать UI настройки плагина (dll) — TPanel с набором размещённых на ней контролов. LT>Контролы в длл — MD.
С некоторой сноровкой — вполне себя неплохо чувствуют. Пример — тот же Total Commander. Хотя более правильный вариант — это диалоги. Ну и совсем правильный — BPL, но их использование заставляет завязыватся на определенную версию Дельфи, что для плагинописателей ну никак не приемлемо.