2014-04-25 6 views
-1

버그 (?)가있는 것으로 확인되어 코드에 버그가 발생했습니다.Delphi : TreeView 요소 대 폼 위치 변경

델파이 XE3, Win32. 나는 캡션 0 요소를 얻을이 후

procedure TForm1.FormCreate(Sender: TObject); 
var 
    i, j: integer; 
    mn: TTreeNode; 
begin 
    for i := 1 to 10 do 
    begin 
     mn := TreeView1.Items.Add(nil, 'M' + IntToStr(i)); 
     for j := 1 to 10 do 
     begin 
      TreeView1.Items.AddChild(mn, 'C' + IntToStr(j)); 

     end; 
    end; 
    Position := poDesigned; 
    beep; 
    Caption := IntToStr(TreeView1.Items.Count); 
end; 

:

procedure TForm4.Button1Click(Sender: TObject); 
begin 
    with TForm1.Create(Application) do 
    begin 
     ShowModal; 
     Release; 
    end; 
end; 

하는 Form1에이 일을한다 : 두 가지 형태, 주요 버튼이있다.

하지만

그럼 내가 좋은 번호 (110 요소)를 볼 수 있습니다
procedure TForm1.Button1Click(Sender: TObject); 
begin 
    Caption := IntToStr(TreeView1.Items.Count); 
end; 

...이 코드와 그 형태의 버튼을 ...있을 때.

위치 변경 후 TreeView1.Handleneeded를 쓰면 그 수 또한 좋습니다.

이 문제는 DestroyHandles를 호출하는 RecreateWnd를 기반으로합니다. 그러나 그들은 Show에서만 수리 될 것입니다 (Activate 이벤트에서 나는 좋은 결과를 볼 수 있습니다).

TreeView는 특수 컨트롤입니다. 트리 요소는 하위이며 실제 하위 개체 목록이 있는지 여부에 관계없이 개수가 계산되므로 특수 컨트롤입니다.

ReCreateWnd가 다른 메서드에서 자주 호출하는 주된 문제는 다른 섹션에서도 문제를 일으킬 수 있으므로 모든 .Count 계산 전에 HandleNeeded를 넣을 수는 없습니다.

(우리는 poScreenCenter가 Positionable 인 경우 위치를 나중에 정정 할 수있는 특별한 기본 형식을 가지고 있습니다. 이것은 FormCreate 호출 이후 내부 메소드에서 발생합니다. 이러한 유형의 양식에서만 문제가 발견되었지만 나중에 우리는 간단한 코드로 그것을 재현 할 수있다.)

그래서이 질문에 대한 전세계적인 해결책은 무엇인가?

은 (너무 XE5이를 경험하셨습니까?)

모든 도움, 정보, 문서 주셔서 감사합니다.

+0

'RecreateWnd'가 호출되는 다른 시나리오는 무엇입니까? –

+0

내 가슴. 그는 이러한 발견했다 : CMCtlD3Changed CMSysColorChange 의 BorderStyle SetAxBorderStyle SetBorderIcons 독 setPosition를 SetPopupMode set_PopupParent RecreateAsPopup ShowModal'RecreateWnd'이 프로그램의 결과로 호출되는 다른 시나리오가 무엇인지 SetMainFormOnTaskBar 내 말은 – durumdara

+0

호 작동 안함. 일반적으로 사용자 이벤트에 응답하여 코드를 작성할 때 할당되지 않은 핸들에 문제가 없어야합니다. –

답변

1

Position을 설정하면 양식 HWND이 파괴됩니다. 그것은 또한 모든 자식 HWND를 파괴합니다. TreeView의 HWNDCount을 읽을 때 아직 다시 작성되지 않았기 때문에 0을보고합니다. Position을 설정 한 후 TreeView.HandleNeeded을 호출하면 TreeView가 즉시 HWND을 다시 생성하도록 강제 실행됩니다. 그러면 트리 캐시 된 트리 노드가 다시로드됩니다. 내부적으로 TreeView의 HWND이 파괴되었을 때 (단, TreeView.CreateWndRestores 속성이 True 인 경우에만 해당) 기본적으로이 속성이 true로 설정됩니다.

TreeView는 자식 노드를 HWND 안에 저장합니다. Items.Count을 읽는 것만으로 얼마나 많은 노드가 있는지 묻습니다. HWND이 없으면 Count은 0이됩니다. TTreeViewTTreeNode 개체 목록을 유지하지 않고 단순히 물리적 노드의 사용자 정의 데이터로 할당합니다 그들 자신. 노드가 트리에서 제거되면 TTreeView은 연관된 TTreeNode 객체를 해제합니다. HWND 레크리에이션의 경우 TTreeViewTTreeNode 데이터를 캐시 한 다음 HWND을 다시 만들 때 새 노드에 다시 할당합니다. 그러나 다시, TTreeNode 개체를 추적하지 않습니다. TTreeView가 할 수 있었던 무엇

