如何检查是不是在 TTabSheet 控件上创建了 OnResize 事件?

Posted

技术标签:

【中文标题】如何检查是不是在 TTabSheet 控件上创建了 OnResize 事件?【英文标题】:How can I check if OnResize event is created on TTabSheet control?如何检查是否在 TTabSheet 控件上创建了 OnResize 事件? 【发布时间】:2016-08-15 15:00:58 【问题描述】:

我在TPageControl 上有多个TTabSheet,并且在某些操作上,用户单击一个按钮,我想在选定的TTabSheet 上运行OnResize 事件。问题是并非所有TTabSheet 控件都创建了OnResize 事件。

我在按钮上有这个代码:

procedure TForm1.Button1Click(Sender: TObject);
begin
  TTabSheet(PageControl1.ActivePage).OnResize(PageControl1.ActivePage);
end;

procedure TForm1.TabSheet1Resize(Sender: TObject);
begin
  // actions on Resize
end;

它在TabSheet1 处于活动状态时起作用。但是当TabSheet2 处于活动状态并且它没有OnResize 事件时,我得到错误:

Project Project1.exe 引发异常类 $C0000005 并显示消息 '访问地址 0x00000000: 读取地址 0x00000000'.

我尝试检查 nil,如下所示:

If TTabSheet(PageControl1.ActivePage).OnResize(PageControl1.ActivePage) <> nil then...

但它不能编译:

E2008 不兼容类型。

我发现的解决方法是让每个TTabSheet 控件都有空的OnResize 事件,只有注释,没有代码。

如果TTabSheetOnResize 事件,有没有比 nil 更好的检查?

谢谢

【问题讨论】:

如果已分配(PageControl1.ActivePage.OnResize) 则 .... 如果我是你,我会尽量避免从代码中调用事件处理程序。 OnResize 改变了一些 TLabrl 和 TEdit 控件的显示方式。我也需要在单击按钮时执行它。我怎样才能避免调用它但被执行? 框架调用事件。如果您想对按钮单击和调整大小事件做出响应,我会执行以下操作: 1. 创建一个执行该操作的私有方法。 2. 从按钮单击处理程序调用该私有方法。 3. 从 resize 事件调用该私有方法。 完全不清楚您要解决什么问题。您是在 IDE 表单设计器中创建 Tabsheet2,还是在代码中创建? 【参考方案1】:

您可能以错误的方式处理此问题。您不应在代码中调用事件处理程序。把它留给框架。而是这样写:

procedure TForm1.DoTabsheet1Resize;
begin
  // actions on Resize
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  DoTabsheet1Resize;
end;

procedure TForm1.TabSheet1Resize(Sender: TObject);
begin
  DoTabsheet1Resize;
end;

这里DoTabsheet1Resize 是您定义的私有方法。

【讨论】:

所以我需要为 TabSheet1 和其他每个 TabSheet 编写这个新的私有方法吗?他们是私人的什么?我知道每个类都可以有私有方法,那么这些在哪个类中呢? TTabSheet 在 Vcl.ComCtrls 类 TWinControl 中定义。那里?还是每个执行它们的 TButton? 啊哈,我明白了……它在 TForm1 中!从未创建私有方法,所以我很困惑。【参考方案2】:
procedure TForm1.Button1Click(Sender: TObject); 
begin
  If Assigned(PageControl‌​1.ActivePage.OnResize‌​) 
    then PageControl‌​1.ActivePage.OnResize‌​(Sender);       
end;

【讨论】:

已分配零、空和未附加检查?这适用于所有情况还是仅适用于某些情况? @Matej 你能定义“空”和“非附加”吗?这些是什么东西?答案是没有这样的事情。如果事件处理程序是nil,则Assigned() 返回False,并且没有附加事件处理程序。看到代码从外部调用事件处理程序仍然让我有点恶心。 好的,谢谢大卫。我对 Objetct Inspector 中的 TTabSheet 事件感到困惑。它显示它们在那里,但没有附加代码。所以它们存在但没有被分配。对我来说还是有点困惑。 所以如果我使用 Assigned 我不需要 TTabSheet() 在 PageControl1.ActivePage 之前? @Matejmtj 是否分配没有区别。 ActivePage 已经是 TTabSheet 了,所以没有必要像这样显式地转换它。【参考方案3】:

从你的评论来看

我设置了 OnResize 事件,但没有设置 TabSheet2。所以 TabSheet2 在 Object Inspector 中有 OnResize 但在 unit1 的代码中没有。

我认为您可能对表单设计器的确切内容感到困惑,它定义了事件处理代码。

试试这个:

    在新创建的空白表单上,放置一个 TPageControl,PageControl1,右键单击它并在其中创建两个 TabSheet,TabSheet1 和 TabSheet2。

    在 OI 中,单击 TabSheet1,在 OI 中单击其事件选项卡,然后单击 OnResize 事件属性中的下拉按钮。你会发现下拉列表是空的。

    现在双击 Tabsheet1 的 OnResize 事件。这样做会导致 OI 在表单的代码中创建一个空的事件处理过程 TabSheet1Resize。它还将 TabSheet1 的 OnResize 属性分配给此过程 TabSheet1Resize。此时不要保存表单,否则 IDE 将检测到此过程不包含代码(或 cmets)并将其删除。

    在 TabSheet1Resize 中添加此代码

    Caption := 'TabSheet1.Resize';

并保存表单的单位。

    现在在 OI 中选择 TabSheet2 的事件选项卡并在其 OnResize 事件中单击。此时 TabSheet2 的 OnResize 属性未分配给任何过程,因此尝试调用 TabSheet2.OnResize() 将导致 AV,因为其 OnResize 属性的值为 Nil。

    现在,在 TabSheet2 的 OnResize 中,单击下拉按钮,您将看到 TabSheet1Resize 作为列表中的单个条目。从列表中选择它,现在 TabSheet2 的 OnResize 属性的值与 TabSheet1'1 相同,即过程 TabSheet1Resize。

第 3 步和第 6 步对于 TabSheet1 和 TabSheet2 具有相同的 OnResize 事件处理程序代码至关重要。

换句话说,您的评论似乎忽略了对象(在本例中为 TTabSheet always)所具有的事件处理程序属性(例如 OnResize)之间的差异(这就是 OI 向您展示的原因事件处理程序 property)和事件处理程序 procedure 中的代码(如果有),事件处理程序属性被分配到。

对不起,如果以上所有内容都非常明显,那么我误解了你的几个 cmets。

如果您希望所有其他选项卡具有与 TabSheet1 相同的 OnResize 处理程序,您可以在例如您的 FormCreate 事件处理程序中进行设置,如下所示:

procedure TForm1.FormCreate(Sender: TObject);
var
  i : integer;
begin
  for i := 1 to PageControl1.PageCount - 1 do
    PageControl1.Pages[i].OnResize := PageControl1.Pages[0].OnResize;
  //  PageControl1.Pages[0] will be equal to TabSheet1, if that's the first Tabsheet
  //   you created
end;

【讨论】:

很好的解释!您确实误解了我的问题,但您正确地认识到我缺乏词汇:事件处理程序属性和事件处理程序过程。现在我知道更多了。

以上是关于如何检查是不是在 TTabSheet 控件上创建了 OnResize 事件?的主要内容,如果未能解决你的问题,请参考以下文章

如何检查鼠标是不是在控件上

如何检查控件是不是已添加到视图中?

Delphi动态添加控件

如何检查刷新控件是不是在其目标操作方法中刷新

如何检查所有文本框是不是已填充? [复制]

创建集群后如何检查是不是启用了反向代理