-2

我有一个 PopupMenu 项目,其中 TMenuItem 标签根据调用它的按钮而变化。每个按钮都有自己的 TEdit(ResultTFile1、ResultTFile2 和 ResultTFile3)。这些 TEdits 是使用表单设计器创建的。

var
 TempResultFile : TEdit;
begin
 // Find the right TEdit
 case (Sender as TMenuItem).Tag of
  1: TempResultFile := ResultTFile1;
  2: TempResultFile := ResultTFile2;
  3: TempResultFile := ResultTFile3;
 end;

 // Call save function
 if (SaveDialog1.Execute) then TempResultFile.Text := SaveDialog1.FileName;
end;

但是,它不起作用。对话框打开,但在执行后,FileName 变量并未提供给真正的 TEdit.Text(ResultTFile1、ResultTFile2 和 ResultTFile3)。没有错误。什么都没有。调试器似乎没有反应——但是,我可能是错的。如果我很好理解这一点,那是因为该值是给 TempResultFile 而不是真正的 TEdit。如何以 TempResultFile指向组件的方式对其进行编码?

4

4 回答 4

1

为了调试您的代码并找出问题所在,请完全更改您的代码——注释解释了每个代码如何帮助您解决问题。

var
  TempResultFile : TEdit;
begin
  if SaveDialog1.Execute then    // Only try assignment if file chosen
  begin
    // Find the right TEdit
    // The default branch will be used if the wrong menu item is 
    // connected to the event, or you forgot to set the tag value
    case (Sender as TMenuItem).Tag of
      1: TempResultFile := ResultTFile1;
      2: TempResultFile := ResultTFile2;
      3: TempResultFile := ResultTFile3;
    else                     // If none of the above match
      raise Exception.CreateFmt('Invalid tag %d in %s', 
         [TMenuItem(Sender).Tag, TMenuItem(Sender).Name]));
    end;
    // At this point, we know TempResultFile has been assigned a value
    TempFileResult.Text := SaveDialog1.FileName;
  end
  else // User clicked cancel on SaveDialog1.
    ShowMessage('SaveDialog1.Execute returned False`);
end;

这会做几件事来帮助你:

  • 如果SaveDialog没有出现,您就知道您没有为菜单项分配 OnClick 处理程序。

  • 如果用户选择CancelSaveDialog则返回 false,这将显示一个对话框来指示该事实(以消除该问题)。

  • 如果用户选择了一个文件,它会检查以确保对 进行了有效的Tag分配TMenuItem,如果您忘记了则引发异常(并为您提供无效的标记值和被单击的组件的名称)。

此外,它比原始问题中的代码更安全,如果标记不是case语句中的值之一并且SaveDialog1.Execute返回 true,则可能导致访问冲突。TempResultFile(当您访问它的Text属性时,可能没有分配任何内容。)

一旦你弄清楚你做错了什么,你可以删除最后的

end
else 
  ShowMessage('SaveDialog1.Execute returned False`);
于 2012-08-02T21:00:47.450 回答
1

正如其他人所说,您显示的代码只有这么多可能出错的地方:

1)TMenuItem.Tag可能包含错误的值。

2)TempResultFile可能没有分配一个有效的TEdit指针。不管其他人怎么说,保持变量未初始化并不能保证会发生访问冲突,尽管它很可能发生。还有一种可能性是,如果TEdit没有正确创建或已被释放,则分配的指针可能为 nil。如果您尝试使用它,那将导致 AV。

3)SaveDialog1.Execute()可能返回 False。如果您取消对话框,就会发生这种情况,但如果对话框有内部错误,也会发生这种情况。在某些情况下,您可以使用CommDlgExtendedError()来检查该情况。

4)SaveDialog1.FileName为空,如果SaveDialog1.Execute()返回 true,则不会发生这种情况,但是如果您使用的是相当现代的 Delphi 版本,在 Windows Vista 或更高版本上运行您的应用程序,并选择非文件系统文件,则可能会发生这种情况。

在调试期间,请确保您正在检查所有这些条件,例如:

var         
  Item: TMenuItem;
  TempResultFile : TEdit;         
  S: String;
begin         
  Item := Sender as TMenuItem;

  case Item.Tag of         
    1: TempResultFile := ResultTFile1;         
    2: TempResultFile := ResultTFile2;         
    3: TempResultFile := ResultTFile3;         
  else
    raise Exception.CreateFmt('%s.Tag (%d) is not an expected value!', [Item.Name, Item.Tag]);
  end;         

  if TempResultFile = nil then
    raise Exception.Create('TempResultFile is nil!');

  if not SaveDialog1.Execute then
    raise Exception.CreateFmt('SaveDialog1.Execute returned false! Possible CommDlg error? (%d)', [CommDlgExtendedError()]);

  S := SaveDialog1.FileName;
  if S = '' then
    raise Exception.Create('SaveDialog1.FileName is empty!');

  TempResultFile.Text := S;
end;   

作为使用 的替代方法TMenuItem.Tag,该TPopupMenu.PopupComponent属性将告诉您哪个 Button 显示了 PopupMenu。您可以将TButton.Tag属性设置为指向TEdit与该按钮对应的组件,然后您不必再使用该TMenuItem.Tag属性来寻找TEdit组件,例如:

procedure TForm1.FormCreate(Sender: TObject);
begin
  ResultTButton1.Tag := NativeInt(ResultTFile1);
  ResultTButton2.Tag := NativeInt(ResultTFile2);
  ResultTButton3.Tag := NativeInt(ResultTFile3);
end;

procedure TForm1.MenuItemClick(Sender: TObject);
var 
  ResultTButton : TButton; 
  TempResultFile : TEdit; 
begin 
  ResultTButton := PopupMenu.PopupComponent as TButton; 
  TempResultFile := TEdit(ResultTButton.Tag); 
  if TempResultFile <> nil then begin
    if SaveDialog1.Execute then
      TempResultFile.Text := SaveDialog1.FileName; 
  end;
end; 
于 2012-08-02T19:00:05.373 回答
-2

嗯...实际上只有一个原因导致您的代码无法正常工作:没有分配 TempResultFile,这意味着“case”语句无法找到匹配项。任何一个:

  • Sender不是TMenuItem 的实例
  • Sender 不是您期望的 TMenuItem 实例
  • Sender 是您的 TMenuItem 但您没有正确分配标签

每个案例都非常容易验证。开始工作吧!

哦,顺便说一句,适当的防御风格会将代码重写为:

var
 TempResultFile : TEdit;
begin
 {$IFDEF DEBUG}
 // preconditions
 Assert(sender is TMenuItem) ;
 {$ENDIF}
 // Find the right TEdit
 case (Sender as TMenuItem).Tag of
     1: TempResultFile := ResultTFile1;
     2: TempResultFile := ResultTFile2;
     3: TempResultFile := ResultTFile3;
     else Assert(false,'Failed to locate the proper TEdit') ;
 end;

 // Call save function
 if (SaveDialog1.Execute) then 
     TempResultFile.Text := SaveDialog1.FileName;

结尾;

于 2012-08-02T22:01:35.513 回答
-2

你可以这样写:

begin
  if (SaveDialog1.Execute) then 
  begin
    case (Sender as TMenuItem).Tag of    
       1: ResultTFile1.text := SaveDialog1.FileName;    
       2: ResultTFile2.text := SaveDialog1.FileName;    
       3: ResultTFile3.text := SaveDialog1.FileName;   
    end;  
  end; 
end;
于 2012-08-02T15:56:44.433 回答