Reconc.pas
上传用户:fh681027
上传日期:2022-07-23
资源大小:1959k
文件大小:11k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. {*******************************************************}
  2. {                                                       }
  3. {       Delphi Visual Component Library                 }
  4. {       ClientDataSet Standard Reconcile Error Dialog   }
  5. {                                                       }
  6. {       Copyright (c) 1998 Borland International        }
  7. {                                                       }
  8. {*******************************************************}
  9. { Note: To use this dialog you should add a call to HandleReconcileError in
  10.   the OnReconcileError event handler of TClientDataSet (see the Client dataset
  11.   demos for an example).  Also, after adding this unit to your project you must
  12.   go into the Project Options dialog and remove this form from the list of
  13.   Auto-created forms or an error will occur when compiling. }
  14. unit Reconc;
  15. interface
  16. uses
  17.   SysUtils, Windows, Variants, Messages, Classes, Graphics, Controls, Forms,
  18.   Dialogs, StdCtrls, Grids, DB, DBClient, Provider, ExtCtrls;
  19. const
  20.   ActionStr: array[TReconcileAction] of string = ('Skip', 'Abort', 'Merge',
  21.     'Correct', 'Cancel', 'Refresh');
  22.   UpdateKindStr: array[TUpdateKind] of string = ('Modified', 'Inserted',
  23.     'Deleted');
  24.   SCaption = 'Update Error - %s';
  25.   SUnchanged = '<Unchanged>';
  26.   SBinary = '(Binary)';
  27.   SFieldName = 'Field Name';
  28.   SOriginal = 'Original Value';
  29.   SConflict = 'Conflicting Value';
  30.   SValue = ' Value';
  31.   SNoData = '<No Records>';
  32.   SNew = 'New';
  33. type
  34.   TReconcileErrorForm = class(TForm)
  35.     UpdateType: TLabel;
  36.     UpdateData: TStringGrid;
  37.     ActionGroup: TRadioGroup;
  38.     CancelBtn: TButton;
  39.     OKBtn: TButton;
  40.     ConflictsOnly: TCheckBox;
  41.     IconImage: TImage;
  42.     ErrorMsg: TMemo;
  43.     ChangedOnly: TCheckBox;
  44.     procedure FormCreate(Sender: TObject);
  45.     procedure FormDestroy(Sender: TObject);
  46.     procedure UpdateDataSetEditText(Sender: TObject; ACol, ARow: Integer;
  47.       const Value: string);
  48.     procedure DisplayFieldValues(Sender: TObject);
  49.     procedure UpdateDataSelectCell(Sender: TObject; Col, Row: Integer;
  50.       var CanSelect: Boolean);
  51.   private
  52.     FDataSet: TDataSet;
  53.     FError: EReconcileError;
  54.     FUpdateKind: TUpdateKind;
  55.     FDataFields: TList;
  56.     FCurColIdx: Integer;
  57.     FNewColIdx: Integer;
  58.     FOldColIdx: Integer;
  59.     procedure AdjustColumnWidths;
  60.     procedure InitDataFields;
  61.     procedure InitUpdateData(HasCurValues: Boolean);
  62.     procedure InitReconcileActions;
  63.     procedure SetFieldValues(DataSet: TDataSet);
  64.   public
  65.     constructor CreateForm(DataSet: TDataSet; UpdateKind: TUpdateKind;
  66.       Error: EReconcileError);
  67.   end;
  68. function HandleReconcileError(DataSet: TDataSet;  UpdateKind: TUpdateKind;
  69.   ReconcileError: EReconcileError): TReconcileAction;
  70. implementation
  71. {$R *.dfm}
  72. type
  73.   PFieldData = ^TFieldData;
  74.   TFieldData = record
  75.     Field: TField;
  76.     NewValue: string;
  77.     OldValue: string;
  78.     CurValue: string;
  79.     EditValue: string;
  80.     Edited: Boolean;
  81.   end;
  82. { Public and Private Methods }
  83. function HandleReconcileError(DataSet: TDataSet; UpdateKind: TUpdateKind;
  84.   ReconcileError: EReconcileError): TReconcileAction;
  85. var
  86.   UpdateForm: TReconcileErrorForm;
  87. begin
  88.   UpdateForm := TReconcileErrorForm.CreateForm(DataSet, UpdateKind, ReconcileError);
  89.   with UpdateForm do
  90.   try
  91.     if ShowModal = mrOK then
  92.     begin
  93.       Result := TReconcileAction(ActionGroup.Items.Objects[ActionGroup.ItemIndex]);
  94.       if Result = raCorrect then SetFieldValues(DataSet);
  95.     end else
  96.       Result := raAbort;
  97.   finally
  98.     Free;
  99.   end;
  100. end;
  101. { Routine to convert a variant value into a string.
  102.   Handles binary fields types and "empty" (Unchanged) field values specially }
  103. function VarToString(V: Variant; DataType: TFieldType): string;
  104. const
  105.   BinaryDataTypes: set of TFieldType = [ftBytes, ftVarBytes, ftBlob,
  106.     ftGraphic..ftCursor];
  107. begin
  108.   try
  109.     if VarIsClear(V) then
  110.       Result := SUnchanged
  111.     else if DataType in BinaryDataTypes then
  112.       Result := SBinary
  113.     else
  114.       Result := VarToStr(V);
  115.   except
  116.     on E: Exception do
  117.       Result := E.Message;
  118.   end;
  119. end;
  120. { TReconcileErrorForm }
  121. constructor TReconcileErrorForm.CreateForm(DataSet: TDataSet;
  122.   UpdateKind: TUpdateKind; Error: EReconcileError);
  123. begin
  124.   FDataSet := DataSet;
  125.   FUpdateKind := UpdateKind;
  126.   FError := Error;
  127.   inherited Create(Application);
  128. end;
  129. { Create a list of the data fields in the dataset, and store string values
  130.   associated with NewValue, OldValue, and CurValue in string variables
  131.   to make display switching faster }
  132. procedure TReconcileErrorForm.InitDataFields;
  133. var
  134.   I: Integer;
  135.   FD: PFieldData;
  136.   V: Variant;
  137.   HasCurValues: Boolean;
  138. begin
  139.   HasCurValues := False;
  140.   for I := 0 to FDataSet.FieldCount - 1 do
  141.   with FDataset.Fields[I] do
  142.   begin
  143.     if (FieldKind <> fkData) then Continue;
  144.     FD := New(PFieldData);
  145.     try
  146.       FD.Field := FDataset.Fields[I];
  147.       FD.Edited := False;
  148.       if FUpdateKind <> ukDelete then
  149.         FD.NewValue := VarToString(NewValue, DataType);
  150.       V := CurValue;
  151.       if not VarIsClear(V) then HasCurValues := True;
  152.       FD.CurValue := VarToString(CurValue, DataType);
  153.       if FUpdateKind <> ukInsert then
  154.         FD.OldValue := VarToString(OldValue, DataType);
  155.       FDataFields.Add(FD);
  156.     except
  157.       Dispose(FD);
  158.       raise;
  159.     end;
  160.   end;
  161.   InitUpdateData(HasCurValues);
  162. end;
  163. { Initialize the column indexes and grid titles }
  164. procedure TReconcileErrorForm.InitUpdateData(HasCurValues: Boolean);
  165. var
  166.   FColCount: Integer;
  167. begin
  168.   FColCount := 1;
  169.   UpdateData.ColCount := 4;
  170.   UpdateData.Cells[0,0] := SFieldName;
  171.   if FUpdateKind <> ukDelete then
  172.   begin
  173.     FNewColIdx := FColCount;
  174.     Inc(FColCount);
  175.     UpdateData.Cells[FNewColIdx,0] := UpdateKindStr[FUpdateKind] + SValue;
  176.   end else
  177.   begin
  178.     FOldColIdx := FColCount;
  179.     Inc(FColCount);
  180.     UpdateData.Cells[FOldColIdx,0] := SOriginal;
  181.   end;
  182.   if HasCurValues then
  183.   begin
  184.     FCurColIdx := FColCount;
  185.     Inc(FColCount);
  186.     UpdateData.Cells[FCurColIdx,0] := SConflict;
  187.   end;
  188.   if FUpdateKind = ukModify then
  189.   begin
  190.     FOldColIdx := FColCount;
  191.     Inc(FColCount);
  192.     UpdateData.Cells[FOldColIdx,0] := SOriginal;
  193.   end;
  194.   UpdateData.ColCount := FColCount;
  195. end;
  196. { Update the reconcile action radio group based on the valid reconcile actions }
  197. procedure TReconcileErrorForm.InitReconcileActions;
  198.   procedure AddAction(Action: TReconcileAction);
  199.   begin
  200.     ActionGroup.Items.AddObject(ActionStr[Action], TObject(Action));
  201.   end;
  202. begin
  203.   AddAction(raSkip);
  204.   AddAction(raCancel);
  205.   AddAction(raCorrect);
  206.   if FCurColIdx > 0 then
  207.   begin
  208.     AddAction(raRefresh);
  209.     AddAction(raMerge);
  210.   end;
  211.   ActionGroup.ItemIndex := 0;
  212. end;
  213. { Update the grid based on the current display options }
  214. procedure TReconcileErrorForm.DisplayFieldValues(Sender: TObject);
  215. var
  216.   I: Integer;
  217.   CurRow: Integer;
  218.   Action: TReconcileAction;
  219. begin
  220.   if not Visible then Exit;
  221.   Action := TReconcileAction(ActionGroup.Items.Objects[ActionGroup.ItemIndex]);
  222.   UpdateData.Col := 1;
  223.   UpdateData.Row := 1;
  224.   CurRow := 1;
  225.   UpdateData.RowCount := 2;
  226.   UpdateData.Cells[0, CurRow] := SNoData;
  227.   for I := 1 to UpdateData.ColCount - 1 do
  228.     UpdateData.Cells[I, CurRow] := '';
  229.   for I := 0 to FDataFields.Count - 1 do
  230.     with PFieldData(FDataFields[I])^ do
  231.     begin
  232.       if ConflictsOnly.Checked and (CurValue = SUnChanged) then Continue;
  233.       if ChangedOnly.Checked and (NewValue = SUnChanged) then Continue;
  234.       UpdateData.RowCount := CurRow + 1;
  235.       UpdateData.Cells[0, CurRow] := Field.DisplayName;
  236.       if FNewColIdx > 0 then
  237.       begin
  238.         case Action of
  239.           raCancel, raRefresh:
  240.             UpdateData.Cells[FNewColIdx, CurRow] := SUnChanged;
  241.           raCorrect:
  242.             if Edited then
  243.               UpdateData.Cells[FNewColIdx, CurRow] := EditValue else
  244.               UpdateData.Cells[FNewColIdx, CurRow] := NewValue;
  245.           else
  246.             UpdateData.Cells[FNewColIdx, CurRow] := NewValue;
  247.         end;
  248.         UpdateData.Objects[FNewColIdx, CurRow] := FDataFields[I];
  249.       end;
  250.       if FCurColIdx > 0 then
  251.         UpdateData.Cells[FCurColIdx, CurRow] := CurValue;
  252.       if FOldColIdx > 0 then
  253.         if (Action in [raMerge, raRefresh]) and (CurValue <> SUnchanged) then
  254.            UpdateData.Cells[FOldColIdx, CurRow] := CurValue else
  255.            UpdateData.Cells[FOldColIdx, CurRow] := OldValue;
  256.       Inc(CurRow);
  257.     end;
  258.   AdjustColumnWidths;
  259. end;
  260. { For fields that the user has edited, copy the changes back into the
  261.   NewValue property of the associated field }
  262. procedure TReconcileErrorForm.SetFieldValues(DataSet: TDataSet);
  263. var
  264.   I: Integer;
  265. begin
  266.   for I := 0 to FDataFields.Count - 1 do
  267.     with PFieldData(FDataFields[I])^ do
  268.       if Edited then Field.NewValue := EditValue;
  269. end;
  270. procedure TReconcileErrorForm.AdjustColumnWidths;
  271. var
  272.   NewWidth, I: integer;
  273. begin
  274.   with UpdateData do
  275.   begin
  276.     NewWidth := (ClientWidth - ColWidths[0]) div (ColCount - 1);
  277.     for I := 1 to ColCount - 1 do
  278.       ColWidths[I] := NewWidth - 1;
  279.   end;
  280. end;
  281. { Event handlers }
  282. procedure TReconcileErrorForm.FormCreate(Sender: TObject);
  283. begin
  284.   if FDataSet = nil then Exit;
  285.   FDataFields := TList.Create;
  286.   InitDataFields;
  287.   Caption := Format(SCaption, [FDataSet.Name]);
  288.   UpdateType.Caption := UpdateKindStr[FUpdateKind];
  289.   ErrorMsg.Text := FError.Message;
  290.   if FError.Context <> '' then
  291.     ErrorMsg.Lines.Add(FError.Context);
  292.   ConflictsOnly.Enabled := FCurColIdx > 0;
  293.   ConflictsOnly.Checked := ConflictsOnly.Enabled;
  294.   ChangedOnly.Enabled := FNewColIdx > 0;
  295.   InitReconcileActions;
  296.   UpdateData.DefaultRowHeight := UpdateData.Canvas.TextHeight('SWgjp') + 7; { Do not localize }
  297. end;
  298. procedure TReconcileErrorForm.FormDestroy(Sender: TObject);
  299. var
  300.   I: Integer;
  301. begin
  302.   if Assigned(FDataFields) then
  303.   begin
  304.     for I := 0 to FDataFields.Count - 1 do
  305.       Dispose(PFieldData(FDataFields[I]));
  306.     FDataFields.Destroy;
  307.   end;
  308. end;
  309. { Set the Edited flag in the DataField list and save the value }
  310. procedure TReconcileErrorForm.UpdateDataSetEditText(Sender: TObject; ACol,
  311.   ARow: Integer; const Value: string);
  312. begin
  313.   PFieldData(UpdateData.Objects[ACol, ARow]).EditValue := Value;
  314.   PFieldData(UpdateData.Objects[ACol, ARow]).Edited := True;
  315. end;
  316. { Enable the editing in the grid if we are on the NewValue column and the
  317.   current reconcile action is raCorrect }
  318. procedure TReconcileErrorForm.UpdateDataSelectCell(Sender: TObject; Col,
  319.   Row: Integer; var CanSelect: Boolean);
  320. begin
  321.   if (Col = FNewColIdx) and
  322.     (TReconcileAction(ActionGroup.Items.Objects[ActionGroup.ItemIndex]) = raCorrect) then
  323.     UpdateData.Options := UpdateData.Options + [goEditing] else
  324.     UpdateData.Options := UpdateData.Options - [goEditing];
  325. end;
  326. end.