2016-06-18 22 views
0

나는 을 TScrollBox에 사용하여 일부 텍스트를 표시하고 상단에 TLabel을 헤더 정보로 사용합니다. 때로는 메모가 스크롤 상자보다 넓으며 물론 Horizontal scroll bar을 사용하여 메모의 텍스트를 보려면 좌우로 스크롤 할 수 있습니다. 레이블을 항상 스크롤 상자의 보이는 영역 중앙에 배치하는 헤더로 싶습니다. Label1.Left:= (Scrollbox1.Width div 2) - (Label1.Width div 2);을 설정하여이 작업을 수행 할 수 있지만 작동하지만 깜박임이 번갈아 가며 흔들릴 때 흔들립니다. 메모가 부드럽게 움직이며 레이블은 움직이지 않습니다.라벨을 스크롤 박스의 중앙에 부드럽게 유지하는 방법은 무엇입니까?

unit Unit1; 

interface 

uses 
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; 

type 

    TScrollBox=Class(VCL.Forms.TScrollBox) 
    procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL; 
    private 
    FOnScrollHorz: TNotifyEvent; 
    public 
    Property OnScrollHorz:TNotifyEvent read FOnScrollHorz Write FonScrollHorz; 
    End; 

    TForm1 = class(TForm) 
    ScrollBox1: TScrollBox; 
    Label1: TLabel; 
    Memo1: TMemo; 
    procedure FormCreate(Sender: TObject); 
    procedure ScrollBox1Resize(Sender: TObject); 
    private 
    procedure MyScrollHorz(Sender: TObject); 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

procedure TScrollBox.WMHScroll(var Message: TWMHScroll); 
begin 
    inherited; 
    if Assigned(FOnScrollHorz) then FOnScrollHorz(Self); 
end; 

procedure TForm1.MyScrollHorz(Sender: TObject); 
begin 
    Label1.Left:= (Scrollbox1.Width div 2) - (Label1.Width div 2); 
end; 

procedure TForm1.ScrollBox1Resize(Sender: TObject); 
begin 
    Label1.Left:= (Scrollbox1.Width div 2) - (Label1.Width div 2); 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    ScrollBox1.OnScrollHorz := MyScrollHorz; 
end; 

end. 

및 DFM :

object Form1: TForm1 
    Left = 0 
    Top = 0 
    Caption = 'Form1' 
    ClientHeight = 212 
    ClientWidth = 458 
    Color = clBtnFace 
    DoubleBuffered = True 
    Font.Charset = DEFAULT_CHARSET 
    Font.Color = clWindowText 
    Font.Height = -11 
    Font.Name = 'Tahoma' 
    Font.Style = [] 
    OldCreateOrder = False 
    OnCreate = FormCreate 
    PixelsPerInch = 96 
    TextHeight = 13 
    object ScrollBox1: TScrollBox 
    Left = 0 
    Top = 0 
    Width = 458 
    Height = 212 
    HorzScrollBar.Smooth = True 
    HorzScrollBar.Tracking = True 
    Align = alClient 
    BiDiMode = bdLeftToRight 
    DoubleBuffered = True 
    ParentBiDiMode = False 
    ParentDoubleBuffered = False 
    TabOrder = 0 
    OnResize = ScrollBox1Resize 
    ExplicitHeight = 337 
    object Label1: TLabel 
     Left = 192 
     Top = 30 
     Width = 69 
     Height = 13 
     BiDiMode = bdLeftToRight 
     Caption = 'Details header' 
     ParentBiDiMode = False 
    end 
    object Memo1: TMemo 
     Left = 24 
     Top = 70 
     Width = 700 
     Height = 89 
     Lines.Strings = (
     'Details...') 
     TabOrder = 0 
    end 
    end 
end 

내가 DoubleBuffered를 사용하여 시도했지만 도움이되지 않습니다 여기

enter image description here

는 단위입니다.

깜박임/흔들림없이 Label1을 이동하는 방법을 제안합니다. 스크롤 할 때 Memo1처럼 매끄럽게 작동합니까?


편집 :

