Delphi 如何使数据感知控件和非数据感知控件的组合彼此保持同步,并与它们所操作的数据库保持同步?

Delphi 如何使数据感知控件和非数据感知控件的组合彼此保持同步,并与它们所操作的数据库保持同步?,delphi,data-aware,Delphi,Data Aware,我有一个有7个控件的窗体。两个控件是数据感知的,一个TDBGrid和一个TDBNavigator。另外三个不支持数据,一个TJvCalendar2和两个TjvDateEdits。最后两个控件是作为数据源数据集的TDataSource和TTzDbf 在我的一生中,我无法弄清楚如何使用JvCalendar或任何一个JvDateEdits上的日期更新当前数据库记录,而不会引发灾难性的争用情况,导致程序崩溃 在表单的OnActivate方法中,我将数据库当前定位的记录中的数据复制到表单变量中。然后调用两

我有一个有7个控件的窗体。两个控件是数据感知的,一个TDBGrid和一个TDBNavigator。另外三个不支持数据,一个TJvCalendar2和两个TjvDateEdits。最后两个控件是作为数据源数据集的TDataSource和TTzDbf

在我的一生中,我无法弄清楚如何使用JvCalendar或任何一个JvDateEdits上的日期更新当前数据库记录,而不会引发灾难性的争用情况,导致程序崩溃

在表单的OnActivate方法中,我将数据库当前定位的记录中的数据复制到表单变量中。然后调用两个方法,一个用于更新JvCalendar,另一个用于更新两个JvDateEdits

这两个方法保存各自控件的OnChange处理程序,然后将其设置为nil,设置控件的日期,恢复控件的原始OnChange处理程序,然后退出

为了跟踪数据集何时被移动,我保存并替换数据集的后滚动和前滚动事件。当dbGrid中的当前行发生更改时,通过在dbGrid中单击鼠标或移动光标,或者通过在dbNavigator中更改记录,这些处理程序在BeforeColl或retrieve期间从表单变量更新数据库的记录,设置表单变量,然后更新JvCalendar和JvDateEdits

在BeforeColl事件期间保存、更新数据库记录会导致重新读取记录、更新控件,然后重写数据库记录。所有这些都会导致循环、堆栈空间耗尽和崩溃

我对事件处理程序和数据感知控件的理解和实现中缺少了什么

完整的示例代码如下:

-----------------------RaceCondition.dpr----------------------

/// <summary>
///   An application to demonstrate one programmer's incomplete 
understanding
///   of data control's event system
/// </summary>
program RaceConditionDpr;

uses
  /// <summary>
  ///   Forms, forms and more forms
  /// </summary>
  Forms,
  /// <summary>
  ///   The application's main form with controls to try to plead for help
  ///   at understanding data control's interactions
  /// </summary>
  RaceConditionFrm in 'RaceConditionFrm.pas' {Form5};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm5, Form5);
  Application.Run;
end.
/// <summary>
///   Unit containing the application, RaceConditionDpr's main form.Uses
///   several third party controls:
///   <list type="number">
///     <item>
///       JEDI's TJvMonthCalendar2
///     </item>
///     <item>
///       JEDI's TJvDateEdit
///     </item>
///     <item>
///       Topaz' TTzDbf dataset. This might be able to be substituted by
///       another dataset type and still demonstrate the race condition
///       problem that this application is intended to convey.
///     </item>
///   </list>
///   Uses several third party libraries:
///   <list type="number">
///     <item>
///       TurboPower's SysTools for routines in its StDate and StDateSt
///       units
///     </item>
///   </list>
/// </summary>
/// <remarks>
///   Has 7 controls on a single form
///   <list type="bullet">
///     <item>
///       Two controls are data aware, a TDBGrid and a TDBNavigator.
///     </item>
///     <item>
///       Three others are not data aware, a TJvCalendar2 and two
///       TjvDateEdits.
///     </item>
///     <item>
///       The last two controls are a TDataSource and a TTzDbf as the
///       dataSource’s dataset.
///     </item>
///   </list>
/// </remarks>
unit RaceConditionFrm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DB, tzprimds, ucommon, utzcds, utzfds, StdCtrls, Mask, JvExMask,
  JvToolEdit, JvExControls, JvCalendar, ExtCtrls, DBCtrls, Grids, DBGrids;

{$ifdef WIN32}
 {$A-}  {byte alignment}
{$else}
 {$ifdef LINUX}
  {$A-}  {byte alignment}
 {$endif}
{$endif}

