2012-11-16 4 views
1

내가 해결하고 싶은 문제는 TDBEdit에 입력 할 때 필드에 남은 문자를 사용자에게 표시하는 것입니다. TDBEdit에서 남은 문자 표시하기

은 현재 내가 완벽하게 정상적으로 작동 TDBEdit와 대한 OnChange 이벤트에 레이블을 업데이트

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

즉의 라인을 따라 뭔가를하고 있어요. 그러나 나는 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 - 줄 설정 'bsClear'를 Canvas.Brush에 제거하고 싶습니다. WM_PAINT 핸들러를 사용하거나 TextOut 앞에 배경 사각형을 채 웁니다. –

+0

@Alister - * underwrite *에 대한 해결책을 제안하는 업데이트 된 코드. –

1

을 그냥 모든 편집 - 구성 요소를 도출하지 않으려면 당신이 시작하기위한 기초로, 여기에 모든 구성 요소에 대한 일반적인 접근 방식 TCustomEdit에서 파생됩니다.

편집 구성 요소의 최대 길이를 값> 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.