08. august 2007 - 12:26Der er
10 kommentarer og 1 løsning
SpinEdit, som accepterer float.
Hej,
jeg skal bruge en SpinEdit, som kan bruges til float. F.eks. skal man kunne stille increment til 0.1, og så med f.eks. SpinEdit1.Text kunne udlæse værdien.
Jeg er klar over, at der findes færdige købe komponenter, som kan dette, men det må da være ret enkelt at lave sin egen TFloatSpinEdit komponent ud fra en normal TSpinEdit.
Er der en, som gider forklare trin for trin, hvordan denne laves?
Det letteste er vel at tage en kopi af den eksisterende SpinEdit, kalde den TFloatSpinEdit og derefter erstatte alle LongInt med Double. Jeg har lige prøvet det og det virker fint.
Jeg er gået ind i Spin.pas og taget en kopi af TSpinEdit og smidt kopien i en ny pas fil dvs. både interface delen og alle funktioner under implementation. Derefter rettede jeg alle de funktioner der brugte LongInt til at benytte Double i stedet. Og det var faktisk det.
Jeg bruger Delphi 7, ved ikke om TSpinEdit også findes i Spin.pas uniten i nyere versioner af Delphi.
I den følgende kode har jeg ændret alle LongInt til double i afsnittet for TSpinEdit. Kan du forklare, hvordan jeg nu kan bruge denne kode til den ændrede komponent? Jeg er ret blank på det område...
function TSpinButton.CreateButton: TTimerSpeedButton; begin Result := TTimerSpeedButton.Create(Self); Result.OnClick := BtnClick; Result.OnMouseDown := BtnMouseDown; Result.Visible := True; Result.Enabled := True; Result.TimeBtnState := [tbAllowTimer]; Result.Parent := Self; end;
procedure TSpinButton.Notification(AComponent: TComponent; Operation: TOperation); begin inherited Notification(AComponent, Operation); if (Operation = opRemove) and (AComponent = FFocusControl) then FFocusControl := nil; end;
procedure TSpinButton.AdjustSize(var W, H: Integer); begin if (FUpButton = nil) or (csLoading in ComponentState) then Exit; if W < 15 then W := 15; FUpButton.SetBounds(0, 0, W, H div 2); FDownButton.SetBounds(0, FUpButton.Height - 1, W, H - FUpButton.Height + 1); end;
procedure TSpinButton.SetBounds(ALeft, ATop, AWidth, AHeight: Integer); var W, H: Integer; begin W := AWidth; H := AHeight; AdjustSize(W, H); inherited SetBounds(ALeft, ATop, W, H); end;
procedure TSpinButton.WMSize(var Message: TWMSize); var W, H: Integer; begin inherited; { check for minimum size } W := Width; H := Height; AdjustSize(W, H); if (W <> Width) or (H <> Height) then inherited SetBounds(Left, Top, W, H); Message.Result := 0; end;
procedure TSpinButton.KeyDown(var Key: Word; Shift: TShiftState); begin case Key of VK_UP: begin SetFocusBtn (FUpButton); FUpButton.Click; end; VK_DOWN: begin SetFocusBtn (FDownButton); FDownButton.Click; end; VK_SPACE: FFocusedButton.Click; end; end;
procedure TSpinButton.BtnMouseDown (Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if Button = mbLeft then begin SetFocusBtn (TTimerSpeedButton (Sender)); if (FFocusControl <> nil) and FFocusControl.TabStop and FFocusControl.CanFocus and (GetFocus <> FFocusControl.Handle) then FFocusControl.SetFocus else if TabStop and (GetFocus <> Handle) and CanFocus then SetFocus; end; end;
procedure TSpinButton.BtnClick(Sender: TObject); begin if Sender = FUpButton then begin if Assigned(FOnUpClick) then FOnUpClick(Self); end else if Assigned(FOnDownClick) then FOnDownClick(Self); end;
procedure TSpinButton.SetFocusBtn (Btn: TTimerSpeedButton); begin if TabStop and CanFocus and (Btn <> FFocusedButton) then begin FFocusedButton.TimeBtnState := FFocusedButton.TimeBtnState - [tbFocusRect]; FFocusedButton := Btn; if (GetFocus = Handle) then begin FFocusedButton.TimeBtnState := FFocusedButton.TimeBtnState + [tbFocusRect]; Invalidate; end; end; end;
procedure TSpinButton.WMGetDlgCode(var Message: TWMGetDlgCode); begin Message.Result := DLGC_WANTARROWS; end;
procedure TSpinButton.Loaded; var W, H: Integer; begin inherited Loaded; W := Width; H := Height; AdjustSize (W, H); if (W <> Width) or (H <> Height) then inherited SetBounds (Left, Top, W, H); end;
function TSpinButton.GetUpGlyph: TBitmap; begin Result := FUpButton.Glyph; end;
procedure TSpinButton.SetUpGlyph(Value: TBitmap); begin if Value <> nil then FUpButton.Glyph := Value else begin FUpButton.Glyph.Handle := LoadBitmap(HInstance, 'SpinUp'); FUpButton.NumGlyphs := 1; FUpButton.Invalidate; end; end;
function TSpinButton.GetUpNumGlyphs: TNumGlyphs; begin Result := FUpButton.NumGlyphs; end;
procedure TSpinButton.SetUpNumGlyphs(Value: TNumGlyphs); begin FUpButton.NumGlyphs := Value; end;
function TSpinButton.GetDownGlyph: TBitmap; begin Result := FDownButton.Glyph; end;
procedure TSpinButton.SetDownGlyph(Value: TBitmap); begin if Value <> nil then FDownButton.Glyph := Value else begin FDownButton.Glyph.Handle := LoadBitmap(HInstance, 'SpinDown'); FUpButton.NumGlyphs := 1; FDownButton.Invalidate; end; end;
function TSpinButton.GetDownNumGlyphs: TNumGlyphs; begin Result := FDownButton.NumGlyphs; end;
procedure TSpinButton.SetDownNumGlyphs(Value: TNumGlyphs); begin FDownButton.NumGlyphs := Value; end;
destructor TSpinEdit.Destroy; begin FButton := nil; inherited Destroy; end;
procedure TSpinEdit.GetChildren(Proc: TGetChildProc; Root: TComponent); begin end;
procedure TSpinEdit.KeyDown(var Key: Word; Shift: TShiftState); begin if Key = VK_UP then UpClick (Self) else if Key = VK_DOWN then DownClick (Self); inherited KeyDown(Key, Shift); end;
procedure TSpinEdit.KeyPress(var Key: Char); begin if not IsValidChar(Key) then begin Key := #0; MessageBeep(0) end; if Key <> #0 then inherited KeyPress(Key); end;
function TSpinEdit.IsValidChar(Key: Char): Boolean; begin Result := (Key in [DecimalSeparator, '+', '-', '0'..'9']) or ((Key < #32) and (Key <> Chr(VK_RETURN))); if not FEditorEnabled and Result and ((Key >= #32) or (Key = Char(VK_BACK)) or (Key = Char(VK_DELETE))) then Result := False; end;
procedure TSpinEdit.CreateParams(var Params: TCreateParams); begin inherited CreateParams(Params); { Params.Style := Params.Style and not WS_BORDER; } Params.Style := Params.Style or ES_MULTILINE or WS_CLIPCHILDREN; end;
procedure TSpinEdit.CreateWnd; begin inherited CreateWnd; SetEditRect; end;
procedure TSpinEdit.SetEditRect; var Loc: TRect; begin SendMessage(Handle, EM_GETRECT, 0, LongInt(@Loc)); Loc.Bottom := ClientHeight + 1; {+1 is workaround for windows paint bug} Loc.Right := ClientWidth - FButton.Width - 2; Loc.Top := 0; Loc.Left := 0; SendMessage(Handle, EM_SETRECTNP, 0, LongInt(@Loc)); SendMessage(Handle, EM_GETRECT, 0, LongInt(@Loc)); {debug} end;
procedure TSpinEdit.WMSize(var Message: TWMSize); var MinHeight: Integer; begin inherited; MinHeight := GetMinHeight; { text edit bug: if size to less than minheight, then edit ctrl does not display the text } if Height < MinHeight then Height := MinHeight else if FButton <> nil then begin if NewStyleControls and Ctl3D then FButton.SetBounds(Width - FButton.Width - 5, 0, FButton.Width, Height - 5) else FButton.SetBounds (Width - FButton.Width, 1, FButton.Width, Height - 3); SetEditRect; end; end;
function TSpinEdit.GetMinHeight: Integer; var DC: HDC; SaveFont: HFont; I: Integer; SysMetrics, Metrics: TTextMetric; begin DC := GetDC(0); GetTextMetrics(DC, SysMetrics); SaveFont := SelectObject(DC, Font.Handle); GetTextMetrics(DC, Metrics); SelectObject(DC, SaveFont); ReleaseDC(0, DC); I := SysMetrics.tmHeight; if I > Metrics.tmHeight then I := Metrics.tmHeight; Result := Metrics.tmHeight + I div 4 + GetSystemMetrics(SM_CYBORDER) * 4 + 2; end;
procedure TSpinEdit.UpClick (Sender: TObject); begin if ReadOnly then MessageBeep(0) else Value := Value + FIncrement; end;
procedure TSpinEdit.DownClick (Sender: TObject); begin if ReadOnly then MessageBeep(0) else Value := Value - FIncrement; end;
procedure TSpinEdit.WMPaste(var Message: TWMPaste); begin if not FEditorEnabled or ReadOnly then Exit; inherited; end;
procedure TSpinEdit.WMCut(var Message: TWMPaste); begin if not FEditorEnabled or ReadOnly then Exit; inherited; end;
procedure TSpinEdit.CMExit(var Message: TCMExit); begin inherited; if CheckValue (Value) <> Value then SetValue (Value); end;
function TSpinEdit.GetValue: LongInt; begin try Result := StrToInt (Text); except Result := FMinValue; end; end;
procedure TSpinEdit.SetValue (NewValue: LongInt); begin Text := IntToStr (CheckValue (NewValue)); end;
function TSpinEdit.CheckValue (NewValue: LongInt): LongInt; begin Result := NewValue; if (FMaxValue <> FMinValue) then begin if NewValue < FMinValue then Result := FMinValue else if NewValue > FMaxValue then Result := FMaxValue; end; end;
procedure TSpinEdit.CMEnter(var Message: TCMGotFocus); begin if AutoSelect and not (csLButtonDown in ControlState) then SelectAll; inherited; end;
{TTimerSpeedButton}
destructor TTimerSpeedButton.Destroy; begin if FRepeatTimer <> nil then FRepeatTimer.Free; inherited Destroy; end;
procedure TTimerSpeedButton.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin inherited MouseDown (Button, Shift, X, Y); if tbAllowTimer in FTimeBtnState then begin if FRepeatTimer = nil then FRepeatTimer := TTimer.Create(Self);
procedure TTimerSpeedButton.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin inherited MouseUp (Button, Shift, X, Y); if FRepeatTimer <> nil then FRepeatTimer.Enabled := False; end;
procedure TTimerSpeedButton.TimerExpired(Sender: TObject); begin FRepeatTimer.Interval := RepeatPause; if (FState = bsDown) and MouseCapture then begin try Click; except FRepeatTimer.Enabled := False; raise; end; end; end;
procedure TTimerSpeedButton.Paint; var R: TRect; begin inherited Paint; if tbFocusRect in FTimeBtnState then begin R := Bounds(0, 0, Width, Height); InflateRect(R, -3, -3); if FState = bsDown then OffsetRect(R, 1, 1); DrawFocusRect(Canvas.Handle, R); end; end;
Det er kun nødvendig at kopiere TSpinEdit. TSpinButton og TTimerSpeedButton skal du ikke have med. Men du får lige den unit jeg lavede her, det ser ud til at der ikke er sket det store siden Delphi 7. Hvis der er noget der ikke virker så sammenlign lige koden med den du har i Delphi 2006.
Jeg har som nævnt skiftet LongInt ud med Double de aktuelle steder og så naturligvis ændret StrToInt og IntToStr til StrToFloat og FloatToStr.
Gem nedenstående kode og kald filen f.eks. FloatSpinEdit.pas Gå ind i Delphi og registrer komponenten, hvordan det virker i D2006 kan jeg så ikke sige. Du vil nu have en fane der hedder Unipeople hvorpå der er en komponent der hedder TFloatSpinEdit, men igen ved jeg ikke om der måske er ændret på den måde komponenter virker i D2006. Hvis du ikke vil have en fane der hedder Unipeople så ret denne linie. RegisterComponents('Unipeople', [TFloatSpinEdit]);
destructor TFloatSpinEdit.Destroy; begin FButton := nil; inherited Destroy; end;
procedure TFloatSpinEdit.GetChildren(Proc: TGetChildProc; Root: TComponent); begin end;
procedure TFloatSpinEdit.KeyDown(var Key: Word; Shift: TShiftState); begin if Key = VK_UP then UpClick (Self) else if Key = VK_DOWN then DownClick (Self); inherited KeyDown(Key, Shift); end;
procedure TFloatSpinEdit.KeyPress(var Key: Char); begin if not IsValidChar(Key) then begin Key := #0; MessageBeep(0) end; if Key <> #0 then inherited KeyPress(Key); end;
function TFloatSpinEdit.IsValidChar(Key: Char): Boolean; begin Result := (Key in [DecimalSeparator, '+', '-', '0'..'9']) or ((Key < #32) and (Key <> Chr(VK_RETURN))); if not FEditorEnabled and Result and ((Key >= #32) or (Key = Char(VK_BACK)) or (Key = Char(VK_DELETE))) then Result := False; end;
procedure TFloatSpinEdit.CreateParams(var Params: TCreateParams); begin inherited CreateParams(Params); { Params.Style := Params.Style and not WS_BORDER; } Params.Style := Params.Style or ES_MULTILINE or WS_CLIPCHILDREN; end;
procedure TFloatSpinEdit.CreateWnd; begin inherited CreateWnd; SetEditRect; end;
procedure TFloatSpinEdit.SetEditRect; var Loc: TRect; begin SendMessage(Handle, EM_GETRECT, 0, LongInt(@Loc)); Loc.Bottom := ClientHeight + 1; {+1 is workaround for windows paint bug} Loc.Right := ClientWidth - FButton.Width - 2; Loc.Top := 0; Loc.Left := 0; SendMessage(Handle, EM_SETRECTNP, 0, LongInt(@Loc)); SendMessage(Handle, EM_GETRECT, 0, LongInt(@Loc)); {debug} end;
procedure TFloatSpinEdit.WMSize(var Message: TWMSize); var MinHeight: Integer; begin inherited; MinHeight := GetMinHeight; { text edit bug: if size to less than minheight, then edit ctrl does not display the text } if Height < MinHeight then Height := MinHeight else if FButton <> nil then begin if NewStyleControls and Ctl3D then FButton.SetBounds(Width - FButton.Width - 5, 0, FButton.Width, Height - 5) else FButton.SetBounds (Width - FButton.Width, 1, FButton.Width, Height - 3); SetEditRect; end; end;
function TFloatSpinEdit.GetMinHeight: Integer; var DC: HDC; SaveFont: HFont; I: Integer; SysMetrics, Metrics: TTextMetric; begin DC := GetDC(0); GetTextMetrics(DC, SysMetrics); SaveFont := SelectObject(DC, Font.Handle); GetTextMetrics(DC, Metrics); SelectObject(DC, SaveFont); ReleaseDC(0, DC); I := SysMetrics.tmHeight; if I > Metrics.tmHeight then I := Metrics.tmHeight; Result := Metrics.tmHeight + I div 4 + GetSystemMetrics(SM_CYBORDER) * 4 + 2; end;
procedure TFloatSpinEdit.UpClick (Sender: TObject); begin if ReadOnly then MessageBeep(0) else Value := Value + FIncrement; end;
procedure TFloatSpinEdit.DownClick (Sender: TObject); begin if ReadOnly then MessageBeep(0) else Value := Value - FIncrement; end;
procedure TFloatSpinEdit.WMPaste(var Message: TWMPaste); begin if not FEditorEnabled or ReadOnly then Exit; inherited; end;
procedure TFloatSpinEdit.WMCut(var Message: TWMPaste); begin if not FEditorEnabled or ReadOnly then Exit; inherited; end;
procedure TFloatSpinEdit.CMExit(var Message: TCMExit); begin inherited; if CheckValue (Value) <> Value then SetValue (Value); end;
function TFloatSpinEdit.GetValue: Double; begin try Result := StrToFloat (Text); except Result := FMinValue; end; end;
procedure TFloatSpinEdit.SetValue (NewValue: Double); begin Text := FloatToStr (CheckValue (NewValue)); end;
function TFloatSpinEdit.CheckValue (NewValue: Double): Double; begin Result := NewValue; if (FMaxValue <> FMinValue) then begin if NewValue < FMinValue then Result := FMinValue else if NewValue > FMaxValue then Result := FMaxValue; end; end;
procedure TFloatSpinEdit.CMEnter(var Message: TCMGotFocus); begin if AutoSelect and not (csLButtonDown in ControlState) then SelectAll; inherited; end;
>>kloge: Hvordan i alverden vil du med 3 linier give en TEdit+TSpinButton samme funktionalitet som en TSpinEdit?? Med en TSpinEdit kan man direkte angive min og max værdier i Object Inspectoren, som kan indtastes. Derudover kan man med en TSpinEdit bruge værdien, som er indtastet, direkte, uden brug af StrToInt. Med mindre du vil lave det som een komponent, har man jo så også 2 komponenter at holde styr på, i stedet for een. Eller har jeg misforstået dit svar??
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.