type
  /// <summary>
  ///   Defines the type used to hold a dBase date in 'yyyymmdd' form. The
  ///   actual .dbf holds the date in this 'yyyymmdd' form but
  ///   retrieval/storage methods may insert date separators between the three
  ///   portions of the date, ie: 'mm/dd/yyyy' if the date locality has been
  ///   set to American.
  /// </summary>
  Tstring10 = string[10];     { for Date fields }
  /// <summary>
  ///   Record structure reflecting the field structure present in the .dbf.
  /// </summary>
  TDATES_Record = Record
     /// <summary>
     ///   Can be populated with the status of the .dbf record as on disk
     /// </summary>
     /// <value>
     ///   True if the record has been marked as deleted; False if not deleted
     /// </value>
     Deleted         : Boolean;
     /// <summary>
     ///   Field with the first date of the date span stored in the .dbf
     /// </summary>
     _DATEFIRST      : Tstring10;     { Date field }
     /// <summary>
     ///   Field with the last date of the date span stored in the .dbf
     /// </summary>
     _DATELAST       : Tstring10;     { Date field }
  end;

/// <summary>
///   Application's main form
/// </summary>
/// <remarks>
///   Has 7 controls.
///   <list type="bullet">
///     <item>
///       Two controls are data aware, a TDBGrid and a TDBNavigator.
///     </item>
///     <item>
///       Three others are not data aware, a TJvCalendar2 and two
///       TjvDateEdits.
///     </item>
///     <item>
///       The last two controls are a TDataSource and a TTzDbf as the
///       dataSource’s dataset.
///     </item>
///   </list>
/// </remarks>
  TForm5 = class(TForm)
    /// <summary>
    ///   dataaware control to display a grid of the database's records' data <br /><br />
    ///   Linked to DataSource DataSource1 <br />
    /// </summary>
    DBGrid1: TDBGrid;
    /// <summary>
    ///   <para>
    ///     dataaware control to ease user re-positioning of the database's
    ///     record pointer
    ///   </para>
    ///   <para>
    ///     Linked to DataSource DataSource1
    ///   </para>
    /// </summary>
    DBNavigator1: TDBNavigator;
    /// <summary>
    ///   <para>
    ///     Cool calendar control that can be configured to display more than
    ///     one month at a time. Will also display a time span in days and
    ///     this across multiple months.
    ///   </para>
    ///   <para>
    ///     Thanks JEDI
    ///   </para>
    /// </summary>
    JvMonthCalendar21: TJvMonthCalendar2;
    /// <summary>
    ///   <para>
    ///     An edit control that drops down a calendar to permit selecting a
    ///     date in a nice natural way. Selects the date that will become the
    ///     DateFirst date.
    ///   </para>
    ///   <para>
    ///     Thanks, again, JEDI
    ///   </para>
    /// </summary>
    JvDateEditDateFirst: TJvDateEdit;
    /// <summary>
    ///   <para>
    ///     An edit control that drops down a calendar to permit selecting a
    ///     date in a nice natural way. Selects the date that will become the
    ///     DateLast date.
    ///   </para>
    ///   <para>
    ///     Thanks, again, JEDI
    ///   </para>
    /// </summary>
    JvDateEditDateLast: TJvDateEdit;
    /// <summary>
    ///   <para>
    ///     the DataSource for the application.
    ///   </para>
    ///   <para>
    ///     Linked to DataSet TzDbf1
    ///   </para>
    /// </summary>
    DataSource1: TDataSource;
    /// <summary>
    ///   <para>
    ///     the DataSet for the application.
    ///   </para>
    ///   <para>
    ///     Linked to DataSource DataSource1
    ///   </para>
    /// </summary>
    TzDbf1: TTzDbf;
    /// <summary>
    ///   When the form gains focus, updates the non-data aware controls with
    ///   the contents of the current database record
    /// </summary>
    procedure FormActivate(Sender: TObject);
    /// <summary>
    ///   <para>
    ///     OnChange event handler called after the DateEdit1 control has
    ///     been changed, either by user interaction or by having its date
    ///     programmatically set.
    ///   </para>
    ///   <para>
    ///     With the control possibly having been edited by the user, it then
    ///     calls UpdateJvMontCalendar to update the calendar too.
    ///   </para>
    /// </summary>
    procedure JvDateEditDateFirstChange(Sender: TObject);
    /// <summary>
    ///   <para>
    ///     OnChange event handler called after the DateEdit2 control has
    ///     been changed, either by user interaction or by having its date
    ///     programmatically set.
    ///   </para>
    ///   <para>
    ///     With the control possibly having been edited by the user, it then
    ///     calls UpdateJvMontCalendar to update the calendar too.
    ///   </para>
    /// </summary>
    procedure JvDateEditDateLastChange(Sender: TObject);
    /// <summary>
    ///   <para>
    ///     OnChange event handler called after the Calendar control has been
    ///     changed, either by user interaction or by having its StartDate
    ///     and/or EndDate programmatically set.
    ///   </para>
    ///   <para>
    ///     With the control possibly having been edited by the user, it then
    ///     calls UpdateJvDateEdits to update the two DateEdit controls too.
    ///   </para>
    /// </summary>
    /// <param name="StartDate">
    ///   The first, earliest date on the calendar control
    /// </param>
    /// <param name="EndDate">
    ///   The second, later date on the calendar control. May be the same date
    ///   as the StartDate if the user has not selected different dates by
    ///   shift-clicking on a second date. The two dates will have been sorted
    ///   to supply the handler with the two different dates in ascending
    ///   order.
    /// </param>
    procedure JvMonthCalendar21SelChange(Sender: TObject; StartDate,
      EndDate: TDateTime);
    /// <summary>
    ///   <para>
    ///     OnAfterScroll event handler for the DataSet.
    ///   </para>
    ///   <para>
    ///     Called once the dataset has settled on what has become the
    ///     current record.
    ///   </para>
    ///   <para>
    ///     Causes the data in the FDates instance variable to be read, from
    ///     the database from its current record
    ///   </para>
    /// </summary>
    procedure TzDbf1AfterScroll(DataSet: TDataSet);
    /// <summary>
    ///   <para>
    ///     OnBeforeScroll event handler for the DataSet. <br /><br />Called
    ///     before the dataset leaves the current record to begin a move to
    ///     another.
    ///   </para>
    ///   <para>
    ///     Causes the data in the FDates instance variable to be written,
    ///     posted, to the database <br />
    ///   </para>
    /// </summary>
    procedure TzDbf1BeforeScroll(DataSet: TDataSet);
  private
    { Private declarations }
    /// <summary>
    ///   <para>
    ///     Instance variable to serve as the holder of values read from the
    ///     .dbf and input by the user by interaction with the form.
    ///   </para>
    ///   <para>
    ///     To be written to the .dbf to replace the field values on the
    ///     current record when the dataset is about to be repositioned.
    ///   </para>
    ///   <para>
    ///     To be populated by the field values on what comes to be the
    ///     current record after the dataset has been repositioned to what is
    ///     now the current record. Will have its field values modified when
    ///     the user interacts with the controls on the form.
    ///   </para>
    /// </summary>
    FDates : TDATES_Record;
    /// <summary>
    ///   Called to update the two date edit controls.
    ///   <list type="bullet">
    ///     <item>
    ///       Updates the DateEdit1 control with the DateFirst value in the
    ///       FDates record
    ///     </item>
    ///     <item>
    ///       Updates the DateEdit2 control with the DateLast value in the
    ///       FDates record <br />
    ///     </item>
    ///   </list>
    /// </summary>
    procedure UpdateJvDateEdits;
    /// <summary>
    ///   Called to update the calendar control.
    ///   <list type="bullet">
    ///     <item>
    ///       Updates the DateFirst property with the DateFirst value in
    ///       the FDates record
    ///     </item>
    ///     <item>
    ///       Updates the DateLast property with the DateLast value in the
    ///       FDates record <br />
    ///     </item>
    ///   </list>
    /// </summary>
    procedure UpdateJvMonthCalendar;
    /// <summary>
    ///   <para>
    ///     Update the .dbf wth the values modified by user interaction with
    ///     the form's controls, that is from instance variable FDates.
    ///   </para>
    ///   <para>
    ///     Writes FDates values to the current database record.
    ///   </para>
    /// </summary>
    procedure UpdateDbf;
    /// <summary>
    ///   Utility method to convert a Topaz style date string into a TDateTime
    ///   equivalent
    /// </summary>
    /// <param name="aTopazDate">
    ///   Date as string in 'yyyymmdd' format
    /// </param>
    /// <returns>
    ///   the equivalent date as a TDateTime
    /// </returns>
    function TopazToDate( const aTopazDate : Tstring10 ): TDateTime;
    /// <summary>
    ///   Utility method to convert a TDateTime into the equivalent Topaz style
    ///   date string in 'yyyymmdd' format
    /// </summary>
    /// <param name="aDate">
    ///   Date as TDateTime in format <br />
    /// </param>
    /// <returns>
    ///   the equivalent date as a string in 'yyyymmdd' format
    /// </returns>
    function DateToTopaz( aDate : TDateTime ): Tstring10;
  public
    { Public declarations }
  end;

