如何从 Delphi 中的 richedit 获得首次输出?

How to get first-time output out of a richedit in Delphi?

当我的代码输出到富编辑时出现问题。当我单击按钮计算所有内容然后显示所有内容时,它不会输出生成的 ClientNum 和 Price。但是,当我第二次单击该按钮时,它会毫不费力地输出所有内容吗?我的代码是否有问题,或者可能是某些外部因素(如防病毒软件)干扰了输出?

图片说明我在说什么:



我的代码:

    type
  TfrmTourBooking = class(TForm)
    rgpDestination: TRadioGroup;
    rgpAccommodation: TRadioGroup;
    sedPeopleAmount: TSpinEdit;
    Label1: TLabel;
    edtID: TEdit;
    Label2: TLabel;
    Label3: TLabel;
    redOut: TRichEdit;
    btnCalc: TButton;
    bmbClose: TBitBtn;
    rgpTransport: TRadioGroup;
    edtName: TEdit;
    Label4: TLabel;
    rgpTourLength: TRadioGroup;
    edtPhoneNum: TLabeledEdit;
    edtEmail: TLabeledEdit;
    bmbReset: TBitBtn;
    dtpTime: TDateTimePicker;
    procedure FormActivate(Sender: TObject);
    procedure btnCalcClick(Sender: TObject);
    procedure rgpDestinationClick(Sender: TObject);
    procedure rgpTourLengthClick(Sender: TObject);
    procedure rgpAccommodationClick(Sender: TObject);
    procedure bmbResetClick(Sender: TObject);
    function GetFinalPriceWithVAT : real;
    function toString : string;
    const
    vatRate = 0.15;
  private
    { Private declarations }
    objGeneralRetrieval:TGenRet;
    sDBName,sDBMail,sDBID,sDBPhone,sDBBirth:string;
    iDBPeople:integer;
    rTranPrice:real;
    arrDestinations: array [0..5] of string;
    arrDestPrice: array [0..5] of integer;
    specialArray: array [1..10] of char;
    procedure DatabasePrep;
    procedure CasesForRadioGroups;
    procedure Validations;
    procedure ValidationForPeopleAmount;
    procedure ValidationForID;
    procedure ValidationForEmail;
    procedure ValidationForName;
    procedure ValidationForPhoneNum;
    procedure ValidationForDOB;
    procedure RadioCheck;
  public
    { Public declarations }
    iPeopleAmount:integer;
    sBookName,sID,sPhoneNum,sEmail,sDOB,sDestName:string;
    sClientNum:string;
    sAccPrint,sTransportPrint,sTLP:string;
    rPrelimPrice:real;
    rPriceWithVAT:real;
    rPrice:real;
  end;

var
  frmTourBooking: TfrmTourBooking;

implementation

{$R *.dfm}


//this button resets the form
procedure TfrmTourBooking.bmbResetClick(Sender: TObject);
begin

//re-disable the radiogroups
rgpTourLength.Enabled:=False;
rgpTransport.Enabled:=False;
rgpAccommodation.Enabled:=False;

//reset the inputs
edtID.Clear;
dtpTime.Date:=Date;
edtName.Clear;
edtPhoneNum.Clear;
edtEmail.Clear;
rgpDestination.ItemIndex:= -1;
rgpAccommodation.ItemIndex:= -1;
rgpTransport.ItemIndex:= -1;
rgpTourLength.ItemIndex:= -1;
sedPeopleAmount.Value:= 0;

//clear the output
redOut.Clear;

end;

//this button calculates everything and gives an output afterwards
procedure TfrmTourBooking.btnCalcClick(Sender: TObject);
begin
  Validations;
  CasesForRadioGroups;
  redOut.lines.Add(toString);
  DatabasePrep;
end;

//validates all the inputs
procedure TfrmTourBooking.Validations;

begin

  //people amount check
  ValidationForPeopleAmount;
  //ID check
  ValidationForID;
  //Email check
  ValidationForEmail;
  //Name check
  ValidationForName;
  //phone number check
  ValidationForPhoneNum;
  //DOB check
  ValidationForDOB;
  //if none of the options on the radio groups are checked
  RadioCheck;

end;