내가 양식에 3 scrollboxes이 각각 하나의 헤더 최대 3 개 메모를 포함하는 것이 디자인은 결국 될 것입니다. 그리고 스크롤은 동일한 스크롤 상자의 모든 메모가 동시에 스크롤되어야하므로 스크롤 상자 여야합니다.

enter image description here


편집 2 : 아래

대답 할 즉, 양식에 다음, 외부 scrollboxes을 형성 또는 패널에 라벨을 넣어 작업과 얼마나 내가 볼 수 없다는 것을 의미 좋은 솔루션을 제공하지만 Scrollbox의 가운데에 놓인 Labels을 넣고 Form에 붙여야합니다. 그리고 나서 Scrollbox'sscroll bars 또는 scroll bars에 직접 Form으로 이동하십시오. 이것은 원하는 효과를 얻지 만, Labels이 더 이상 Scrollbox의 일부가 아니기 때문에 약간의 불편 함을 더합니다.

+1

스크롤 상자에 레이블을 추가 할 필요가 없습니다. 패널을 컨테이너로 사용하고, 패널 위에 레이블을 배치하고 클라이언트 구성 요소로 스크롤 상자를 패널에 배치하십시오. – sddk

+1

메모를 스크롤하면 스크롤을 허용 할 수 있습니다. 스크롤하면 메모를 스크롤 할 수 있습니다 (역주 : TMemo.Scrollbars 속성 사용). 내장 된 스크롤 기능을 사용하면 스크롤 상자가 전혀 필요 없으며 레이블을 옮길 필요가 없습니다. –

+0

@KenWhite, sddk : 양식에 최대 4 개의 스크롤 상자를 넣을 수 있으며 각 스크롤 상자에 헤더가있는 메모를 최대 3 개까지 가질 수 있습니다. 그래서 그들은 그룹화 된 스크롤 박스에 있어야합니다. 폼/패널에 배치하고 관련 그룹 정보를 스크롤하는 데 필요한 디자인을 유지하는 방법을 알지 못합니다. 질문 편집을 참조하십시오. –

답변

2

-은 ". 메모가 원활하게 이동, 라벨은하지 않습니다"당신이 이동을 방지하기 위해 노력하고 있기 때문이다

. OnScrollHorz 핸들러를 분리하면 라벨이 부드럽게 움직입니다. 그러나 그것은 당신이 원하는 것이 아니며, 더 이상 그 중심에 있지 않을 것입니다.

inherited (WM_HSCROLL) 전화가 걸리는 동안 레이블이 메모와 함께 이동합니다. 기본 처리 후에 레이블을 재배치하므로 깜박임이 발생합니다.

기본 스크롤 (OnBeforeHorzScroll) 전에 실행될 추가 이벤트 처리기를 노출하고 시작할 때 레이블을 숨길 수 있습니다. 은 부드럽게 가운데에 인 반면, 라벨이 일시적으로 사라지는 경우 다른 종류의 깜박임이 발생합니다. 여전히 만족스럽지 않을 수 있습니다.

해결 방법은 폼에 부모 역할을하는 컨트롤 인 스크롤 상자의 형제를 사용하는 것입니다. TLabel을 그래픽 컨트롤로 사용할 수는 없지만 TStaticText을 사용할 수 있습니다. 정적이 우연히 디자인 타임에 스크롤 상자 뒤로 이동하면 IDE의 "구조 창"이 유용 할 수 있습니다.

+0

아하, 이제 왜 깜박 거리는 지 이해가된다.디자인에 대한 그림보기에서 질문에 맞게 편집하십시오. 즉, 폼에 머리글을 넣을 수있는 옵션이 많지 않습니다. 각 스크롤 상자는 그룹화 된 모든 메모 컨트롤을 스크롤해야하기 때문입니다. –

+1

'TStaticText'를 사용할 수 있다고 생각합니다. 스크롤 상자에 하나를 버리고 구조 패널을 사용하여 폼을 폼으로 이동하여 부모가 폼이되도록합니다. 폼의 OnResize에서 위치를 계산하십시오. 하나를 시도하고 나는 어떤 합병증이있을 것이라고 생각하지 않습니다. –

+0