var
  /// <summary>
  ///   Instance variable holding the form
  /// </summary>
  Form5: TForm5;

implementation

{$R *.dfm}

uses
  StDate,
  StDateSt;

const
  /// <summary>
  ///   constant for use in converting Topaz string dates to and from TDateTime
  /// </summary>
  zYYYYdMMdDDmask = 'yyyy.mm.dd';
//  zyyyymmddMask = 'yyyymmdd';

procedure TForm5.FormActivate(Sender: TObject);
begin
  FDates._DATEFIRST := TzDbf1.GetDField( 'DateFirst' );
  FDates._DATELAST := TzDbf1.GetDField( 'DateLast' );
  UpdateJvDateEdits;
  UpdateJvMonthCalendar;
end;

procedure TForm5.TzDbf1AfterScroll(DataSet: TDataSet);
begin
  UpdateJvDateEdits;
  UpdateJvMonthCalendar;
end;

procedure TForm5.TzDbf1BeforeScroll(DataSet: TDataSet);
begin
  UpdateDbf;
end;

procedure TForm5.UpdateDbf;
begin
//  TzDbf1.DisableControls;
  repeat
    asm nop end;
  until (TzDbf1.RLock);
  TzDbf1.SetDField( 'DateFirst', FDates._DATEFIRST );
  TzDbf1.SetDField( 'DateLast',  FDates._DATELAST );
  TzDbf1.ReplaceRec;
  TzDbf1.UnLock;
