2013-10-06 2 views
0

나는 로그로 TMemo을 사용하고 이벤트가 호출 될 때마다 그 행을 추가합니다. 새 줄을 추가하기 전에 BeginUpdate을 사용한 다음 EndUpdate을 사용하고 DoubleBuffered을 사용하도록 설정했습니다. 그러나 스크롤 막대가 두 번 깜박임없이 계속 버퍼링되는 것처럼 보입니다. 스크롤바를 DoubleBuffered := True으로 설정할 수있는 방법이 있습니까?TMemo의 스크롤 막대 DoubleBuffered

편집 :

국경이 너무 깜박 거리는 것처럼 보입니다. 그것이 스크롤 바 (들)과 관련이 있는지 확실하지 않습니다.

unit uMainWindow; 

interface 

uses 
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, IdContext, 
    IdBaseComponent, IDGlobal, IdComponent, IdCustomTCPServer, IdTCPServer, 
    Vcl.ComCtrls, Winsock; 

type 
    TMainWindow = class(TForm) 
    TCPServer: TIdTCPServer; 
    StatusBar: TStatusBar; 
    PageControl: TPageControl; 
    ConfigSheet: TTabSheet; 
    StartButton: TButton; 
    PortEdit: TLabeledEdit; 
    LogSheet: TTabSheet; 
    LogMemo: TMemo; 
    LogEdit: TLabeledEdit; 
    TCPLogSheet: TTabSheet; 
    TCPLogEdit: TLabeledEdit; 
    TCPLogMemo: TMemo; 
    CheckBox1: TCheckBox; 
    procedure StartButtonClick(Sender: TObject); 
    private 

    public 

    end; 

// ============================= Public Vars =================================== 

var 
    MainWindow   : TMainWindow; 
    hServer    : TSocket; 
    sAddr    : TSockAddrIn; 
    ListenerThread  : TThread; 

// =============================== Threads ===================================== 

type 
    TListenThread = class (TThread) 
    private 
    procedure WriteToTCPLog (Text : String); 
    public 
    Form  : TMainWindow; 
    procedure Execute; override; 
end; 

type 
    TReceiveThread = class (TThread) 
    private 
    procedure WriteToTCPLog (Text : String); 
    public 
    Form   : TMainWindow; 
    hSocket  : TSocket; 
    IP   : String; 
    procedure Execute; override; 
end; 

implementation 

{$R *.dfm} 

// ================================= Uses ====================================== 

uses 
    uTools, 
    uCommonConstants; 

// ================================== TListenThread ============================ 

procedure TListenThread.WriteToTCPLog(Text: string); 
var 
    MaxLines : Integer; 
begin 
    if not(Form.CheckBox1.Checked) then exit; 
    if GetCurrentThreadId = MainThreadID then begin 
    Form.TCPLogMemo.Lines.BeginUpdate; 
    MaxLines := StrToInt(Form.TCPLogEdit.Text); 
    if Form.TCPLogMemo.Lines.Count >= MaxLines then begin 
     repeat 
     Form.TCPLogMemo.Lines.Delete(0); 
     until Form.TCPLogMemo.Lines.Count < MaxLines; 
    end; 
    Form.TCPLogMemo.Lines.Add (Text); 
    Form.TCPLogMemo.Lines.EndUpdate; 
    end else begin 
    Text := '[' + DateToStr (Now) + ' - ' + TimeToStr(Now) + '] ' + Text; 
    Synchronize(procedure begin WriteToTCPLog(Text); end); 
    end; 
end; 

procedure TListenThread.Execute; 
var 
    iSize    : Integer; 
    hClient    : TSocket; 
    cAddr    : TSockAddrIn; 
    SynchIP    : String; 
begin 
    WriteToTCPLog ('Server started'); 
    while not (terminated) do begin 
    iSize := SizeOf(cAddr); 
    hClient := Accept(hServer, @cAddr, @iSize); 
    if (hClient <> INVALID_SOCKET) then begin 
     SynchIP := inet_ntoa(cAddr.sin_addr); 
     WriteToTCPLog(SynchIP + ' - connected.'); 
     with TReceiveThread.Create (TRUE) do begin 
     FreeOnTerminate := TRUE; 
     hSocket   := hClient; 
     IP    := SynchIP; 
     Form   := Self.Form; 
     Resume; 
     end; 
    end else begin 
     break; 
    end; 
    end; 
    WriteToTCPLog('Server stopped.'); 
end; 

// ==================================== TReceiveThread ========================= 

procedure TReceiveThread.WriteToTCPLog(Text: string); 
var 
    MaxLines : Integer; 