//the seperate validation procedures
procedure TfrmTourBooking.ValidationForPeopleAmount;
begin

  if (sedPeopleAmount.Value < 1) then
  begin
    ShowMessage('Error: please enter a valid amount of people going on the tour');
    //disable the radiogroups
    rgpTourLength.Enabled := False;
    rgpTransport.Enabled := False;
    rgpAccommodation.Enabled := False;
    //reset the inputs
    edtID.Clear;
    edtName.Clear;
    edtPhoneNum.Clear;
    edtEmail.Clear;
    dtpTime.Date:=Date;
    rgpDestination.ItemIndex := -1;
    rgpAccommodation.ItemIndex := -1;
    rgpTransport.ItemIndex := -1;
    rgpTourLength.ItemIndex := -1;
    sedPeopleAmount.Value := 0;
    //clear the output
    redOut.Clear;
  end;

  if (sedPeopleAmount.Value > 30) then
  begin
    ShowMessage('Error: please enter a valid amount of people going on the tour');
    //disable the radiogroups
    rgpTourLength.Enabled := False;
    rgpTransport.Enabled := False;
    rgpAccommodation.Enabled := False;
    //reset the inputs
    edtID.Clear;
    edtName.Clear;
    edtPhoneNum.Clear;
    edtEmail.Clear;
    rgpDestination.ItemIndex := -1;
    rgpAccommodation.ItemIndex := -1;
    rgpTransport.ItemIndex := -1;
    rgpTourLength.ItemIndex := -1;
    sedPeopleAmount.Value := 0;
    //clear the output
    redOut.Clear;
  end;

end;

procedure TfrmTourBooking.ValidationForID;
var
  iIDLength: integer;
  c:char;
  i: Integer;
begin
  iIDLength := Length(edtID.Text);

  //array of special characters
  specialArray[1]:= '@';
  specialArray[2]:= '!';
  specialArray[3]:= '#';
  specialArray[4]:= '$';
  specialArray[5]:= '%';
  specialArray[6]:= '^';
  specialArray[7]:= '&';
  specialArray[8]:= '*';
  specialArray[9]:= '(';
  specialArray[10]:= ')';
  //end of array

  if (iIDLength = 13) then
  begin

    for c in sID do
    begin
      for i := 1 to 10 do
      begin

      if c = specialArray[i] then
      begin
        ShowMessage('Error: invalid format');
          //disable the radiogroups
        rgpTourLength.Enabled := False;
        rgpTransport.Enabled := False;
        rgpAccommodation.Enabled := False;
        //reset the inputs
        edtID.Clear;
        edtName.Clear;
        edtPhoneNum.Clear;
        edtEmail.Clear;
        rgpDestination.ItemIndex := -1;
        rgpAccommodation.ItemIndex := -1;
        rgpTransport.ItemIndex := -1;
        rgpTourLength.ItemIndex := -1;
        sedPeopleAmount.Value := 0;
        //clear the output
        redOut.Clear;
      end;

      end;
    end;

  end;
end;

procedure TfrmTourBooking.ValidationForEmail;
begin

end;

procedure TfrmTourBooking.ValidationForName;
begin

end;

procedure TfrmTourBooking.ValidationForPhoneNum;
begin

end;

procedure TfrmTourBooking.ValidationForDOB;
begin

end;

procedure TfrmTourBooking.RadioCheck;
begin

end;
//end of validation procedures



//outputs everything to the rich edit
function TfrmTourBooking.toString : string;
var
firstLine,secondLine,thirdLine,fourthLine,fifthLine:string;
output:string;
begin
  iPeopleAmount:= sedPeopleAmount.Value;
  sEmail:=edtEmail.Text;
  sDOB:=DateToStr(dtpTime.Date);
  sBookName:=edtName.Text;
  sID:=edtID.Text;
  sPhoneNum:=edtPhoneNum.Text;
  rPrice:= GetFinalPriceWithVAT;
  firstLine:= 'QUOTATION FOR: '+ sClientNum + #13+#13+'Amount of people going on the tour: '+ IntToStr(iPeopleAmount) + #13 + #13 ;
  secondLine:='CLIENT DETAILS: '+ #13 + 'Name of booker: '+ sBookName + #13 + 'DOB: ' + sDOB + #13 + 'ID number: '+ sID + #13 + 'Contact Number: '+ sPhoneNum +#13+ 'Email: '+ sEmail + #13 + #13;
  thirdLine:= 'TOUR DETAILS: ' + #13 + 'Destination: ' + sDestName + #13 + 'Tour Length: '+ sTLP +#13+ 'Transport type: ' + sTransportPrint + #13 + 'Accomodation: '+ sAccPrint + #13 + #13;
  fourthLine:= 'PRICING: ' + #13 + FloatToStrF(rPrice,ffCurrency,6,2)+ #13;
  fifthLine:= #13 + '©The Travel Agency 2020';
  output:= firstLine + secondLine + thirdLine + fourthLine + fifthLine;
  Result:= output;