//  TzDbf1.EnableControls;
end;

procedure TForm5.UpdateJvDateEdits;
var
  EventSaved : TNotifyEvent;
begin
  EventSaved := JvDateEditDateFirst.OnChange;
  JvDateEditDateFirst.OnChange := nil;
  JvDateEditDateFirst.Date := TopazToDate( FDates._DATEFIRST );
  JvDateEditDateFirst.OnChange := EventSaved;

  EventSaved := JvDateEditDateLast.OnChange;
  JvDateEditDateLast.OnChange := nil;
  JvDateEditDateLast.Date := TopazToDate( FDates._DATELAST );
  JvDateEditDateLast.OnChange := EventSaved;
end;

procedure TForm5.UpdateJvMonthCalendar;
var
  EventSaved : TJvMonthCalSelEvent;
begin
  EventSaved := JvMonthCalendar21.OnSelChange;
  JvMonthCalendar21.OnSelChange := nil;

  JvMonthCalendar21.DateFirst := TopazToDate( FDates._DATEFIRST );
  JvMonthCalendar21.DateLast := TopazToDate( FDates._DATELAST );

  JvMonthCalendar21.OnSelChange := EventSaved;
end;

procedure TForm5.JvDateEditDateFirstChange(Sender: TObject);
begin
  FDates._DATEFIRST := DateToTopaz( JvDateEditDateFirst.Date );

  UpdateJvMonthCalendar;
end;

procedure TForm5.JvDateEditDateLastChange(Sender: TObject);
begin
  FDates._DATELAST := DateToTopaz( JvDateEditDateLast.Date );

  UpdateJvMonthCalendar;
end;

procedure TForm5.JvMonthCalendar21SelChange(Sender: TObject; StartDate,
  EndDate: TDateTime);
begin
  FDates._DATEFIRST := DateToTopaz( StartDate );
  FDates._DATELAST := DateToTopaz( EndDate );

  UpdateJvDateEdits;
end;

function TForm5.TopazToDate( const aTopazDate : Tstring10 ): TDateTime;
var
  anStDate : StDate.TStDate;
begin
  anStDate := stdatest.DateStringToStDate( zYYYYdMMdDDmask, aTopazDate, 2000 );
  Result := StDate.StDateToDateTime( anStDate );
end;

function TForm5.DateToTopaz(aDate: TDateTime): Tstring10;
var
  anStDate : StDate.TStDate;
