2016-07-10 9 views
4

나는 MCVE의 코드를 작성하기 위해 다른 q의 작성자에게 나를 보내어 사용자 정의 컴포넌트의 문제점을 설명했다.하위 구성 요소의 TCollection 속성을 올바르게 스트리밍하는 방법 (예 : 포함 된 TDBGrid의 Columns 속성

구성 요소는 포함 된 TDBGrid를 포함하는 TPanel 하위 항목입니다. 소스의 나의 버전과 테스트 프로젝트가 아래에 있습니다.

문제는 포함 된 DBGrid를 영구 열이 생성 된 경우의 테스트 프로젝트가 IDE에서 다시 열릴 때, 는, 예외가 TColumn.Grid.Expanded를 읽고

오류가 발생한다는 것입니다. 속성 Grid이 존재하지 않습니다. 비교를 위해

가, 나 또한 내 양식에 정상을 TDBGrid, DBGrid1를 가지고 : 테스트 프로젝트의 Stream 방법을 실행

이 문제가 발생하는 방법을 보여줍니다. 이 DBGrid1의 열이

Columns = < 
    item 
    Expanded = False 
    FieldName = 'ID' 
    Visible = True 
    end 
[...] 

로 스트리밍되는 반면 포함 된 그리드의 열은 그것은 분명 Grid.ExpandedGrid 접두사와 문제의 원인이되는 다른 열 속성의이

Grid.Columns = < 
    item 
    Grid.Expanded = False 
    Grid.FieldName = 'ID' 
    Grid.Visible = True 
    end 
[...] 

같은 스트리밍됩니다.

문제는 DBGridColumns 이 TCollection 자손이며 DFM의 에있는 최상위 개체가 아니라는 사실과 관련이 있다고 생각합니다.

내 질문은 : 그리드의 열이 올바르게 스트리밍되도록 TMyPanel 코드를 수정해야합니까?

구성 요소 소스 :

unit MAGridu; 

interface 

uses 
    Windows, SysUtils, Classes, Controls, ExtCtrls, DBGrids; 

type 
    TMyPanel = class(TPanel) 
    private 
    FGrid : TDBGrid; 
    public 
    constructor Create(AOwner : TComponent); override; 
    published 
    property Grid : TDBGrid read FGrid; 
    end; 

procedure Register; 

implementation 

procedure Register; 
begin 
    RegisterComponents('Standard', [TMyPanel]); 
end; 

constructor TMyPanel.Create(AOwner: TComponent); 
begin 
    inherited Create(AOwner); 
    FGrid := TDBGrid.Create(Self); 
    FGrid.SetSubcomponent(True); 
    FGrid.Parent := Self; 
end; 

end. 

테스트 프로젝트 소스 :

type 
    TForm1 = class(TForm) 
    DBGrid1: TDBGrid; 
    CDS1: TClientDataSet; 
    DataSource1: TDataSource; 
    MyPanel1: TMyPanel; 
    Memo1: TMemo; 
    Button1: TButton; 
    procedure Button1Click(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
    private 
    procedure Stream; 
    public 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    Stream; 
end; 

procedure TForm1.Stream; 
// This method is included as an easy way of getting at the contents of the project's 
// DFM. It saves the form to a stream, and loads it into a memo on the form. 
var 
    SS : TStringStream; 
    MS : TMemoryStream; 
    Writer : TWriter; 
begin 
    SS := TStringStream.Create(''); 
    MS := TMemoryStream.Create; 
    Writer := TWriter.Create(MS, 4096); 

    try 
    Writer.Root := Self; 
    Writer.WriteSignature; 
    Writer.WriteComponent(Self); 
    Writer.FlushBuffer; 
    MS.Position := 0; 
    ObjectBinaryToText(MS, SS); 
    Memo1.Lines.Text := SS.DataString; 
    finally 
    Writer.Free; 
    MS.Free; 
    SS.Free; 
    end; 
end; 
end. 

procedure TForm1.FormCreate(Sender: TObject); 
var 
    Field : TField; 
begin 
    Field := TIntegerField.Create(Self); 
    Field.FieldName := 'ID'; 
    Field.FieldKind := fkData; 
    Field.DataSet := CDS1; 

    Field := TStringField.Create(Self); 
    Field.FieldName := 'Name'; 
    Field.Size := 20; 
    Field.FieldKind := fkData; 
    Field.DataSet := CDS1; 

    CDS1.CreateDataSet; 
    CDS1.InsertRecord([1, 'One']); 

end; 

end. 
+1

궁금한 점 : 왜 내재 된 그리드의 부모를 기본 MyPanel의 부모로 설정합니까? TMyPanel 동안. 그리드는 MyPanel에 부모 역할을합니다. 그런 식으로 머물러야하지 않습니까? –

+0

@UweRaabe : 관심을 가져 주셔서 감사합니다. "TMyPanel 동안. 그리드는 MyPanel을 부모로 만듭니다." TMyPanel에서 빠져 나올 때 따라야하는지 잘 모르겠다. FGrid의 부모를 만드는 것은 아무런 이유없이 D7에서 Nil이므로 TMyPanel.SetParent에 할당하지 않으면 그리드가 보이지 않는다. – MartynA

+0

@MartynA : TDBGrid를 항상 Panel의 하위 항목으로 만들려고하십니까? 그렇다면 Panel의 생성자는'FGrid.Parent : = Self;'문을 가져야하고'SetParent()'재정의는 완전히 제거되어야합니다. –

답변

2

훨씬 당신이 그것에 대해 할 수가없는 것 같다. 절차 WriteCollectionProp (로컬 TWriter.WriteProperties)을 살펴보면 WriteCollection으로 전화하기 전에 FPropPath이 삭제 된 것을 볼 수 있습니다.

TDBGrid, 또는 더 나은 TCustomDBGrid의 문제는, 컬렉션 stored false로 표시되고 스트리밍 작업을 할 TCustomDBGrid.WriteColumns을 사용하는, DefineProperties에게 위임되어 있다는 것입니다.

해당 메서드를 검사하면 WriteCollection도 호출하지만 FPropPath의 내용은 이전에 지워지지 않았 음을 알 수 있습니다. FPropPath은 (는) 비공개 필드이므로 다소 예상됩니다.

그럼에도 불구하고 표준 사용 사례에서 작동하는 이유는 쓰기의 순간에 FPropPath이 비어 있다는 것입니다.

델파이 10.1 베를린조차 델파이 7과 동일하게 동작하므로이 예제와 함께 QP 보고서를 제출하는 것이 좋습니다.

+0

많은 감사. 나는 원래 q의 OP가 사용하고있는 것이 확실하지 않았기 때문에 D7을 사용했다. 나는 그것이 시애틀 (베를린은 아직 설치되지 않았다)에서 유사하게 행동하는지 확인하고, 당신이 제안한대로 QP 보고서를 제출한다. – MartynA

1

솔루션에는 패널을 스트리밍 루트로 소유하지 않고 패널 자체를 포함하지 않는 포함 된 격자가 포함됩니다. 그리드 속성이 'Grid'에 의해 한정되는 것을 방지 할 수 있습니다. 그리드 속성은 실제로 열 속성이 잘못된 속성으로 인해 제거되는 것을 방지합니다. 즉, 아래는 잘못된 동작에 대한 해결 방법입니다.

constructor TMyPanel.Create(AOwner: TComponent); 
begin 
    inherited Create(AOwner); 
    FGrid := TDBGrid.Create(Self); 
// FGrid.SetSubcomponent(True); 
    FGrid.Parent := Self; 
end; 

csSubComponent 스타일

이제 그리드가 전혀 스트리밍되지 않으며, 제거되지는 SetSubComponent 호출을 제거 위를 달성했다.

그런 다음 패널에서 그리드를 스트리밍 할 패널에 대해 GetChildren을 무시하십시오. GetChildrendocumented으로 어떤 하위 컨트롤이 컨트롤의 저장 (스트리밍)되는지 확인하는 데 사용됩니다. 우리는 하나의 컨트롤 (그리드)을 가지고 있기 때문에 구별 할 필요가 없으며 대신 루트를 수정하는 상속 된 핸들러를 호출 할 수 있습니다.

type 
    TMyPanel = class(TPanel) 
    private 
    FGrid : TDBGrid; 
    public 
    constructor Create(AOwner : TComponent); override; 
    procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override; 
    published 
    property Grid : TDBGrid read FGrid; 
    end; 

... 

procedure TMyPanel.GetChildren(Proc: TGetChildProc; Root: TComponent); 
begin 
    inherited GetChildren(Proc, Self); 
end; 


그런 다음 하위 합병증을 해결 남아있다. 여기서 복잡한 것은 스트리밍 된 속성을 가정하는 패널 앞에 앉아있는 두 번째 그리드입니다. 에 관해서는 this 대답이없는 질문입니다. 이 문제는 위에 제공된 솔루션과 관련이 없습니다. 원래 코드는 동일한 문제점을 표시합니다.

위에서 언급 한 질문을 읽고 데, 그리고 this 하나, 그리고 this 하나, 그리고 this 하나, 여전히 코드의 도움으로 해결 할 수없는, 단서, 그들의 조언, 나는 스트리밍 시스템을 추적하고 온 다음과 같이 내 솔루션을 사용하십시오.

나는 그것이 그것이 있어야하는 방법이라고 주장하지 않습니다. 내가 이것을 어떻게 작동하게 할 수있을뿐입니다. 주요 수정 사항은 서브 글 그리드가 이제 쓸 수 있으며 (프로덕션 코드에 setter가 필요함), 그리드의 조건부 작성 및 패널의 GetChildOwner 오버라이드입니다. 다음은 전체 기기가 TMyPanel2 (TMyPanel은 만들 수 없습니다 ...)입니다.

unit TestPanel2; 

interface 

uses 
    Windows, SysUtils, Classes, Controls, ExtCtrls, DBGrids; 

type 
    TMyPanel2 = class(TPanel) 
    private 
    FGrid : TDBGrid; 
    protected 
    function GetChildOwner: TComponent; override; 
    public 
    constructor Create(AOwner : TComponent); override; 
    destructor Destroy; override; 
    procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override; 
    published 
    property Grid : TDBGrid read FGrid write FGrid; 
    end; 

procedure Register; 

implementation 

procedure Register; 
begin 
    RegisterComponents('Test', [TMyPanel2]); 
end; 

constructor TMyPanel2.Create(AOwner: TComponent); 
begin 
    inherited Create(AOwner); 
    if not (csReading in AOwner.ComponentState) then begin 
    FGrid := TDBGrid.Create(Self); 
    FGrid.Name := 'InternalDBGrid'; 
    FGrid.Parent := Self; 
    end else 
    RegisterClass(TDBGrid); 
end; 

destructor TMyPanel2.Destroy; 
begin 
    FGrid.Free; 
    inherited; 
end; 

function TMyPanel2.GetChildOwner: TComponent; 
begin 
    Result := Self; 
end; 

procedure TMyPanel2.GetChildren(Proc: TGetChildProc; Root: TComponent); 
begin 
    Proc(Grid); 
end; 

end.