end;

//this procedure is to handle the nitty gritty of the database input
procedure TfrmTourBooking.DatabasePrep;
begin
  //create the object
  objGeneralRetrieval := TGenRet.Create(edtName.Text, edtID.Text, edtPhoneNum.Text, sedPeopleAmount.Value, edtEmail.Text);

  //get variables for db input from object
  sDBName:=objGeneralRetrieval.GetName;
  sDBID:=objGeneralRetrieval.GetID;
  sDBMail:=objGeneralRetrieval.GetEmail;
  sDBPhone:=objGeneralRetrieval.GetPhoneNum;
  iDBPeople:=objGeneralRetrieval.GetPeopleAmount;
  sDBBirth:=sDOB;
  sClientNum := AnsiUpperCase(objGeneralRetrieval.GetClientNum);

  //destroy object when done
  objGeneralRetrieval.Free;
end;

//this procedure is for the multiple radio groups' outcomes
procedure TfrmTourBooking.CasesForRadioGroups;
var
  iBasePrice: Integer;
  rMultiplied: Real;
  rAdded: Real;
begin
  iBasePrice:=0;
  rMultiplied:=0;
  rAdded:=0;
  //the starting prices
  arrDestPrice[1]:=4000;
  arrDestPrice[2]:=5000;
  arrDestPrice[3]:=2500;
  arrDestPrice[4]:=3000;
  arrDestPrice[5]:=1950;

  //when you choose the destination
  case (rgpDestination.ItemIndex) of
    0:
      begin
        iBasePrice := arrDestPrice[1];
        sDestName := arrDestinations[1];
      end;
    1:
      begin
        iBasePrice := arrDestPrice[2];
        sDestName := arrDestinations[2];
      end;
    2:
      begin
        iBasePrice := arrDestPrice[3];
        sDestName := arrDestinations[3];
      end;
    3:
      begin
        iBasePrice := arrDestPrice[4];
        sDestName := arrDestinations[4];
      end;
    4:
      begin
        iBasePrice := arrDestPrice[5];
        sDestName := arrDestinations[5];
      end;
  end;

  //When you choose the length of the tour
  case (rgpTourLength.ItemIndex) of
    0:
      begin
        rMultiplied := (iBasePrice * 1.5);
        sTLP:='3 days';
      end;
    1:
      begin
        rMultiplied := (iBasePrice * 2);
        sTLP:='5 days';
      end;
    2:
      begin
        rMultiplied := (iBasePrice * 2.5);
        sTLP:='7 days';
      end;
  end;

  //When you choose the accommodation
  case (rgpAccommodation.ItemIndex) of
    0:
      begin
        rAdded := 1500;
        sAccPrint:='Hotel';
      end;
    1:
      begin
        rAdded := 850;
        sAccPrint:='Guesthouse';
      end;
    2:
      begin
        rAdded := 0;
        sAccPrint:='Own Accommodation';
      end;
  end;

  //When you choose a mode of transport
  case (rgpTransport.ItemIndex) of
    0:
      begin
        rTranPrice := 1000;
        sTransportPrint:='Bus';
      end;
    1:
      begin
        rTranPrice := 3000;
        sTransportPrint:='Flight';
      end;
    2:
      begin
        rTranPrice := 0;
        sTransportPrint:='Own Transport';
      end;
  end;

 rPrelimPrice:= (rMultiplied + rAdded + rTranPrice) * iPeopleAmount;

end;

//to calculate the final price with VAT
function TfrmTourBooking.GetFinalPriceWithVAT : real;
var
vat:real;
begin
vat:= rPrelimPrice * vatRate;
rPriceWithVAT := vat + rPrelimPrice;
Result:= rPriceWithVAT;
end;

//this is when the form starts
procedure TfrmTourBooking.FormActivate(Sender: TObject);
begin
//disable other radio groups than destination at startup
rgpTourLength.Enabled:=False;
rgpAccommodation.Enabled:=False;
rgpTransport.Enabled:=False;

