2012-11-16 44 views
1

我想解決的問題是向用戶顯示字段中剩餘的字符,因爲它們鍵入到TDBEdit中。使TDBEdit顯示剩餘的字符

目前我正在做沿着

lCharRemaining.Caption := Field.Size - length(dbedit.text); 

即在OnChange事件的TDBEdit,這工作完全正常更新標籤線的東西。不過,我想爲一些TDBEdits做這件事,並試圖編寫一個自定義組件,它將顯示右側編輯框中剩餘的長度。但是它會干擾編輯。我可能認爲我可以在有人打字的時候顯示提示,表明該領域的剩餘空間 - 有什麼建議嗎?

這裏是我的組件代碼(如果有人可以提出改進建議)。

unit DBEditWithLenghtCountdown; 

interface 

uses 
    SysUtils, Classes, Controls, StdCtrls, Mask, DBCtrls, messages, Graphics; 

type 
    TDBEditWithLenghtCountdown = class(TDBEdit) 
    private 
    { Private declarations } 
    FCanvas: TCanvas; 
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT; 
    protected 
    { Protected declarations } 
    property Canvas: TCanvas read FCanvas; 
    procedure WndProc(var Message: TMessage); override; 
    public 
    { Public declarations } 
    function CharactersRemaining : integer; 
    constructor Create(AOwner: TComponent); override; 
    destructor Destroy; override; 
    published 
    { Published declarations } 
    end; 

procedure Register; 

implementation 

uses 
    db, Types; 

procedure Register; 
begin 
    RegisterComponents('Samples', [TDBEditWithLenghtCountdown]); 
end; 

{ TDBEditWithLenghtCountdown } 

function TDBEditWithLenghtCountdown.CharactersRemaining: integer; 
begin 
    result := -1; 
    if Assigned(Field)then 
    begin 
    result := Field.Size - Length(Text); 
    end; 
end; 

constructor TDBEditWithLenghtCountdown.Create(AOwner: TComponent); 
begin 
    inherited Create(AOwner); 
    FCanvas := TControlCanvas.Create; 
    TControlCanvas(FCanvas).Control := Self; 
end; 

destructor TDBEditWithLenghtCountdown.Destroy; 
begin 
    FCanvas.Free; 
    inherited; 
end; 

procedure TDBEditWithLenghtCountdown.WMPaint(var Message: TWMPaint); 
var 
    R: TRect; 
    Remaining : string; 
    WidthOfText: Integer; 
    x: Integer; 
begin 
    inherited; 
    if not focused then 
    exit; 


    Remaining := IntToStr(CharactersRemaining); 
    R := ClientRect; 
    Inc(R.Left, 1); 
    Inc(R.Top, 1); 
    Canvas.Brush.Assign(Self.Brush); 
    Canvas.Brush.Style := bsClear; 
    Canvas.Font.Assign(Self.Font); 
    Canvas.Font.Color := clRed; 

    WidthOfText := Canvas.TextWidth(Remaining); 
    x := R.right - WidthOfText - 4; 
    Canvas.TextOut(x,2, Remaining); 
end; 

procedure TDBEditWithLenghtCountdown.WndProc(var Message: TMessage); 
begin 
    inherited WndProc(Message); 
    with Message do 
    case Msg of 
     CM_MOUSEENTER, CM_MOUSELEAVE, WM_LBUTTONUP, WM_LBUTTONDOWN, 
     WM_KEYDOWN, WM_KEYUP, 
     WM_SETFOCUS, WM_KILLFOCUS, 
     CM_FONTCHANGED, CM_TEXTCHANGED: 
     begin 
     Invalidate; 
     end; 
    end; // case 
end; 

end. 
+0

它如何「干擾編輯?」你基本上只是說「它不工作」,這是調試歷史中最不有用的短語。當你嘗試做什麼時出了什麼問題? –

+0

@MasonWheeler當您接近編輯框的末尾時,剩餘長度文本會覆蓋當前正在鍵入的內容。 – Alister

+0