HWND 파괴하는 동안 노드의 현재 번호를 저장 한 다음 HWND 아직 다시 생성되지 않은 경우 가치 Items.Count 수익을 가지고있다. 하지만 슬프게도 TTreeView은 그렇게하지 않습니다. 그러나 TTreeView을 서브 클래 싱하여 수동으로 구현하여 CreateWnd()DestroyWnd() 메서드를 호출 한 다음 HandleAllocated이 true 일 때 실제 Items.Count을 반환하고 false 인 경우 캐시 된 값을 반환하는 함수를 작성할 수 있습니다.

양식이 TTreeView 사용자에게 표시되는 경우, 제어는 매우 Items.Count가 제공되는, HWND없이 볼 수 없기 때문에 다음의 HWND (및 어린이의 HWND가)이 사용할 수있는 사용자가 볼 수 있으면

. 표시되는 동안 HWND이 파괴되는 경우 VCL은 즉시 HWND을 다시 생성하므로 사용자는 누락 된 컨트롤을 보지 못합니다. 그러나 HWND이 파괴 된 경우 Form (또는 TreeView)이 사용자에게 표시되지 않으면 Form/TreeView가 다시 표시 될 때까지 실제로 필요하기 전까지 HWND이 다시 생성되지 않습니다.

항상 양식 작성시 poDesigned 될하기 위해 Position을 강요하고 있기 때문에, 왜 단지 디자인 타임에 poDesignedPosition를 설정하지? 그렇지 않으면, 당신은 단순히 적어도, 나중에 트 리뷰를 채우는 대신 전에 Position을 설정할 수 있습니다 : 이것은 델파이의 모든 버전에서 발생

procedure TForm1.FormCreate(Sender: TObject); 
var 
    i, j: integer; 
    mn: TTreeNode; 
begin 
    Position := poDesigned; // <-- here 
    for i := 1 to 10 do 
    begin 
    mn := TreeView1.Items.Add(nil, 'M' + IntToStr(i)); // <-- first call to Add() forces HWND recreation if needed 
    for j := 1 to 10 do 
    begin 
     TreeView1.Items.AddChild(mn, 'C' + IntToStr(j)); 
    end; 
    end; 
    Beep; 
    Caption := IntToStr(TreeView1.Items.Count); // <-- correct value reported 
end; 

합니다. 이것은 단순히 VCL에서 일하는 방식입니다.

참고로 Release() 대신 Free()을 사용해야합니다. Release()은 양식이 유휴 상태가 될 때까지 Free()을 지연하여 양식 처리기에서와 같이 양식 자체가 Free() 일 때만 사용됩니다. 양식이 이미 닫혀 있고 ShowModal() 종료 시간까지 유휴 상태이므로 양식을 바로 Free()으로 보내면됩니다.

+0

이는 데모 코드입니다. 우리는 "FormCreate"후에 일을하는 특별한 기본 폼을 사용하고 있습니다. 예를 들어, 화면이 poScreenCenter 인 경우 poDesigned로 변경하고 가운데로 위치를 지정합니다 (사용자가 양식의 위치를 ​​저장하고 복원하기 때문에). 따라서이 데모보다 더 복잡합니다. 어제 내 친구. 상속 된 CM_RECREATEWND 핸들러가 작성한 코드를 작성했으며 정상 핸들러 이후에 손실 된 핸들을 복원 할 수있는 것은 무엇입니까? – durumdara

0

과민 반응을하는 것 같습니다. 당신은 상태 :

ReCreateWnd가 자주 다른 방법으로 호출하는 주된 문제는 다른 섹션에서도 문제를 일으킬 수 있으므로 모든 .Count 계산 전에 HandleNeeded를 넣을 수는 없습니다.

이 CMCtlD3Changed CMSysColorChange의 BorderStyle SetAxBorderStyle SetBorderIcons 독 setPosition를 SetPopupMode set_PopupParent RecreateAsPopup ShowModal SetMainFormOnTaskBar이

실제로이 방법은 윈도우 레크리에이션의 원인이됩니다

는하지만 의견 당신이 다른 시나리오는 말한다. 하지만 그게 왜 너에게 중요한거야? 정기적으로 MainFormOnTaskBar에 할당하고 즉시 트리보기에서 항목 수를 요청합니까?Ctl3D을 정기적으로 변경 하시겠습니까? BorderIcons을 동적으로 변경 하시겠습니까? 나는 그것을 매우 의심한다.

그래서 나는 Position의 설정 타이밍과 관련된 즉각적인 문제를 해결해야한다고 생각합니다. 트리 뷰를 채우기 전에 Position이 설정되었는지 확인하여 처리 할 것입니다. 디자인 타임에 Position을 설정하거나 트리보기를 채우기 전에 설정하십시오.

물론 윈도우 생성과 관련된 다른 문제가있을 수 있습니다. 케이스별로 사례를 처리해야합니다. 나는이 문제를 사라지게하는 일종의 마법 스위치가 필요하다고 생각합니다. 나는 하나 있다고 믿지 않는다. 핸들이 생성되기 전에 항목 수를 읽으려고하면이 문제가 발생합니다. 해결 방법은 핸들이 만들어지기 전에 항목 수를 읽지 않는 것입니다.