begin 
    if not(Form.CheckBox1.Checked) then exit; 
    if GetCurrentThreadId = MainThreadID then begin 
    Form.TCPLogMemo.Lines.BeginUpdate; 
    MaxLines := StrToInt(Form.TCPLogEdit.Text); 
    if Form.TCPLogMemo.Lines.Count >= MaxLines then begin 
     repeat 
     Form.TCPLogMemo.Lines.Delete(0); 
     until Form.TCPLogMemo.Lines.Count < MaxLines; 
    end; 
    Form.TCPLogMemo.Lines.Add (Text); 
    Form.TCPLogMemo.Lines.EndUpdate; 
    end else begin 
    Text := '[' + DateToStr (Now) + ' - ' + TimeToStr(Now) + '] ' + Text; 
    Synchronize(procedure begin WriteToTCPLog(Text); end); 
    end; 
end; 

procedure TReceiveThread.Execute; 
var 
    iRecv : Integer; 
    bytBuf : Array[0..1023] of byte; 
begin 
    iRecv := 0; 
    while true do begin 
    ZeroMemory(@bytBuf[0], Length(bytBuf)); 
    iRecv := Recv(hSocket, bytBuf, SizeOf(bytBuf), 0); 
    if iRecv > 0 then begin 
     WriteToTCPLog(IP + ' - data received (' + inttostr(iRecv) + ' bytes).'); 
    end; 
    if iRecv <= 0 then break; 
    end; 
    WriteToTCPLog(IP + ' - disconnected.'); 
    closesocket(hSocket); 
end; 

// ================================= TMainWindow =============================== 

procedure TMainWindow.StartButtonClick(Sender: TObject); 
begin 
    if StartButton.Caption = 'Start' then begin 
    try 
     hServer        := Socket(AF_INET, SOCK_STREAM, 0); 
     sAddr.sin_family     := AF_INET; 
     sAddr.sin_port      := htons(StrToInt(PortEdit.Text)); 
     sAddr.sin_addr.S_addr    := INADDR_ANY; 
     if Bind(hServer, sAddr, SizeOf(sAddr)) <> 0 then raise Exception.Create(''); 
     if Listen(hServer, 3)     <> 0 then raise Exception.Create(''); 
    except 
     OutputError (Self.Handle, 'Error','Port is already in use or blocked by a firewall.' + #13#10 + 
            'Please use another port.'); 
     exit; 
    end; 
    ListenerThread      := TListenThread.Create (TRUE); 
    TListenThread(ListenerThread).Form := Self; 
    TListenThread(ListenerThread).Resume; 
    StartButton.Caption := 'Stop'; 
    end else begin 
    closesocket(hServer); 
    ListenerThread.Free; 
    StartButton.Caption := 'Start'; 
    end; 
end; 

end. 
+1

일부 코드를 표시하거나 해결하려는 내용을 설명 할 수 있습니까? TMemo를 여러 앱에서 로그처럼 사용합니다. Begin/EndUpdate 또는 DoubleBuffered를 사용하지 마십시오. 문제가 없습니다. 'Lines.Add()'를 사용하고 있습니까? –

+0

@MarcusAdams 예'Lines.Add'를 사용합니다. GUI와 동기화되는 많은 tthreads 코드는 없습니다. Begin/EndUpdate 또는 DoubleBuffered를 사용하지 않으면 깜박입니다. –

+1

BeginUpdate/EndUpdate 기능을 사용하지 마십시오. –

답변

4

이중 버퍼링이 여기에 도움이되는지 의심 스럽습니다. 사실, 일반적으로 나는 항상 그것을 피하는 것이 좋습니다. 최신 운영 체제에서는 자동으로 버퍼링을 수행하므로 성능이 저하되고 시각적으로 변경되지 않습니다.

GUI를 너무 자주 업데이트하는 것처럼 문제가 많이 발생합니다. 그림을 버퍼링하는 대신 GUI 컨트롤의 텍스트 내용을 버퍼링하십시오.

  1. 새 로그 메시지를 보관할 문자열 버퍼 인 문자열 버퍼를 만듭니다.
  2. 새로 고침 빈도 (예 : 5Hz)의 타이머를 추가하십시오. 원하는 경우 다른 요율을 선택하십시오.
  3. 새 로그 정보가 있으면 버퍼 문자열 목록에 추가하십시오.
  4. 타이머가 작동하면 버퍼를 GUI 컨트롤에 추가하고 버퍼 목록을 비 웁니다.

날짜 경쟁을 피하기 위해 주 스레드의 버퍼 목록과의 모든 상호 작용을 수행하십시오.

+0

메모의 텍스트 내용이 깜박이지 않습니다. 하숙인과 스크롤바뿐입니다. 당신의 대답을 주셔서 감사합니다. –

+1

당신은 우리에게 그때 repro을 줄 필요가 있다고 생각합니다. 문제가 업데이트 빈도가 아닌 경우 다른 문제가 있기 때문입니다. 어떻게 문제를 재현 할 수 있습니까? –

+0

몇 가지 코드를 추가했습니다. 지속적으로 데이터를 보내는 TCP 클라이언트가 필요합니다. –