kab*_*rgo 12 delphi delphi-xe2
我在delphi的最后几天遇到了很多麻烦,我试图做的很简单,在某个点阻止接口并在其他一些点之后启用.
但就像听起来一样,我无法弄清楚为什么设计允许某些事情,所以要澄清:
1)创建一个项目
2)在表格中放置编辑和按钮,编辑的标签顺序必须先行
3)配置编辑的OnExit事件并写入:
Enabled := False;
Run Code Online (Sandbox Code Playgroud)
4)配置按钮的OnClick事件并写入:
ShowMessage('this is right?');
Run Code Online (Sandbox Code Playgroud)
基本上就是它,现在编译,它将在编辑时按下,按下选项卡,表格将按照我们的要求被禁用,因此相应于标签顺序,下一个获得焦点的控件是按钮(但我们禁用了表单),现在按空格键,消息应该出现.
所以问题是:这是对的吗?这个行为的逻辑解释是什么?
thx提前.
J..*_*... 11
这两个TButton和TEdit的TWinControl后代-这意味着它们是窗口控件.创建它们时,它们将自己分配HWND,操作系统在有焦点时直接向它们发送消息.禁用其包含形式可防止主窗体接收输入消息或接收焦点,但如果已有输入焦点,则不会禁用任何其他窗口控件.
如果这些控件没有输入焦点,则当用户输入(单击,制表键等)指示时,包含表单的责任是将输入焦点传递给它们.如果表单被禁用并且这些控件未被聚焦,则表单将不会接收允许其传输焦点的输入消息.如果焦点被转移到一个窗口的控制,但是,那么所有的用户输入直接进入到控制,即使它们的父控件的窗口被禁用-它们实际上是自己单独的窗口.
我不确定你观察到的行为是一个错误 - 它可能不是预期的,但它是标准行为.通常不期望禁用一个窗口也会禁用同一应用程序中的其他窗口.
问题是有两个独立的层次结构在起作用.在VCL级别,Button是一个子控件,并有一个父级(窗体).但是,在操作系统级别,两者都是单独的窗口,并且操作系统不知道(组件级别)父/子关系.这将是类似的情况:
procedure TForm1.Button1Click(Sender: TObject);
var
form2 : TForm1;
begin
self.Enabled := false;
form2 := TForm1.Create(self);
try
form2.ShowModal;
finally
form2.Free;
end;
end;
Run Code Online (Sandbox Code Playgroud)
你真的希望form2在它出现时被禁用,只是因为它的TComponent主人是Form1?当然不是.窗口控件大致相同.
Windows本身也可以具有父/子关系,但这与组件所有权(VCL父/子)分开,并且不一定以相同的方式运行. 来自MSDN:
系统将子窗口的输入消息直接传递给子窗口; 消息不通过父窗口传递. 唯一的例外是 EnableWindow函数已禁用子窗口.在这种情况下,系统会将已经转到子窗口的任何输入消息传递给父窗口.这允许父窗口检查输入消息并在必要时启用子窗口.
强调我的 - 如果你禁用一个子窗口,那么它的消息将被路由到父级,以便有机会检查并对它们采取行动.反之则不然 - 禁用的父母不会阻止孩子接收消息.
一个相当繁琐的解决方法可能是制作你自己的一组TWinControl行为如下:
TSafeButton = class(TButton)
protected
procedure WndProc(var Msg : TMessage); override;
end;
{...}
procedure TSafeButton.WndProc(var Msg : TMessage);
function ParentForm(AControl : TWinControl) : TWinControl;
begin
if Assigned(AControl) and (AControl is TForm) then
result := AControl
else
if Assigned(AControl.Parent) then
result := ParentForm(AControl.Parent)
else result := nil;
end;
begin
if Assigned(ParentForm(self)) and (not ParentForm(self).Enabled) then
Msg.Result := 0
else
inherited;
end;
Run Code Online (Sandbox Code Playgroud)
这会走向VCL父树,直到它找到一个表单 - 如果它发生并且表单被禁用,那么它也拒绝对窗口控件的输入.凌乱,可能更有选择性(也许一些消息不应该被忽略......)但它可能是一些可行的开始.
进一步挖掘,这似乎与文档不一致:
一次只能有一个窗口可以接收键盘输入; 该窗口据说具有键盘焦点.如果应用程序使用EnableWindow函数禁用键盘焦点窗口,则窗口除了被禁用外还会失去键盘焦点.然后EnableWindow将键盘焦点设置为NULL,这意味着没有窗口具有焦点.如果子窗口或其他后代窗口具有键盘焦点,则子窗口在禁用父窗口时会失去焦点.有关更多信息,请参阅键盘输入.
这似乎没有发生,甚至明确地将按钮的窗口设置为具有以下内容的子项:
oldParent := WinAPI.Windows.SetParent(Button1.Handle, Form1.Handle);
// here, in fact, oldParent = Form1.Handle, so parent/child HWND
// relationship is correct by default.
Run Code Online (Sandbox Code Playgroud)
多一点(对于repro) - 相同的场景Edit选项卡专注于按钮,退出处理程序启用TTimer.这里表单被禁用,但按钮保持焦点,即使这似乎确认Form1的HWND确实是按钮的父窗口,它应该失去焦点.
procedure TForm1.Timer1Timer(Sender: TObject);
var
h1, h2, h3 : cardinal;
begin
h1 := GetFocus; // h1 = Button1.Handle
h2 := GetParent(h1); // h2 = Form1.Handle
self.Enabled := false;
h3 := GetFocus; // h3 = Button1.Handle
end;
Run Code Online (Sandbox Code Playgroud)
在我们将按钮移动到面板的情况下,一切似乎都按预期工作(大多数情况下).面板被禁用,按钮失去焦点,但焦点移动到父窗体(WinAPI建议它应该为NULL).
procedure TForm1.Timer1Timer(Sender: TObject);
var
h1, h2, h3 : cardinal;
begin
h1 := GetFocus; // h1 = Button1.Handle
h2 := GetParent(h1); // h2 = Panel1.Handle
Panel1.Enabled := false;
h3 := GetFocus; // h3 = Form1.Handle
end;
Run Code Online (Sandbox Code Playgroud)
问题的一部分似乎在这里 - 看起来顶级形式本身正在负责控制散焦.这种方式有效,除非表单本身是被禁用的表单:
procedure TWinControl.CMEnabledChanged(var Message: TMessage);
begin
if not Enabled and (Parent <> nil) then RemoveFocus(False);
// ^^ False if form itself is being disabled!
if HandleAllocated and not (csDesigning in ComponentState) then
EnableWindow(WindowHandle, Enabled);
end;
procedure TWinControl.RemoveFocus(Removing: Boolean);
var
Form: TCustomForm;
begin
Form := GetParentForm(Self);
if Form <> nil then Form.DefocusControl(Self, Removing);
end
Run Code Online (Sandbox Code Playgroud)
哪里
procedure TCustomForm.DefocusControl(Control: TWinControl; Removing: Boolean);
begin
if Removing and Control.ContainsControl(FFocusedControl) then
FFocusedControl := Control.Parent;
if Control.ContainsControl(FActiveControl) then SetActiveControl(nil);
end;
Run Code Online (Sandbox Code Playgroud)
这部分解释了上述观察到的行为 - 焦点移动到父控件并且活动控件失去焦点.它仍然无法解释为什么'EnableWindow`无法将焦点锁定到按钮的子窗口.这确实看起来像WinAPI问题......
| 归档时间: |
|
| 查看次数: |
1905 次 |
| 最近记录: |