이런 식으로 잘 작동하는 것 같아요, 왜 작동하는지 놀랍습니다. –

2

당신은 이런 식으로 작업을 수행 할 수 있습니다 대신 스크롤 박스의

은 폼에 스크롤 막대를 넣어. 정렬을 맨 아래로 설정하십시오 (또는 더 많은 열을 원하거나 각각의 패널을 자신의 패널에 넣을 수 있으려면 크기와 위치를 수동으로 설정하십시오). 그런 다음 메모의 크기를 설정하고 양식의 가운데에 레이블을 배치하십시오.

ScrollBar1.Min:=0-Memo1.Left; 
ScrollBar1.Max:=Memo1.Width-Form1.ClientWidth+Memo1.Left; 

마지막 것은 스크롤바 OnChange 이벤트를 설정하는 것입니다 : 코드를 삽입 (동적으로 코드를 통해 아마도)이 메모의 크기를 설정 한 후

procedure TForm1.ScrollBar1Change(Sender: TObject); 
begin 
    Memo1.Left:=0-ScrollBar1.Position; 
    Memo2.Left:=0-ScrollBar1.Position; 
    ... 
    MemoXY.Left:=0-ScrollBar1.Position; 
end; 

다음과 비슷한 모습이 될 것입니다 양식 :

Scrollable memos

는 완료! 안정적인 가운데 맞춤 레이블과 부드럽게 스크롤 할 수있는 메모가 있습니다.

편집 :

Vertical scroll

그리고 전체 소스 코드 :

unit Unit1; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, StdCtrls, ExtCtrls; 

type 
    TForm1 = class(TForm) 
    Panel1: TPanel; 
    Panel2: TPanel; 
    Panel3: TPanel; 
    Label1: TLabel; 
    Label2: TLabel; 
    Label3: TLabel; 
    Label4: TLabel; 
    Label5: TLabel; 
    Label6: TLabel; 
    Memo1: TMemo; 
    Memo2: TMemo; 
    Memo3: TMemo; 
    Memo4: TMemo; 
    Memo5: TMemo; 
    Memo6: TMemo; 
    ScrollBar1: TScrollBar; 
    ScrollBar2: TScrollBar; 
    ScrollBar3: TScrollBar; 
    ScrollBar4: TScrollBar; 
    ScrollBar5: TScrollBar; 
    ScrollBar6: TScrollBar; 
    procedure FormCreate(Sender: TObject); 
    procedure ScrollBarHChange(Sender: TObject); 
    procedure ScrollBarVChange(Sender: TObject); 
    procedure FormResize(Sender: TObject); 
    private 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

procedure TForm1.FormCreate(Sender: TObject); 
var cycle: Integer; 
begin 
    //GENERATE YOUR COMPONENTS HERE 

    //sets every components tag to its default top position 
    //(you can do this in any other way for example using array) 
    for cycle:=0 to Form1.ComponentCount-1 do 
    begin 
    if(Form1.Components[cycle] is TControl)then 
     Form1.Components[cycle].Tag:=(Form1.Components[cycle] as TControl).Top 
    end; 
end; 

