OnCalcFields导致StackOverflow

时间:2012-10-17 13:31:57

标签: delphi stack-overflow

使用Delphi 2005,我有一个TwwDBGrid(InfoPower),允许用户选择部分&数量以生成报价。选择过程的一部分是允许用户为包含DiscountAmount和DiscountType的每个部分添加折扣。 DiscountAmount是折扣金额,DiscountType是折扣金额的类型(%折扣,$ off或平价)。此数据保存在名为ClientDataSetParts的ClientDataSet中。

在OnCalc方法(ClientDataSetPartsCalcFields)中,我有以下代码,它将根据零件成本和任何折扣重新计算TotalPrice。 RangePricing用于根据范围内的数量计算价格的情况(即1-100 = $ 100,101-200 = $ 150等):

procedure TfrmCustom_Services.ClientDataSetPartsCalcFields(
  DataSet: TDataSet);
begin
  inherited;
  // if part is selected then calculate Total
  if (ClientDataSetPartsSelected.Value) then begin
    // if range pricing is defined
    if (ClientDataSetParts.FieldByName('RangePricing').AsBoolean) then begin
      ClientDataSetParts.FieldByName('TotalPrice').Value :=
            ClientDataSetParts.FieldbyName('UnitPrice').Value;
    // otherwise use regular pricing
    end else begin
      ClientDataSetParts.FieldByName('TotalPrice').Value :=
          ClientDataSetParts.FieldbyName('UnitPrice').Value *
          ClientDataSetParts.FieldByName('Quantity').Value;
    end;

  // otherwise clear the Total
  end else begin
    ClientDataSetParts.FieldByName('TotalPrice').Clear;
    if (ClientDataSetParts.FieldByName('Quantity').Value <> null) then
      ClientDataSetParts.FieldByName('Quantity').Clear;
    if (ClientDataSetParts.FieldByName('DiscountAmount').Value <> null) then
      ClientDataSetParts.FieldByName('DiscountAmount').Clear;
    if (ClientDataSetParts.FieldByName('DiscountType').Value <> null) then
      ClientDataSetParts.FieldByName('DiscountType').Clear;
  end;

  // Update totals if Discount is applied
  // Only recalculate if both discount value and type are applied
  // otherwise will constantly get errors when switching fields
  if ((ClientDataSetPartsDiscountAmount.Value > 0) and (ClientDataSetPartsDiscountType.Value <> '')) then begin
    case StringToCaseSelect((ClientDataSetPartsDiscountType.Value), ['% Disc','$ Disc','Price']) of
      0 :
      begin
        ClientDataSetParts.FieldByName('DiscountDollarAmount').Value :=
            ClientDataSetParts.FieldByName('TotalPrice').Value * (ClientDataSetParts.FieldByName('DiscountAmount').Value/100);
        ClientDataSetPartsTotalPrice.Value := ClientDataSetPartsTotalPrice.Value - (ClientDataSetPartsTotalPrice.Value * (ClientDataSetPartsDiscountAmount.Value/100));
      end;
      1 :
      begin
        ClientDataSetParts.FieldByName('DiscountDollarAmount').Value := ClientDataSetPartsDiscountAmount.Value;
        ClientDataSetPartsTotalPrice.Value := ClientDataSetPartsTotalPrice.Value - ClientDataSetPartsDiscountAmount.Value;
      end;
      2 :
      begin
        ClientDataSetParts.FieldByName('DiscountDollarAmount').Value :=
            ClientDataSetPartsTotalPrice.Value - ClientDataSetPartsDiscountAmount.Value;
        ClientDataSetPartsTotalPrice.Value := ClientDataSetPartsDiscountAmount.Value;
      end;
    end;
  end;

end;

当我尝试计算DiscountDollarAmount(我必须通过API返回外部源的折扣的实际美元数字)时出现问题。每次我设置ClientDataSetParts.FieldByName('DiscountDollarAmount')。Value,再次调用ClientDataSetPartsCalcFields导致无限次调用并最终导致堆栈溢出。

如何在不递归调用ClientDataSetPartsCalcFields的情况下更新此值?

2 个答案:

答案 0 :(得分:2)

您进入的无限循环是由代码更改值,看到它已更改并重新启动更改(如您所知)引起的。要解决此问题,请在过程开始时将OnCalc事件设置为nil,然后在结束时将其重新设置为过程。这样,事件监视将在实际处理期间暂停,然后在处理完成后重新开始,并且已分配新值。

答案 1 :(得分:2)

修改非计算字段时会发生此行为。 OnCalcFields事件不应对非计算字段进行任何修改。

我对您的数据库结构一无所知,但如果我的预感是正确的,那么您正在OnCalcFields事件处理程序中的某处修改非计算字段。并且因为您修改了非计算字段,所以需要重新计算计算字段。所以OnCalcFields被称为。{1}}。其中修改了非计算字段。而且,嗯,你可以看到它的发展方向!