首页 > 解决方案 > 如何从 Delphi 的 Richedit 中获得首次输出?

问题描述

当我的代码输出到富编辑时,我遇到了问题。当我单击按钮计算所有内容然后显示所有内容时,它不会输出生成的 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.

标签: delphidelphi-xe2pascal

解决方案


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

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

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

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

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

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


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

  • 你应该重构它,尤其是避免重复自己。例如,您应该创建一个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,因此它应该是那里的局部变量。将它作为类字段是危险的,特别是因为在您释放对象而不将指针设置nil.

  • 在 Microsoft Windows 平台上,换行符序列为 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;
    

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


推荐阅读