procedure TForm1.FormResize(Sender: TObject); 
begin 
    //changes the panels sizes and positions 
    Panel1.Width:=Form1.ClientWidth div 3; 
    Panel2.Width:=Form1.ClientWidth div 3; 
    Panel3.Width:=Form1.ClientWidth div 3; 
    Panel2.Left:=Panel1.Width+1; 
    Panel3.Left:=Panel1.Width+Panel2.Width+1; 

    //if you dont want all scrollbars to reset on window resize, you need to handle the positioning of elements when window (and panels) size is changing 
    ScrollBar1.Position:=ScrollBar1.Min; 
    ScrollBar2.Position:=ScrollBar2.Min; 
    ScrollBar3.Position:=ScrollBar3.Min; 
    ScrollBar4.Position:=ScrollBar4.Min; 
    ScrollBar5.Position:=ScrollBar5.Min; 
    ScrollBar6.Position:=ScrollBar6.Min; 

    //make these tests on the widest element of each panel (8 is just a margin so the memo has some space on the right) 
    if((Memo1.Left+Memo1.Width)>(Panel1.ClientWidth-ScrollBar4.Width-8))then 
    begin 
    ScrollBar1.Enabled:=true; 
    ScrollBar1.Max:=Memo1.Width-Panel1.ClientWidth+Memo1.Left+ScrollBar4.Width+8; 
    end 
    else 
    ScrollBar1.Enabled:=false; 

    if((Memo3.Left+Memo3.Width)>(Panel2.ClientWidth-ScrollBar5.Width-8))then 
    begin 
    ScrollBar2.Enabled:=true; 
    ScrollBar2.Max:=Memo3.Width-Panel1.ClientWidth+Memo3.Left+ScrollBar5.Width+8; 
    end 
    else 
    begin 
    ScrollBar2.Position:=ScrollBar2.Min; 
    ScrollBar2.Enabled:=false; 
    end; 

    if((Memo5.Left+Memo5.Width)>(Panel3.ClientWidth-ScrollBar6.Width-8))then 
    begin 
    ScrollBar3.Enabled:=true; 
    ScrollBar3.Max:=Memo5.Width-Panel1.ClientWidth+Memo5.Left+ScrollBar6.Width+8; 
    end 
    else 
    ScrollBar3.Enabled:=false; 

    //make these tests on the bottom element of each panel (16 is just a margin so the memo has some space on the bottom) 
    if((Memo2.Top+Memo2.Height)>(Panel1.ClientHeight-ScrollBar1.Height-16))then 
    begin 
    ScrollBar4.Enabled:=true; 
    ScrollBar4.Max:=Memo2.Top+Memo2.Height-Panel1.ClientHeight+ScrollBar1.Height+16; 
    end 
    else 
    ScrollBar4.Enabled:=false; 

    if((Memo4.Top+Memo4.Height)>(Panel2.ClientHeight-ScrollBar2.Height-16))then 
    begin 
    ScrollBar5.Enabled:=true; 
    ScrollBar5.Max:=Memo4.Top+Memo4.Height-Panel2.ClientHeight+ScrollBar2.Height+16; 
    end 
    else 
    ScrollBar5.Enabled:=false; 

    if((Memo6.Top+Memo6.Height)>(Panel3.ClientHeight-ScrollBar3.Height-16))then 
    begin 
    ScrollBar6.Enabled:=true; 
    ScrollBar6.Max:=Memo6.Top+Memo6.Height-Panel3.ClientHeight+ScrollBar3.Height+16; 
    end 
    else 
    ScrollBar6.Enabled:=false; 
end; 

procedure TForm1.ScrollBarHChange(Sender: TObject); 
var cycle: Integer; 
begin 
    for cycle:=0 to ((Sender as TScrollBar).Parent as TPanel).ControlCount-1 do 
    begin 
    if(((Sender as TScrollBar).Parent as TPanel).Controls[cycle] is TMemo)then 
     (((Sender as TScrollBar).Parent as TPanel).Controls[cycle] as TMemo).Left:=0-(Sender as TScrollBar).Position+8; 
    end; 
end; 

procedure TForm1.ScrollBarVChange(Sender: TObject); 
var cycle: Integer; 
begin 
    for cycle:=0 to ((Sender as TScrollBar).Parent as TPanel).ControlCount-1 do 
    begin 
    if(not (((Sender as TScrollBar).Parent as TPanel).Controls[cycle] is TScrollBar))then 
     (((Sender as TScrollBar).Parent as TPanel).Controls[cycle] as TControl).Top:=(((Sender as TScrollBar).Parent as TPanel).Controls[cycle] as TControl).Tag-(Sender as TScrollBar).Position; 
    end; 
end; 

end. 
다음

자신의 패널과 또한 수직 스크롤 막대와 3 열 각각 버전입니다

.dfm :