但爲什麼你想要一個新的組件?它可以推廣到所有派生自TCustomEdit的類 –

回答

1

您可以測試它會是什麼樣子,沒有任何文字干擾通過設置編輯利潤留下空間的提示文本。快速測試:

type 
    TDBEditWithLenghtCountdown = class(TDBEdit) 
    .. 
    protected 
    procedure CreateWnd; override; 
    property Canvas: TCanvas read FCanvas; 
    .. 


procedure TDBEditWithLenghtCountdown.CreateWnd; 
var 
    MaxWidth, Margins: Integer; 
begin 
    inherited; 
    MaxWidth := Canvas.TextWidth('WW'); 
    Margins := Perform(EM_GETMARGINS, 0, 0); 
    Margins := MakeLong(HiWord(Margins), LoWord(Margins) + MaxWidth); 
    Perform(EM_SETMARGINS, EC_LEFTMARGIN or EC_RIGHTMARGIN, Margins); 
end; 


超越這是個人意見,但我覺得這是一個有點混亂。我會做的是可能在派生編輯上發佈狀態面板字段,並在編輯控件的文本發生更改時分配一些文本。

編輯:這裏是一個應該在註釋中提到的問題的護理(如導航留下了一個長文本,編輯文本覆蓋提示文本)稍微擴展版本,並且還設置僅在控制具有焦點利潤率。 (而不是從問題複製完整的代碼,只能修改位。)

type 
    TDBEditWithLenghtCountdown = class(TDBEdit) 
    private 
    FCanvas: TCanvas; 
    FTipWidth: Integer; 
    FDefMargins: Integer; 
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT; 
    protected 
    .. 


procedure TDBEditWithLenghtCountdown.WMPaint(var Message: TWMPaint); 
var 
    PaintStruct: TPaintStruct; 
    EndPaint: Boolean; 
    Rgn: HRGN; 
    R, TipR: TRect; 
    Remaining : string; 
begin 
    if not Focused then 
    inherited 
    else begin 
    EndPaint := Message.Dc = 0; 
    if Message.DC = 0 then 
     Message.DC := BeginPaint(Handle, PaintStruct); 

    R := ClientRect; 
    TipR := R; 
    TipR.Left := TipR.Right - FTipWidth; 
    Remaining := IntToStr(CharactersRemaining); 
    Canvas.Handle := Message.DC; 
    SetBkColor(Canvas.Handle, ColorToRGB(Color)); 
    Canvas.Font := Font; 
    Canvas.Font.Color := clRed; 
    Canvas.TextRect(TipR, Remaining, [tfSingleLine, tfCenter, tfVerticalCenter]); 

    R.Right := TipR.Left; 
    Rgn := CreateRectRgn(R.Left, R.Top, R.Right, R.Bottom); 
    SelectClipRgn(Canvas.Handle, Rgn); 
    DeleteObject(Rgn); 
    inherited; 
    if EndPaint then 
     windows.EndPaint(Handle, PaintStruct); 
    end; 
end; 

procedure TDBEditWithLenghtCountdown.WndProc(var Message: TMessage); 
const 
    TipMargin = 3; 
begin 
    inherited WndProc(Message); 
    with Message do 
    case Msg of 
     CM_MOUSEENTER, CM_MOUSELEAVE, WM_LBUTTONUP, WM_LBUTTONDOWN, 
     WM_KEYDOWN, WM_KEYUP, 
     CM_TEXTCHANGED: Invalidate; 
     WM_CREATE: FDefMargins := Perform(EM_GETMARGINS, 0, 0); 
     CM_FONTCHANGED: 
     begin 
      Canvas.Handle := 0; 
      Canvas.Font := Font; 
      FTipWidth := Canvas.TextWidth('67') + 2 * TipMargin; 
     end; 
     WM_SETFOCUS: 
     Perform(EM_SETMARGINS, EC_LEFTMARGIN or EC_RIGHTMARGIN, 
      MakeLong(HiWord(FDefMargins), LoWord(FDefMargins) + FTipWidth)); 
     WM_KILLFOCUS: 
     Perform(EM_SETMARGINS, EC_LEFTMARGIN or EC_RIGHTMARGIN, FDefMargins); 
    end; 
