{*******************************************************} { } { Delphi VCL Extensions (RX) } { } { Copyright (c) 1997 Master-Bank } { } {*******************************************************} unit rxdbcomb; {$I rx.inc} interface uses LCLType, LCLProc, LCLIntf, LMessages, Menus, Graphics, Classes, Controls, DB, StdCtrls, DbCtrls; type { TCustomDBComboBox } TCustomDBComboBox = class(TCustomComboBox) private FDataLink: TFieldDataLink; procedure DataChange(Sender: TObject); procedure EditingChange(Sender: TObject); function GetDataField: string; function GetDataSource: TDataSource; function GetField: TField; function GetReadOnly: Boolean; procedure SetDataField(const Value: string); procedure SetDataSource(Value: TDataSource); procedure SetEditReadOnly; procedure SetItems(const Value: TStrings); procedure SetReadOnly(Value: Boolean); procedure UpdateData(Sender: TObject); function GetComboText: string; virtual; procedure SetComboText(const Value: string); virtual; protected procedure EditingDone; override; procedure Change; override; procedure Click; override; procedure CreateWnd; override; procedure DropDown; override; function GetPaintText: string; virtual; procedure KeyDown(var Key: Word; Shift: TShiftState); override; procedure KeyPress(var Key: Char); override; procedure Loaded; override; procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure SetStyle(Value: TComboBoxStyle); override; procedure WndProc(var Message: TLMessage); override; property ComboText: string read GetComboText write SetComboText; property DataField: string read GetDataField write SetDataField; property DataSource: TDataSource read GetDataSource write SetDataSource; property ReadOnly: Boolean read GetReadOnly write SetReadOnly default False; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; function ExecuteAction(AAction: TBasicAction): Boolean; override; function UpdateAction(AAction: TBasicAction): Boolean; override; function UseRightToLeftAlignment: Boolean; property Field: TField read GetField; property Items write SetItems; property Text; end; { TRxDBComboBox } TRxDBComboBox = class(TCustomDBComboBox) private FValues: TStrings; FEnableValues: Boolean; procedure SetEnableValues(Value: Boolean); procedure SetValues(Value: TStrings); procedure ValuesChanged(Sender: TObject); protected procedure SetStyle(Value: TComboBoxStyle); override; function GetComboText: string; override; function GetPaintText: string; override; procedure SetComboText(const Value: string); override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property Anchors; property ArrowKeysTraverseList; property AutoDropDown; property AutoSize; property BorderSpacing; property Style; { must be published before Items } property Color; property DataField; property DataSource; property DragMode; property DragCursor; property DropDownCount; property Enabled; property EnableValues: Boolean read FEnableValues write SetEnableValues; property Font; property Constraints; property DragKind; property ItemHeight; property Items; property ItemWidth; property MaxLength default -1; property ParentColor; property ParentFont; property ParentShowHint; property PopupMenu; property ReadOnly; property ShowHint; property Sorted; property TabOrder; property TabStop; property Values: TStrings read FValues write SetValues; property Visible; property OnChange; property OnChangeBounds; property OnClick; property OnCloseUp; property OnDblClick; property OnDragDrop; property OnDragOver; property OnDrawItem; property OnDropDown; property OnEndDrag; property OnEnter; property OnExit; property OnKeyDown; property OnKeyPress; property OnKeyUp; property OnMeasureItem; property OnMouseDown; property OnMouseMove; property OnMouseUp; property OnSelect; property OnStartDrag; property OnUTF8KeyPress; property OnContextPopup; property OnEndDock; property OnStartDock; end; implementation uses DBUtils; { TCustomDBComboBox } constructor TCustomDBComboBox.Create(AOwner: TComponent); begin inherited Create(AOwner); ControlStyle := ControlStyle + [csReplicatable]; FDataLink := TFieldDataLink.Create; FDataLink.Control := Self; FDataLink.OnDataChange := @DataChange; FDataLink.OnUpdateData := @UpdateData; FDataLink.OnEditingChange := @EditingChange; end; destructor TCustomDBComboBox.Destroy; begin FDataLink.OnDataChange := nil; FDataLink.OnUpdateData := nil; FDataLink.Free; FDataLink := nil; inherited Destroy; end; procedure TCustomDBComboBox.Loaded; begin inherited Loaded; if (csDesigning in ComponentState) then DataChange(Self); end; procedure TCustomDBComboBox.Notification(AComponent: TComponent; Operation: TOperation); begin inherited Notification(AComponent, Operation); if (Operation = opRemove) and (FDataLink <> nil) and (AComponent = DataSource) then DataSource := nil; end; procedure TCustomDBComboBox.CreateWnd; begin inherited CreateWnd; SetEditReadOnly; end; procedure TCustomDBComboBox.DataChange(Sender: TObject); begin if DroppedDown then Exit; if FDataLink.Field <> nil then ComboText := FDataLink.Field.AsString else if csDesigning in ComponentState then ComboText := Name else ComboText := ''; end; procedure TCustomDBComboBox.UpdateData(Sender: TObject); begin FDataLink.Field.AsString := ComboText; end; procedure TCustomDBComboBox.SetComboText(const Value: string); var I: Integer; Redraw: Boolean; begin if Value <> ComboText then begin if Style <> csDropDown then begin Redraw := (Style <> csSimple) and HandleAllocated; // if Redraw then SendMessage(Handle, LM_SETREDRAW, 0, 0); try if Value = '' then I := -1 else I := Items.IndexOf(Value); ItemIndex := I; finally if Redraw then begin // SendMessage(Handle, WM_SETREDRAW, 1, 0); Invalidate; end; end; if I >= 0 then Exit; end; if Style in [csDropDown, csSimple] then Text := Value; end; end; function TCustomDBComboBox.GetComboText: string; var I: Integer; begin if Style in [csDropDown, csSimple] then Result := Text else begin I := ItemIndex; if I < 0 then Result := '' else Result := Items[I]; end; end; procedure TCustomDBComboBox.Change; begin FDataLink.Edit; FDataLink.Modified; FDataLink.UpdateRecord; inherited Change; end; procedure TCustomDBComboBox.Click; begin FDataLink.Edit; inherited Click; FDataLink.Modified; end; procedure TCustomDBComboBox.DropDown; begin FDataLink.Edit; inherited DropDown; end; function TCustomDBComboBox.GetDataSource: TDataSource; begin Result := FDataLink.DataSource; end; procedure TCustomDBComboBox.SetDataSource(Value: TDataSource); begin if not (FDataLink.DataSourceFixed and (csLoading in ComponentState)) then FDataLink.DataSource := Value; if Value <> nil then Value.FreeNotification(Self); end; function TCustomDBComboBox.GetDataField: string; begin Result := FDataLink.FieldName; end; procedure TCustomDBComboBox.SetDataField(const Value: string); begin FDataLink.FieldName := Value; end; function TCustomDBComboBox.GetReadOnly: Boolean; begin Result := FDataLink.ReadOnly; end; procedure TCustomDBComboBox.SetReadOnly(Value: Boolean); begin FDataLink.ReadOnly := Value; end; function TCustomDBComboBox.GetField: TField; begin Result := FDataLink.Field; end; procedure TCustomDBComboBox.KeyDown(var Key: Word; Shift: TShiftState); begin inherited KeyDown(Key, Shift); if (Key = VK_BACK) or (Key = VK_DELETE) or (Key = VK_UP) or (Key = VK_DOWN) or (Key in [32..255]) then begin if not FDataLink.Edit and (Key in [VK_UP, VK_DOWN]) then Key := 0; end; end; procedure TCustomDBComboBox.KeyPress(var Key: Char); begin inherited KeyPress(Key); if (Key in [#32..#255]) and (FDataLink.Field <> nil) and not FDataLink.Field.IsValidChar(Key) then begin // MessageBeep(0); Key := #0; end; case Key of ^H, ^V, ^X, #32..#255: FDataLink.Edit; #27: begin FDataLink.Reset; SelectAll; end; end; end; procedure TCustomDBComboBox.EditingChange(Sender: TObject); begin SetEditReadOnly; end; procedure TCustomDBComboBox.SetEditReadOnly; begin (* if (Style in [csDropDown, csSimple]) and HandleAllocated then SendMessage({$IFDEF WIN32} EditHandle {$ELSE} FEditHandle {$ENDIF}, EM_SETREADONLY, Ord(not FDataLink.Editing), 0); *) end; procedure TCustomDBComboBox.WndProc(var Message: TLMessage); begin if not (csDesigning in ComponentState) then case Message.Msg of LM_COMMAND: if TLMCommand(Message).NotifyCode = CBN_SELCHANGE then if not FDataLink.Edit then begin { if Style <> csSimple then PostMessage(Handle, LB_SHOWDROPDOWN, 0, 0);} Exit; end; { CB_SHOWDROPDOWN: if Message.WParam <> 0 then FDataLink.Edit else if not FDataLink.Editing then DataChange(Self); }{Restore text} {$IFDEF WIN32} { LM_CREATE, WM_WINDOWPOSCHANGED, CM_FONTCHANGED: FPaintControl.DestroyHandle;} {$ENDIF} end; inherited WndProc(Message); end; procedure TCustomDBComboBox.EditingDone; begin if Assigned(FDataLink.DataSet) and (FDataLink.DataSet.State in [dsinsert,dsedit]) then begin try FDataLink.UpdateRecord; except SelectAll; if CanFocus then SetFocus; raise; end; inherited EditingDone; end; end; function TCustomDBComboBox.GetPaintText: string; begin if FDataLink.Field <> nil then Result := FDataLink.Field.Text else Result := ''; end; procedure TCustomDBComboBox.SetItems(const Value: TStrings); begin Items.Assign(Value); DataChange(Self); end; procedure TCustomDBComboBox.SetStyle(Value: TComboBoxStyle); begin if (Value = csSimple) and Assigned(FDatalink) and FDatalink.DatasourceFixed then _DBError('SNotReplicatable'); inherited SetStyle(Value); end; function TCustomDBComboBox.UseRightToLeftAlignment: Boolean; begin // Result := DBUseRightToLeftAlignment(Self, Field); end; function TCustomDBComboBox.ExecuteAction(AAction: TBasicAction): Boolean; begin { Result := inherited ExecuteAction(AAction) or (FDataLink <> nil) and FDataLink.ExecuteAction(AAction);} end; function TCustomDBComboBox.UpdateAction(AAction: TBasicAction): Boolean; begin { Result := inherited UpdateAction(AAction) or (FDataLink <> nil) and FDataLink.UpdateAction(AAction);} end; { TRxDBComboBox } constructor TRxDBComboBox.Create(AOwner: TComponent); begin inherited Create(AOwner); FValues := TStringList.Create; TStringList(FValues).OnChange := @ValuesChanged; EnableValues := False; end; destructor TRxDBComboBox.Destroy; begin TStringList(FValues).OnChange := nil; FValues.Free; inherited Destroy; end; procedure TRxDBComboBox.ValuesChanged(Sender: TObject); begin if FEnableValues then DataChange(Self); end; function TRxDBComboBox.GetPaintText: string; var I: Integer; begin Result := ''; if FDataLink.Field <> nil then begin if FEnableValues then begin I := Values.IndexOf(FDataLink.Field.Text); if I >= 0 then Result := Items.Strings[I] end else Result := FDataLink.Field.Text; end; end; function TRxDBComboBox.GetComboText: string; var I: Integer; begin if (Style in [csDropDown, csSimple]) and (not FEnableValues) then Result := Text else begin I := ItemIndex; if (I < 0) or (FEnableValues and (FValues.Count < I + 1)) then Result := '' else if FEnableValues then Result := FValues[I] else Result := Items[I]; end; end; procedure TRxDBComboBox.SetComboText(const Value: string); var I: Integer; Redraw: Boolean; begin if Value <> ComboText then begin if Style <> csDropDown then begin Redraw := (Style <> csSimple) and HandleAllocated; // if Redraw then SendMessage(Handle, WM_SETREDRAW, 0, 0); try if Value = '' then I := -1 else if FEnableValues then I := Values.IndexOf(Value) else I := Items.IndexOf(Value); if I >= Items.Count then I := -1; ItemIndex := I; finally if Redraw then begin // SendMessage(Handle, WM_SETREDRAW, 1, 0); // Invalidate; end; end; if I >= 0 then Exit; end; if Style in [csDropDown, csSimple] then Text := Value; Invalidate; end; end; procedure TRxDBComboBox.SetEnableValues(Value: Boolean); begin if FEnableValues <> Value then begin if Value and (Style in [csDropDown, csSimple]) then Style := csDropDownList; FEnableValues := Value; DataChange(Self); end; end; procedure TRxDBComboBox.SetValues(Value: TStrings); begin FValues.Assign(Value); end; procedure TRxDBComboBox.SetStyle(Value: TComboboxStyle); begin if (Value in [csSimple, csDropDown]) and FEnableValues then Value := csDropDownList; inherited SetStyle(Value); end; end.