【问题标题】:Delphi Application.CreateForm is the Handle Unique for each Form?Delphi Application.CreateForm 是每个表单的唯一句柄吗?
【发布时间】:2019-10-29 13:40:44
【问题描述】:

我有一个 TreeList ,有很多 Items ,每个项目都有它自己的唯一 ID 。 我允许用户一次打开多个 ID。但是我想防止用户两次打开同一个ID。

所以我考虑创建一个简单的动态数组,在其中存储哪个 TreeList ID 连接到哪个 Form HWND 。如果我在我的列表中找到一个带有匹配 HWND 的 ID,那么我只需将已经创建的表单带到前台。

  Application.CreateForm(TChapter, Chapter);
  Chapter.PopupParent:=Main;
  Chapter.FID:=qryTreeID.Value;
  Chapter.Caption:=qryTreeName.Value+Cardinal(Chapter.Handle).ToString;
  Chapter.Show;

这就是我创建表单的方式。这只是一个“基本”示例。我只是想确保句柄是唯一的,我打开了多个表单,数字总是不同的。但我想确定一下。

谢谢!

【问题讨论】:

  • 当您似乎已经在 TChapter 表单的 FID 字段/属性中获得了可用信息时,为什么还要创建一个新数组。因此,您所要做的就是遍历所有表单以查看具有特定 FID 的表单是否已经打开。如果是,您只需显示它。如果不是,则创建一个新的。
  • HWND 转换为 Cardinal 在 64 位上可能存在风险,其中 HWND 是 64 位类型,但由于实现细节,您将侥幸逃脱。你不会逃脱的是窗口娱乐。表单的唯一 ID 是它自己的引用。将Cardinal(Chapter.Handle) 替换为NativeUInt(Chapter)

标签: forms delphi handle


【解决方案1】:

如果您想维护自己的查找,TDictionary 比动态数组更有意义。但无论哪种方式,您都应该将 ID 映射到实际的 TForm 对象,而不是它的 HWNDHWND 保证是唯一的,但不是持久的,因为它可以在表单的生命周期内更改。它还可以让您免于从HWND 获取TForm 对象的额外步骤。

例如:

var
  Chapters: TDictionary<Integer, TChapter> = nil;

procedure ChapterDestroyed(Self: Pointer; Sender: TObject);
begin
  if Chapters <> nil then
    Chapters.Remove(TChapter(Sender).FID);
end;

function FindChapterByID(ID: Integer): TChapter;
// var I: Integer;
begin
  {
  for I := 0 to Screen.FormCount-1 do
  begin
    if Screen.Forms[I] is TChapter then
    begin
      Result := TChapter(Screen.Forms[I]);
      if Result.FID = ID then Exit;
    end;
  end;
  Result := nil;
  }
  if not Chapters.TryGetValue(ID, Result) then
    Result := nil;
end;

function CreateChapter(ID: Integer): TChapter;
var
  Event: TNotifyEvent;
begin
  TMethod(Event).Data := nil;
  TMethod(Event).Code = @ChapterDestroyed;

  Result := TChapter.Create(Main);
  try
    Result.FID := ID;
    Result.PopupParent := Main;
    Result.Caption := qryTreeName.Value + ID.ToString;
    Result.OnDestroy := Event;
    Chapters.Add(ID, Result);
  except
    Result.Free;
    raise;
  end;
end;

function ShowChapterByID(ID: Integer): TChapter;
begin
  Result := FindChapterByID(ID);
  if Result = nil then Result := CreateChapter(ID);
  Result.Show;
end;

initialization
  Chapters := TDictionary<Integer, TChapter>.Create;
finalization
  FreeAndNil(Chapters);
Chapter := ShowChapterByID(qryTreeID.Value);

【讨论】:

    【解决方案2】:

    谢谢你们俩。因为简单,我接受了 SilverWariors 的建议 :)

      for i := 0 to Screen.FormCount-1 do
      begin
        if Screen.Forms[i] is TChapter then
          if (Screen.Forms[i] as TChapter).FID = qryTreeID.Value then
          begin
            (Screen.Forms[i] as TChapter).BringToFront;
            Exit;
          end;
      end;
    

    【讨论】:

    • 一旦Screen.Forms[i] is TChapter 返回true,你最好写TChapter(Screen.Forms[i]),而不是使用as 的checked cast。没有必要像你的代码那样检查类型三次。
    猜你喜欢
    • 1970-01-01
    • 2020-03-26
    • 1970-01-01
    • 1970-01-01
    • 2013-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多