object Form1: TForm1 
    Left = 0 
    Top = 0 
    Caption = 'Form1' 
    ClientHeight = 473 
    ClientWidth = 769 
    Color = clBtnFace 
    Font.Charset = DEFAULT_CHARSET 
    Font.Color = clWindowText 
    Font.Height = -11 
    Font.Name = 'Tahoma' 
    Font.Style = [] 
    OldCreateOrder = False 
    OnCreate = FormCreate 
    OnResize = FormResize 
    DesignSize = (
    769 
    473) 
    PixelsPerInch = 96 
    TextHeight = 13 
    object Panel1: TPanel 
    Left = 0 
    Top = 0 
    Width = 257 
    Height = 473 
    Anchors = [akLeft, akTop, akBottom] 
    BevelOuter = bvNone 
    BorderStyle = bsSingle 
    TabOrder = 0 
    object Label1: TLabel 
     Left = 104 
     Top = 16 
     Width = 31 
     Height = 13 
     Caption = 'Label1' 
    end 
    object Label2: TLabel 
     Left = 104 
     Top = 152 
     Width = 31 
     Height = 13 
     Caption = 'Label2' 
    end 
    object Memo1: TMemo 
     Left = 8 
     Top = 32 
     Width = 497 
     Height = 89 
     Lines.Strings = (
     'Memo1') 
     TabOrder = 0 
    end 
    object Memo2: TMemo 
     Left = 8 
     Top = 168 
     Width = 497 
     Height = 89 
     Lines.Strings = (
     'Memo2') 
     TabOrder = 1 
    end 
    object ScrollBar1: TScrollBar 
     AlignWithMargins = True 
     Left = 0 
     Top = 452 
     Width = 236 
     Height = 17 
     Margins.Left = 0 
     Margins.Top = 0 
     Margins.Right = 17 
     Margins.Bottom = 0 
     Align = alBottom 
     PageSize = 0 
     TabOrder = 2 
     OnChange = ScrollBarHChange 
     ExplicitWidth = 253 
    end 
    object ScrollBar4: TScrollBar 
     Left = 236 
     Top = 0 
     Width = 17 
     Height = 452 
     Align = alRight 
     Enabled = False 
     Kind = sbVertical 
     PageSize = 0 
     TabOrder = 3 
     OnChange = ScrollBarVChange 
     ExplicitTop = 248 
     ExplicitHeight = 121 
    end 
    end 
    object Panel2: TPanel 
    Left = 256 
    Top = 0 
    Width = 257 
    Height = 473 
    Anchors = [akLeft, akTop, akBottom] 
    BevelOuter = bvNone 
    BorderStyle = bsSingle 
    TabOrder = 1 
    object Label3: TLabel 
     Left = 104 
     Top = 16 
     Width = 31 
     Height = 13 
     Caption = 'Label3' 
    end 
    object Label4: TLabel 
     Left = 104 
     Top = 152 
     Width = 31 
     Height = 13 
     Caption = 'Label4' 
    end 
    object Memo3: TMemo 
     Left = 8 
     Top = 32 
     Width = 497 
     Height = 89 
     Lines.Strings = (
     'Memo3') 
     TabOrder = 0 
    end 
    object Memo4: TMemo 
     Left = 8 
     Top = 168 
     Width = 497 
     Height = 89 
     Lines.Strings = (
     'Memo4') 
     TabOrder = 1 
    end 
    object ScrollBar2: TScrollBar 
     AlignWithMargins = True 
     Left = 0 
     Top = 452 
     Width = 236 
     Height = 17 
     Margins.Left = 0 
     Margins.Top = 0 
     Margins.Right = 17 
     Margins.Bottom = 0 
     Align = alBottom 
     PageSize = 0 
     TabOrder = 2 
     OnChange = ScrollBarHChange 
     ExplicitWidth = 253 
    end 
    object ScrollBar5: TScrollBar 
     Left = 236 
     Top = 0 
     Width = 17 
     Height = 452 
     Align = alRight 
     Enabled = False 
     Kind = sbVertical 
     PageSize = 0 
     TabOrder = 3 
     OnChange = ScrollBarVChange 
     ExplicitTop = 248 
     ExplicitHeight = 121 
    end 
    end 
    object Panel3: TPanel 
    Left = 512 
    Top = 0 
    Width = 257 
    Height = 473 
    Anchors = [akLeft, akTop, akBottom] 
    BevelOuter = bvNone 
    BorderStyle = bsSingle 
    TabOrder = 2 
    object Label5: TLabel 
     Left = 104 
     Top = 16 
     Width = 31 
     Height = 13 
     Caption = 'Label5' 
    end 
    object Label6: TLabel 
     Left = 104 
     Top = 152 
     Width = 31 
     Height = 13 
     Caption = 'Label6' 
    end 
    object Memo5: TMemo 
     Left = 8 
     Top = 32 
     Width = 497 
     Height = 89 
     Lines.Strings = (
     'Memo5') 
     TabOrder = 0 
    end 
    object Memo6: TMemo 
     Left = 8 
     Top = 168 
     Width = 497 
     Height = 89 
     Lines.Strings = (
     'Memo6') 
     TabOrder = 1 
    end 
    object ScrollBar3: TScrollBar 
     AlignWithMargins = True 
     Left = 0 
     Top = 452 
     Width = 236 
     Height = 17 
     Margins.Left = 0 
     Margins.Top = 0 
     Margins.Right = 17 
     Margins.Bottom = 0 
     Align = alBottom 
     PageSize = 0 
     TabOrder = 2 
     OnChange = ScrollBarHChange 
     ExplicitWidth = 253 
    end 
    object ScrollBar6: TScrollBar 
     Left = 236 
     Top = 0 
     Width = 17 
     Height = 452 
     Align = alRight 
     Enabled = False 
     Kind = sbVertical 
     PageSize = 0 
     TabOrder = 3 
     OnChange = ScrollBarVChange 
     ExplicitTop = 248 
     ExplicitHeight = 121 
    end 
    end 
