#delphi #findcontrol
#delphi #findcontrol
Вопрос:
Я динамически создал TValueListEditor
компонент VCL на TForm
. Код находится во вложенной процедуре одного из методов основной формы. Я установил:
ValueListEditor.KeyOptions := [keyEdit, keyAdd, keyUnique];
Это выглядит следующим образом:
TMainForm.Method();
Method
имеет вложенную процедуру, содержащую код, который создает компоненты, упомянутые выше.
Затем у меня есть вспомогательная функция:
function GetMenuListData(XMLNode: TXMLNode; const XNMLDoc: string = '') : string;
В этом помощнике я использую этот код для загрузки XML-файла, а затем извлекаю его узлы и вставляю их в ValueListEditor
.
XMLDoc := TXMLDocument.Create(Self);
XMLDoc.ParseOptions := [poPreserveWhiteSpace];
try
XMLDoc.LoadFromFile(XNMLDoc);
try
Control := FindControl(FindWindow('TForm',PChar('(' ExtractFileExt(Form1.Edit1.Text) ')')));
if Control <> nil then
begin
TValuelistEditor(Control).Keys[TValuelistEditor(Control).RowCount-1] := XMLDoc.DocumentElement.NodeName;
if XMLDoc.DocumentElement.ChildNodes.First.AttributeNodes.Count > 0 then
TValuelistEditor(Control).Values[TValuelistEditor(Control).Keys[TValuelistEditor(Control).RowCount-1]] := String(XMLDoc.DocumentElement.Attributes['id'])
else
TValuelistEditor(Control).Values[TValuelistEditor(Control).Keys[TValuelistEditor(Control).RowCount-1]] := '<Empty>';
end else begin
MessageBeep(0);
FlashWindow(Application.Handle, True);
ShowMessagePos('...');
end;
finally
XMLDoc.Active := False; Result := 'Forced ' Form1.RAWInputBtn.Caption ' in ' DateTimeToStr(Now);
end;
except
on E : EXMLDocError do
begin
Result := 'Forced ' Form1.RAWInputBtn.Caption ' in ' DateTimeToStr(Now);
end;
end;
Проблема в том, что я получаю сообщение о нарушении доступа каждый раз, когда код переходит в строку:
TValuelistEditor(Control).Keys[TValuelistEditor(Control).RowCount-1] := XMLDoc.DocumentElement.NodeName;
Я пробовал различные типизации, значения, параметры .. ничего не помогает.
В чем моя ошибка?
Я использую Delphi XE.
Комментарии:
1. Опубликованный вами код (в частности,
FindWindow
строка) ищетTForm
некоторый дополнительный текст. Почему вы предполагаете, что затем вы можете вызвать FindControl, используя этоHWND
и типизировать все, что он возвращает вTValueListEditor
?
Ответ №1:
Как прокомментировал Кен, ваша проблема в том, что вместо поиска редактора списка значений вы находите свою форму, а затем вводите ее в редактор списка значений, отсюда и AV.
Сначала вы передаете ‘TForm’ как ‘lpClassName’ в FindWindow
. Предполагая, что ‘TForm’ — это имя класса вашей формы, он, конечно, найдет форму, а не дочернее окно в ней. Во-вторых, вы не можете использовать FindWindow
для поиска дочернего окна, смотрите его документацию, он выполняет поиск окон верхнего уровня.
Если бы вы протестировали возврат FindControl
, код, вызывающий AV, никогда бы не выполнялся:
if (Control <> nil) and (Control is TValueListEditor) then
Вы можете использовать FindWindowEx
для поиска в дочерних окнах, если вы не знаете дескриптор вашей формы, найдите его первым, как вы уже делали:
FormHandle := FindWindow('TForm',PChar('(' ExtractFileExt(Form1.Edit1.Text) ')'));
if FormHandle <> 0 then
begin
Control := FindControl(FindWindowEx(FormHandle, 0, 'TValueListEditor', nil));
или, еще лучше, протестируйте возврат FindWindowEx
first, чтобы избежать передачи ‘0’ в FindControl
:
ValueListEditorHandle := FindWindowEx(FormHandle, 0, 'TValueListEditor', nil);
if Win32Check(ValueListEditorHandle <> 0) then
begin
Control := FindControl(ValueListEditorHandle);
if Assigned(Control) then
begin
...
Комментарии:
1. Sertac, пожалуйста, выделите FindWindEx в качестве функции ответа на этот вопрос.
2. @HX_ Ну, перед упоминанием есть разрыв строки, и он должен отображаться цветом ссылки. 🙂 Если серьезно, я думаю, это должно быть достаточно очевидно для любого, кто читает ответ..
Ответ №2:
Если ваша динамически создаваемая форма является частью того же приложения, вам не нужен весь шум из-за некорректности FindControl(FindWindow())
. Просто создайте свою форму, присвоив ей имя и назначив Application
владельца:
MyForm := TMyForm.Create(Application);
MyForm.Name := 'MyDynamicForm';
Когда вы хотите получить новую ссылку на него:
var
TheForm: TMyForm;
i: Integer;
begin
TheForm := nil;
for i := 0 to Screen.FormCount - 1 do
if Screen.Forms[i] is TMyForm then
// Could also use Screen.Forms[i].Caption
if Screen.Forms[i].Name = 'MyDynamicForm' then
TheForm := TMyForm(Screen.Forms[i]);
if Assigned(TheForm) then
TheForm.MethodThatLoadsXML(XMLFileName); // or whatever
end;
TheForm.MethodThatLoadsXML
теперь я могу получить доступ к TValueListEditor
напрямую:
procedure TMyForm.MethodThatLoadsXML(const XMLFileName: string);
begin
// Load xml as before, using XMLFileName
with TValueListEditor.Create(Self) do
begin
Options := [Whatever];
Parent := Self;
Left := SomeNumber;
Top := SomeNumber;
// Create items for value list from XML and other stuff
end;
end;
Комментарии:
1. Кен, большое тебе спасибо за твой ответ, но в моем случае он недействителен, потому что у меня динамический изменяемый тип формы (MDI, SDI), и я не могу просто полагаться на Screen unit. 1, хотя за время, которое вы потратили на этот вопрос!
2. HX, это оправдание не имеет никакого смысла.
Screen
Объект содержит ссылки на все формы вашей программы, независимо от того, какого рода они являются. Но если вам не нравится поиск формы вScreen
объекте, тогда сохраните динамически созданную форму в каком-нибудь другом контейнере, например, в списке. Если у вас есть только один экземпляр формы, вам даже не нужен контейнер. Просто сохраните его в простойTForm
переменной, а затем обратитесь к нему напрямую, когда вам понадобится форма.3. Роб, да, я мог бы, но я боюсь, что если бы я использовал Screen object для доступа к динамическим элементам, я бы рисковал столкнуться с некоторыми неизвестными проблемами. В любом случае спасибо за совет… Кроме того, это не оправдание, скорее чат, чтобы узнать другие варианты решения такой задачи для опытных профессионалов, таких как ты, Роб. 😉
4. HX, Роб прав. Используя Screen. Формы, как я показал вам выше, совершенно безопасны, независимо от того, является ли форма SDI или MDI, и вы можете протестировать конкретный тип вашей динамической формы, прежде чем что-либо делать (опять же, как я продемонстрировал). Я действительно думаю, что вы чрезмерно усложняете ситуацию, и если вы не опубликуете еще какой-нибудь код, показывающий, почему мы неправы, я буду продолжать так думать. 🙂
5. Кен, хе-хе, я не запрещаю тебе так думать 😉 В основном, я усложнял это, поскольку у меня есть некоторые переопределения элементов системного меню в различных WMS… поэтому я не хочу столкнуться с ситуацией, когда мне придется рефакторинговать или перепроектировать некоторые элементы только потому, что я не могу отследить какое-либо поведение. Кроме того, я стараюсь кодировать с учетом совместимости с WineHQ и, насколько я знаю, подложки экрана, в значительной степени функции D3D / GDI небезопасны в среде WineHQ .. jet … AFAIK, использующий FindWindow (Ex) / FindControl, в значительной степени зависит от не-компоновки и графической подсистемы…