begin
  anStDate := StDate.DateTimeToStDate( aDate );
  Result := StDateSt.StDateToDateString( zYYYYdMMdDDmask, anStDate, False );
end;

end.
object Form5: TForm5
  Left = 0
  Top = 0
  Caption = 'Form5'
  ClientHeight = 336
  ClientWidth = 628
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnActivate = FormActivate
  PixelsPerInch = 96
  TextHeight = 13
  object DBGrid1: TDBGrid
    Left = 8
    Top = 8
    Width = 320
    Height = 120
    DataSource = DataSource1
    TabOrder = 0
    TitleFont.Charset = DEFAULT_CHARSET
    TitleFont.Color = clWindowText
    TitleFont.Height = -11
    TitleFont.Name = 'Tahoma'
    TitleFont.Style = []
  end
  object DBNavigator1: TDBNavigator
    Left = 8
    Top = 134
    Width = 240
    Height = 25
    DataSource = DataSource1
    TabOrder = 1
  end
  object JvMonthCalendar21: TJvMonthCalendar2
    Left = 168
    Top = 168
    Width = 451
    ParentColor = False
    TabStop = True
    TabOrder = 2
    DateFirst = 43364.000000000000000000
    DateLast = 43364.000000000000000000
    MaxSelCount = 366
    MultiSelect = True
    Today = 43364.458842245370000000
    OnSelChange = JvMonthCalendar21SelChange
  end
  object JvDateEditDateFirst: TJvDateEdit
    Left = 24
    Top = 192
    Width = 121
    Height = 21
    ShowNullDate = False
    StartOfWeek = Sun
    TabOrder = 3
    OnChange = JvDateEditDateFirstChange
  end
  object JvDateEditDateLast: TJvDateEdit
    Left = 24
    Top = 240
    Width = 121
    Height = 21
    ShowNullDate = False
    StartOfWeek = Sun
    TabOrder = 4
    OnChange = JvDateEditDateLastChange
  end
  object DataSource1: TDataSource
    DataSet = TzDbf1
    Left = 408
    Top = 64
  end
  object TzDbf1: TTzDbf
    Active = True
    BeforeScroll = TzDbf1BeforeScroll
    AfterScroll = TzDbf1AfterScroll
    DbfFields.Strings = (
      'datefirst, D, 10, 0'
      'datelast, D, 10, 0')
    DbfFileName = 
      'f:\delphi projects\theo\fillsound in delphi for mdx on 20161109\' +
      'dunit\holidaytracking\race condition\dates.dbf'
    HideDeletedRecs = False
    TableLanguage = tlOem
    ReadOnly = False
    CreateIndex = ciNotFound
    Exclusive = True
    Left = 496
    Top = 64
  end
end

在这种情况下,明智的做法是使用一个全局标志,您可以检查(并避免不必要的)递归

var
   FImCallingMyself: Boolean;

procedure callsitself;
begin
   if FImcallingmyself then
     EXIT;
   FImcallingmself := True;
   try
     // do stuff
   finally
      FImcallingmyself := False;
   end;
end;

在这种情况下,明智的做法是使用一个全局标志,您可以检查(并避免不必要的)递归

var
   FImCallingMyself: Boolean;

procedure callsitself;
begin
   if FImcallingmyself then
     EXIT;
   FImcallingmself := True;
   try
     // do stuff
   finally
      FImcallingmyself := False;
   end;
end;

我使用
BeforePost
方法读取非db感知控件中的值并设置记录值,使用
AfterScroll
方法设置非db感知控件

[编辑以显示一些基本示例代码]
BeforePost
的整个概念是有机会更改记录中的字段。这是我一直在做的一个简单的伪例子。在本例中,我使用的是Win10日期选择器。我的单位还有一个日期的私有变量,因为我还需要转换为希伯来日历。我在
BeforePost
方法中的
aftercroll
中检查日期选择器是否与原始日期发生了更改,然后在记录中设置字段

    unit uYarzheit;