//array initialize
arrDestinations[1]:='Cape Town';
arrDestinations[2]:='Camps Bay';
arrDestinations[3]:='Mossel Bay';
arrDestinations[4]:='Knysna';
arrDestinations[5]:='Oudtshoorn';
//end of array initialize

end;


//these are to disable the radio groups until the previous group is clicked
procedure TfrmTourBooking.rgpAccommodationClick(Sender: TObject);
begin
rgpTransport.Enabled:=True;
end;

procedure TfrmTourBooking.rgpDestinationClick(Sender: TObject);
begin
rgpTourLength.Enabled:=True;
end;

procedure TfrmTourBooking.rgpTourLengthClick(Sender: TObject);
begin
rgpAccommodation.Enabled:=True;
end;

end.

让我们看看您的按钮单击处理程序,它负责填充 Rich Edit 控件:

procedure TfrmTourBooking.btnCalcClick(Sender: TObject);
begin
  Validations;
  CasesForRadioGroups;
  redOut.lines.Add(toString);
  DatabasePrep;
end;

如果您查看创建输出的 toString 方法,您会发现它使用了 public sClientNum 字段。

你什么时候真正设置这个变量?它仅在 DatabasePrep 中设置一次。所以在你有 运行 DatabasePrep 之前,这个字符串将是空字符串(因为 class 字段总是被初始化)。

那么,你什么时候打电话给 DatabasePrep?您只能在 btnCalcClick 中调用它,但只能在填充 Rich Edit 控件后调用

因此,第一次单击该按钮时,您将获得空字符串的输出,而第二次您将获得完整的输出。


您的代码中还有其他问题:

  • 你应该refactor it, especially to avoid repeating yourself。例如,您应该创建一个 ResetForm 方法。

  • 每次创建一个对象,都要对其进行保护,以免内存泄露:

    procedure TfrmTourBooking.DatabasePrep;
    begin
    
      objGeneralRetrieval := TGenRet.Create(edtName.Text, edtID.Text, edtPhoneNum.Text, sedPeopleAmount.Value, edtEmail.Text);
      try
        sDBName := objGeneralRetrieval.GetName;
        sDBID := objGeneralRetrieval.GetID;
        sDBMail := objGeneralRetrieval.GetEmail;
        sDBPhone := objGeneralRetrieval.GetPhoneNum;
        iDBPeople := objGeneralRetrieval.GetPeopleAmount;
        sDBBirth := sDOB;
        sClientNum := AnsiUpperCase(objGeneralRetrieval.GetClientNum);
      finally
        objGeneralRetrieval.Free;
       end;
    
    end;
    
  • objGeneralRetrieval变量只在DatabasePrep中使用,所以应该是那里的局部变量。将它作为 class 字段是很危险的,特别是因为在您释放对象后没有将指针设置为 nil.

    之后它将成为 dangling pointer
  • 在微软Windows平台上,newline sequence是CRLF:#13#10


其他一些提示:

  • specialArray应该是常量,不是变量:

    const
      SpecialChars: array[1..10] of Char = '@!#$%^&*()';
    
  • 当你迭代这个数组时,你会做 for i := 1 to 10 do。但是,如果您添加了一个新的特殊字符而忘记更新此 for 循环行怎么办?最好做 for i := Low(SpecialChars) to High(SpecialChars)。更好的是使用 for..in 循环。

  • 此代码:

    case (rgpDestination.ItemIndex) of
      0:
        begin
          iBasePrice := arrDestPrice[1];
          sDestName := arrDestinations[1];
        end;
      1:
        begin
          iBasePrice := arrDestPrice[2];
          sDestName := arrDestinations[2];
        end;
      2:
        begin
          iBasePrice := arrDestPrice[3];
          sDestName := arrDestinations[3];
        end;
      3:
        begin
          iBasePrice := arrDestPrice[4];
          sDestName := arrDestinations[4];
        end;
      4:
        begin
          iBasePrice := arrDestPrice[5];
          sDestName := arrDestinations[5];
        end;
    end;
    

    可以写得更简洁:

    if rgpDestination.ItemIndex <> -1 then
    begin
      iBasePrice := arrDestPrice[rgpDestination.ItemIndex + 1];
      sDestName := arrDestinations[rgpDestination.ItemIndex + 1];
    end;
    

    更少的代码更易于阅读和推理。减少愚蠢错别字的风险。