数据感知控件可以链接到数据集,以显示当前行中的字段中包含的数据,或者在某些情况下,显示来自多行中的一个或多个列的数据. TTabControl允许您以易于理解的方式将同一组控件应用于不同的数据值集.
在我看来他们会很好地在一起. TTabControl将提供良好的数据感知控制(将其链接到数据集中的标识列,它可能是比TDBNavigator更直观的导航器),但VCL中没有一个.
有没有人创建了数据感知选项卡控件?我发现的唯一一个是Jean-Luc Mattei的DBTABCONTROL98,它可以追溯到1998年(Delphi 3时代),甚至在修改它以使其在XE下编译之后,实际上并不起作用.有没有其他工作符合预期? (即,在数据集中添加/删除新记录时添加/删除选项卡,并在用户更改选项卡时切换数据集的活动行,反之亦然.)
是的,我知道如果数据集中有很多行,那可能会有点笨拙.我正在寻找一些东西来构建一个用例,其中行数是单个或非常低的两位数.
解决方法
我为你写了一个TDBTabControl.如果未设置DataField属性,则选项卡的标题将是记录索引.带星号的选项卡表示新记录,可以使用ShowInsertTab属性切换可见性.
我继承自TCustomTabControl,因为可能不会为此组件发布属性Tabs,TabIndex和MultiSelect.
- unit DBTabControl;
- interface
- uses
- Classes,Windows,SysUtils,Messages,Controls,ComCtrls,DB,DBCtrls;
- type
- TCustomDBTabControl = class(TCustomTabControl)
- private
- FDataLink: TFieldDataLink;
- FPrevTabIndex: Integer;
- FShowInsertTab: Boolean;
- procedure ActiveChanged(Sender: TObject);
- procedure DataChanged(Sender: TObject);
- function GetDataField: String;
- function GetDataSource: TDataSource;
- function GetField: TField;
- procedure RebuildTabs;
- procedure SetDataField(const Value: String);
- procedure SetDataSource(Value: TDataSource);
- procedure SetShowInsertTab(Value: Boolean);
- procedure CMExit(var Message: TCMExit); message CM_EXIT;
- procedure CMGetDataLink(var Message: TMessage); message CM_GETDATALINK;
- protected
- function CanChange: Boolean; override;
- procedure Change; override;
- procedure KeyDown(var Key: Word; Shift: TShiftState); override;
- procedure Notification(AComponent: TComponent; Operation: TOperation);
- override;
- procedure Loaded; override;
- property DataField: String read GetDataField write SetDataField;
- property DataSource: TDataSource read GetDataSource write SetDataSource;
- property Field: TField read GetField;
- property ShowInsertTab: Boolean read FShowInsertTab write SetShowInsertTab
- default False;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- function ExecuteAction(Action: TBasicAction): Boolean; override;
- function UpdateAction(Action: TBasicAction): Boolean; override;
- end;
- TDBTabControl = class(TCustomDBTabControl)
- public
- property DisplayRect;
- property Field;
- published
- property Align;
- property Anchors;
- property BiDiMode;
- property Constraints;
- property DockSite;
- property DataField;
- property DataSource;
- property DragCursor;
- property DragKind;
- property DragMode;
- property Enabled;
- property Font;
- property HotTrack;
- property Images;
- property MultiLine;
- property OwnerDraw;
- property ParentBiDiMode;
- property ParentFont;
- property ParentShowHint;
- property PopupMenu;
- property RaggedRight;
- property ScrollOpposite;
- property ShowHint;
- property ShowInsertTab;
- property Style;
- property TabHeight;
- property TabOrder;
- property TabPosition;
- property TabStop;
- property TabWidth;
- property Visible;
- property OnChange;
- property OnChanging;
- property OnContextPopup;
- property OnDockDrop;
- property OnDockOver;
- property OnDragDrop;
- property OnDragOver;
- property OnDrawTab;
- property OnEndDock;
- property OnEndDrag;
- property OnEnter;
- property OnExit;
- property OnGetImageIndex;
- property OnGetSiteInfo;
- property OnMouseDown;
- property OnMouseMove;
- property OnMouseUp;
- property OnResize;
- property OnStartDock;
- property OnStartDrag;
- property OnUnDock;
- end;
- implementation
- { TCustomDBTabControl }
- procedure TCustomDBTabControl.ActiveChanged(Sender: TObject);
- begin
- RebuildTabs;
- end;
- function TCustomDBTabControl.CanChange: Boolean;
- begin
- FPrevTabIndex := TabIndex;
- Result := (inherited CanChange) and (DataSource <> nil) and
- (DataSource.State in [dsBrowse,dsEdit,dsInsert]);
- end;
- procedure TCustomDBTabControl.Change;
- var
- NewTabIndex: Integer;
- begin
- try
- if FDataLink.Active and (DataSource <> nil) then
- begin
- if FShowInsertTab and (TabIndex = Tabs.Count - 1) then
- DataSource.DataSet.Append
- else if DataSource.State = dsInsert then
- begin
- NewTabIndex := TabIndex;
- DataSource.DataSet.CheckBrowseMode;
- DataSource.DataSet.MoveBy(NewTabIndex - TabIndex);
- end
- else
- DataSource.DataSet.MoveBy(TabIndex - FPrevTabIndex);
- end;
- inherited Change;
- except
- TabIndex := FPrevTabIndex;
- raise;
- end;
- end;
- procedure TCustomDBTabControl.CMExit(var Message: TCMExit);
- begin
- try
- FDataLink.UpdateRecord;
- except
- SetFocus;
- raise;
- end;
- inherited;
- end;
- procedure TCustomDBTabControl.CMGetDataLink(var Message: TMessage);
- begin
- Message.Result := Integer(FDataLink);
- end;
- constructor TCustomDBTabControl.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FDataLink := TFieldDataLink.Create;
- FDataLink.Control := Self;
- FDataLink.OnActiveChange := ActiveChanged;
- FDataLink.OnDataChange := DataChanged;
- end;
- procedure TCustomDBTabControl.DataChanged(Sender: TObject);
- const
- StarCount: array[Boolean] of Integer = (0,1);
- var
- NewTabIndex: Integer;
- begin
- if FDataLink.Active and (DataSource <> nil) then
- with DataSource do
- begin
- if DataSet.RecordCount <> Tabs.Count - StarCount[FShowInsertTab] then
- RebuildTabs
- else if (State = dsInsert) and FShowInsertTab then
- TabIndex := Tabs.Count - 1
- else if Tabs.Count > 0 then
- begin
- NewTabIndex := Tabs.IndexOfObject(TObject(DataSet.RecNo));
- if (TabIndex = NewTabIndex) and (State <> dsInsert) and
- (Field <> nil) and (Field.AsString <> Tabs[TabIndex]) then
- Tabs[TabIndex] := Field.AsString;
- TabIndex := NewTabIndex;
- end;
- end;
- end;
- destructor TCustomDBTabControl.Destroy;
- begin
- FDataLink.Free;
- FDataLink := nil;
- inherited Destroy;
- end;
- function TCustomDBTabControl.ExecuteAction(Action: TBasicAction): Boolean;
- begin
- Result := inherited ExecuteAction(Action) or FDataLink.ExecuteAction(Action);
- end;
- function TCustomDBTabControl.GetDataField: String;
- begin
- Result := FDataLink.FieldName;
- end;
- function TCustomDBTabControl.GetDataSource: TDataSource;
- begin
- Result := FDataLink.DataSource;
- end;
- function TCustomDBTabControl.GetField: TField;
- begin
- Result := FDataLink.Field;
- end;
- procedure TCustomDBTabControl.KeyDown(var Key: Word; Shift: TShiftState);
- begin
- if (DataSource <> nil) and (DataSource.State = dsInsert) and
- (Key = VK_ESCAPE) then
- begin
- DataSource.DataSet.Cancel;
- Change;
- end;
- inherited keyDown(Key,Shift);
- end;
- procedure TCustomDBTabControl.Loaded;
- begin
- inherited Loaded;
- if (csDesigning in ComponentState) then
- RebuildTabs;
- end;
- procedure TCustomDBTabControl.Notification(AComponent: TComponent;
- Operation: TOperation);
- begin
- inherited Notification(AComponent,Operation);
- if (Operation = opRemove) and (FDataLink <> nil) and
- (AComponent = DataSource) then
- DataSource := nil;
- end;
- procedure TCustomDBTabControl.RebuildTabs;
- var
- Bookmark: TBookmark;
- begin
- if (DataSource <> nil) and (DataSource.State = dsBrowse) then
- with DataSource do
- begin
- if HandleAllocated then
- LockWindowUpdate(Handle);
- Tabs.BeginUpdate;
- DataSet.DisableControls;
- BookMark := DataSet.GetBookmark;
- try
- Tabs.Clear;
- DataSet.First;
- while not DataSet.Eof do
- begin
- if Field = nil then
- Tabs.AddObject(IntToStr(Tabs.Count + 1),TObject(DataSet.RecNo))
- else
- Tabs.AddObject(Field.AsString,TObject(DataSet.RecNo));
- DataSet.Next;
- end;
- if FShowInsertTab then
- Tabs.AddObject('*',TObject(-1));
- finally
- DataSet.GotoBookmark(Bookmark);
- DataSet.FreeBookmark(Bookmark);
- DataSet.EnableControls;
- Tabs.EndUpdate;
- if HandleAllocated then
- LockWindowUpdate(0);
- end;
- end
- else
- Tabs.Clear;
- end;
- procedure TCustomDBTabControl.SetDataField(const Value: String);
- begin
- FDataLink.FieldName := Value;
- RebuildTabs;
- end;
- procedure TCustomDBTabControl.SetDataSource(Value: TDataSource);
- begin
- FDataLink.DataSource := Value;
- if DataSource <> nil then
- DataSource.FreeNotification(Self);
- if not (csLoading in ComponentState) then
- RebuildTabs;
- end;
- procedure TCustomDBTabControl.SetShowInsertTab(Value: Boolean);
- begin
- if FShowInsertTab <> Value then
- begin
- FShowInsertTab := Value;
- RebuildTabs;
- end;
- end;
- function TCustomDBTabControl.UpdateAction(Action: TBasicAction): Boolean;
- begin
- Result := inherited UpdateAction(Action) or FDataLink.UpdateAction(Action);
- end;
- end.
- unit DBTabControlReg;
- interface
- uses
- Classes,DBTabControl;
- procedure Register;
- implementation
- procedure Register;
- begin
- RegisterComponents('Samples',[TDBTabControl]);
- end;
- end.
- package DBTabControl70;
- {$R *.res}
- {$ALIGN 8}
- {$ASSERTIONS ON}
- {$BOOLEVAL OFF}
- {$DEBUGINFO ON}
- {$EXTENDEDSyntax ON}
- {$IMPORTEDDATA ON}
- {$IOCHECKS ON}
- {$LOCALSYMBOLS ON}
- {$LONGSTRINGS ON}
- {$OPENSTRINGS ON}
- {$OPTIMIZATION OFF}
- {$OVERFLOWCHECKS ON}
- {$RANGECHECKS ON}
- {$REFERENCEINFO ON}
- {$SAFEDIVIDE OFF}
- {$STACKFRAMES ON}
- {$TYPEDADDRESS OFF}
- {$VARSTRINGCHECKS ON}
- {$WRITEABLECONST OFF}
- {$MINENUMSIZE 1}
- {$IMAGEBASE $400000}
- {$DESCRIPTION '#DBTabControl'}
- {$IMPLICITBUILD OFF}
- requires
- rtl,vcl,dbrtl,vcldb;
- contains
- DBTabControl in 'DBTabControl.pas',DBTabControlReg in 'DBTabControlReg.pas';
- end.