end; 
+0

這是一個很大的改進,可用性明智,並且在添加到TDBEdit末尾時效果很好,但如果您正在編輯中間,或者光標位於開頭,文本仍會覆蓋(覆蓋?)數字。 – Alister

+0

@Alister - 我想你想在WM_PAINT處理程序中將行設置'bsClear'移除到Canvas.Brush.Style,或者在TextOut之前填充背景矩形。 –

+0

@Alister - 更新代碼,爲* underwrite *提出解決方案。 –

1

就像一個基地,你下手,如果不希望得到每一個編輯分量,這裏是每一個組件的通用方法派生自TCustomEdit。

將編輯組件的MaxLength設置爲值> 0,並且該單元將在文本下方爲您繪製一條細紅線作爲填充指示符。

該單元只能出現在您的項目中。

unit ControlInfoHandler; 

interface 

uses 
    Vcl.Forms; 

implementation 

uses 
    System.Classes, 
    Vcl.Graphics, Vcl.Controls, Vcl.StdCtrls; 

type 
    TControlInfoHandler = class(TComponent) 
    private 
    FCurrent :  TWinControl; 
    FCurrentLength : Integer; 
    protected 
    procedure ActiveControlChange(Sender : TObject); 
    procedure ApplicationIdle(Sender : TObject; var Done : Boolean); 
    procedure Notification(AComponent : TComponent; Operation : TOperation); override; 
    end; 

    THackedEdit = class(TCustomEdit) 
    published 
    property MaxLength; 
    end; 

var 
    LControlInfoHandler : TControlInfoHandler; 

    { TControlInfoHandler } 

procedure TControlInfoHandler.ActiveControlChange(Sender : TObject); 
begin 
    FCurrent  := Screen.ActiveControl; 
    FCurrentLength := 0; 
    if Assigned(FCurrent) 
    then 
    FCurrent.FreeNotification(Self); 
end; 

procedure TControlInfoHandler.ApplicationIdle(Sender : TObject; var Done : Boolean); 
var 
    LEdit : THackedEdit; 
    LCanvas : TControlCanvas; 
    LWidth : Integer; 
begin 
    if not Assigned(FCurrent) or not (FCurrent is TCustomEdit) 
    then 
    Exit; 

    LEdit := THackedEdit(FCurrent as TCustomEdit); 

    if (LEdit.MaxLength > 0) 
    then 
    begin 

     LCanvas   := TControlCanvas.Create; 
     LCanvas.Control := LEdit; 

     LCanvas.Pen.Style := psSolid; 
     LCanvas.Pen.Width := 2; 

     LWidth := LEdit.Width - 6; 

     if FCurrentLength <> LEdit.GetTextLen 
     then 
     begin 
      LCanvas.Pen.Color := LEdit.Color; 
      LCanvas.MoveTo(0, LEdit.Height - 4); 
      LCanvas.LineTo(LWidth, LEdit.Height - 4); 
     end; 

     LCanvas.Pen.Color := clRed; 
     LWidth   := LWidth * LEdit.GetTextLen div LEdit.MaxLength; 

     LCanvas.MoveTo(0, LEdit.Height - 4); 
     LCanvas.LineTo(LWidth, LEdit.Height - 4); 

     FCurrentLength := LEdit.GetTextLen; 

    end; 
end; 

procedure TControlInfoHandler.Notification(AComponent : TComponent; Operation : TOperation); 
begin 
    inherited; 
    if (FCurrent = AComponent) and (Operation = opRemove) 
    then 
    FCurrent := nil; 
end; 

initialization 

LControlInfoHandler   := TControlInfoHandler.Create(Application); 
Screen.OnActiveControlChange := LControlInfoHandler.ActiveControlChange; 
Application.OnIdle   := LControlInfoHandler.ApplicationIdle; 

end.