...
type
   TYarzheitForm = class(Tform)
   ...
   fdqYz : tTFDQuery;
   ...
   dpCivilDoD : TDatePicker;
   ...
   procedure fdqYzAfterScroll(DataSet : TDataSet);
   procedure fdqYzBeforePost(DataSet : TDataSet);
   ...
   private
     dbCDod  : tdatetime;
    ....

  implementaion
  ...

  procedure TYarzheitForm.fdqYzAfterScroll(DataSet : TDataSet);
  begin
  ....
     dbCDoD := fdqYz.FieldByName('MilestoneDate').AsDateTime;
     dpCivilDoD.date := dbCDoD;
  ...
  end;

  procedure TYarzheitForm.fdqYzBeforePost(DataSet : TDataSet);
  begin
    if dpCivilDoD.Date <> dbCDoD then 
     fdqYz.FieldByName('MilestoneDate').AsDateTime := dpCivilDoD.Date;
  end;
end;
单位uYarzheit;
...
类型
TYarzheitForm=类(Tform)
...
fdqYz:tTFDQuery;
...
dpdod:TDatePicker;
...
程序fdqYzAfterScroll(数据集:TDataSet);
程序fdqYzBeforePost(数据集:TDataSet);
...
私有的
dbCDod:tdatetime;
....
实施
...
程序TYarzheitForm.fdqYzAfterScroll(数据集:TDataSet);
开始
....
dbCDoD:=fdqYz.FieldByName('MilestoneDate').AsDateTime;
dpCivilDoD.date:=dbCDoD;
...
结束;
过程TYarzheitForm.fdqYzBeforePost(数据集:TDataSet);
开始
如果是dpCivilDoD.Date dbCDoD,则
fdqYz.FieldByName('MilestoneDate').AsDateTime:=dpCivilDoD.Date;
结束;
结束;

BeforePost
方法是在将记录更改写入数据库之前进行各种验证的好地方(例如,从文本字段中删除尾随空格)。

我使用
BeforePost
方法读取非db感知控件中的值并设置记录的值,以及用于设置非db感知控件的
AfterScroll
方法

[编辑以显示一些基本示例代码]
BeforePost
的整个概念是有机会更改记录中的字段。这是我一直在做的一个简单的伪例子。在本例中,我使用的是Win10日期选择器。我的单位还有一个日期的私有变量,因为我还需要转换为希伯来日历。我在
BeforePost
方法中的
aftercroll
中检查日期选择器是否与原始日期发生了更改,然后在记录中设置字段

    unit uYarzheit;
...
type
   TYarzheitForm = class(Tform)
   ...
   fdqYz : tTFDQuery;
   ...
   dpCivilDoD : TDatePicker;
   ...
   procedure fdqYzAfterScroll(DataSet : TDataSet);
   procedure fdqYzBeforePost(DataSet : TDataSet);
   ...
   private
     dbCDod  : tdatetime;
    ....

  implementaion
  ...

  procedure TYarzheitForm.fdqYzAfterScroll(DataSet : TDataSet);
  begin
  ....
     dbCDoD := fdqYz.FieldByName('MilestoneDate').AsDateTime;
     dpCivilDoD.date := dbCDoD;
  ...
  end;

  procedure TYarzheitForm.fdqYzBeforePost(DataSet : TDataSet);
  begin
    if dpCivilDoD.Date <> dbCDoD then 
     fdqYz.FieldByName('MilestoneDate').AsDateTime := dpCivilDoD.Date;
  end;
end;
单位uYarzheit;
...
类型
TYarzheitForm=类(Tform)
...
fdqYz:tTFDQuery;
...
dpdod:TDatePicker;
...
程序fdqYzAfterScroll(数据集:TDataSet);
程序fdqYzBeforePost(数据集:TDataSet);
...
私有的
dbCDod:tdatetime;
....
实施
...
程序TYarzheitForm.fdqYzAfterScroll(数据集:TDataSet);
开始
....
dbCDoD:=fdqYz.FieldByName('MilestoneDate').AsDateTime;
dpCivilDoD.date:=dbCDoD;
...
结束;
过程TYarzheitForm.fdqYzBeforePost(数据集:TDataSet);
开始
如果是dpCivilDoD.Date dbCDoD,则
fdqYz.FieldByName('MilestoneDate').AsDateTime:=dpCivilDoD.Date;
结束;
结束;

BeforePost
方法是在将记录更改写入数据库之前进行各种验证的好地方(例如,从文本字段中删除尾随空格)。

首先,需要注意以下几点:

  • 我不确定您是否知道,但是TJvDateEdit有一个db感知版本,tJvDBDateEdit-

  • 有一个Embarcadero教程,介绍如何制作支持db的TMMonthCalendar版本, 它应该很容易适应TJVM日历

第二,我想我将包括一个示例,说明如何使TMD在功能上具有db意识,而不必编写db意识的d