end 
+0

고마워요! 세로 스크롤 막대가 필요하도록 양식의 높이를 조정할 때 어떻게 문제를 해결할 수 있습니까? 레이블은 양식에서 '스크롤'해야합니다. 맞습니까? –

+1

두 번째 예제는 이제 완전히 기능적인 수직 스크롤바가되도록 게시물을 편집했습니다. 단점은 사용자가 창 크기를 변경할 때 두 스크롤바가 모두 재설정된다는 것입니다 (큰 문제는 아닐 것입니다). 또한 각 패널의 오른쪽 하단 모서리에 아래의 구성 요소를 볼 수있는 작은 사각형이 있습니다 (그러나 쉽게 예를 들어이 작은 사각형을 패널과 함께 숨 깁니다. – jano152

+1

ScrollBarChange 이벤트는 보편적이므로 원하는만큼 여러 구성 요소를 패널에 추가 할 수 있으며 여전히 작동합니다. 비슷한 방식으로 FormResize에서 왼쪽 + 너비가 가장 높고 최상 높이가 가장 큰 구성 요소를 찾아 보편적으로 만들 수 있습니다. 그런 다음 som 구성 요소를 만들어야하고 매번 작동합니다. FormResize 이벤트를 범용으로 사용하는 데 도움이 필요하면 도움을받을 수 있습니다. – jano152

1

두 개의 스크롤 상자를 사용하지 않는 이유.

세로 스크롤 용으로 하나를 사용합니다. 그것에 레이블과 두 번째 스크롤 상자를 메모와 함께 넣습니다.

이 두 번째 스크롤 상자는 필요할 때 가로 스크롤에 사용됩니다.

또는 더 나은 해결책은 자체 스크롤바가 구현 된 TRichEdit와 같은 다른 컨트롤로 TMemo를 대체하는 것입니다. 따라서 스크롤 상자가 하나 뿐이므로 TRicheddit은 텍스트가 넓어 질 때 스크롤을 처리합니다.

+0

이렇게하면 단일 스크롤 막대를 사용하여 내용을보기로 스크롤하기 위해 여러 개의 TMemo (또는 TRichEdit)를 결합 된보기로 함께 그룹화하는 Srollbox의 목적을 무효화 할 수 있습니다. 권리? –

+1

죄송합니다. 세 가지 메모를 모두 동시에 스크롤하고 싶지는 않습니다. 이것은 약간 복잡한 일입니다. 그런 시나리오의 경우 @ jano152 대답이 아마도 가장 좋습니다. – SilverWarior