如何使此代码可重用

时间:2015-11-22 12:38:24

标签: delphi delphi-xe7 virtualtreeview

我有一种特定的方法来为Virtual Treeview构建节点(我在几年前就把这个例子改成了,并且从来没有理由改变它,直到现在)。由于我在大约150个案例中使用几乎相同的代码,我想尝试让它可重用并减少整个代码行。

我在表格上附上了2个按钮和Vritual Treeview的完整示例代码:

unit Unit1;

interface

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

type

  rTreeData = record
    IndexInMyData: integer;
  end;

  tLine = record
    Level:integer;
    Txt:string;
    NodePointer: PVirtualNode;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    VTV: TVirtualStringTree;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  vArray:array of tLine;

implementation

{$R *.dfm}



procedure TForm1.Button1Click(Sender: TObject);
var
  Node: PVirtualNode;
  Data: ^rTreeData;
  i, j: integer;
begin
  SetLength(vArray,5);
  vArray[0].Level:=0; vArray[0].Txt:='One';
  vArray[1].Level:=1; vArray[1].Txt:='Two';
  vArray[2].Level:=1; vArray[2].Txt:='three';
  vArray[3].Level:=2; vArray[3].Txt:='Four';
  vArray[4].Level:=0; vArray[4].Txt:='Give';

  VTV.BeginUpdate;
  VTV.Clear;
  for i := Low(vArray) to High(vArray) do
  begin
     if i = 0 then
    begin
      Node := VTV.AddChild(nil);
      Data := VTV.GetNodeData(Node);
    end
    else
    begin
      if vArray[i].Level = 0 then
        Node := VTV.AddChild(nil)
      else if vArray[i].Level > vArray[i - 1].Level then
        Node := VTV.AddChild(Node)
      else if vArray[i].Level < vArray[i - 1].Level then
      begin
        Node := Node.Parent;
        for j := 1 to (vArray[i - 1].Level - vArray[i].Level) do
          Node := Node.Parent;
        Node := VTV.AddChild(Node);
      end
      else
      begin
        Node := Node.Parent;
        Node := VTV.AddChild(Node);
      end;

      Data := VTV.GetNodeData(Node);
    end;

    // Create link to your data record into VST node
    Data.IndexInMyData := i;
    vArray[Data.IndexInMyData].NodePointer := Node;
  end;
  VTV.FullExpand;
  VTV.EndUpdate;
end;

function AddNode(vTV: TvirtualStringTree; vI, vLevel, vLevelPrev:integer; vNode:PVirtualNode; var vData:rTreeData):PVirtualNode;
var j:integer;
begin
   if vI = 0 then
    begin
      Result := vTV.AddChild(nil);
      vData := rTreeData(vTV.GetNodeData(Result)^);
    end
    else
    begin
      if vLevel = 0 then  Result := vTV.AddChild(nil)
      else if vLevel > vLevelPrev then  Result := vTV.AddChild(vNode)
      else if vLevel < vLevelPrev then
      begin
        Result := vNode.Parent;
        for j := 1 to (vLevelPrev - vLevel) do
          Result := Result.Parent;
        Result := vTV.AddChild(Result);
      end
      else
      begin
        Result := vNode.Parent;
        Result := vTV.AddChild(Result);
      end;

      vData := rTreeData(vTV.GetNodeData(Result)^);
    end;
    vData.IndexInMyData := vI;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  Node: PVirtualNode;
  Data: ^rTreeData;

  i, j,vLevelPrev: integer;
begin
  SetLength(vArray,5);
  vArray[0].Level:=0; vArray[0].Txt:='One';
  vArray[1].Level:=1; vArray[1].Txt:='Two';
  vArray[2].Level:=1; vArray[2].Txt:='three';
  vArray[3].Level:=2; vArray[3].Txt:='Four';
  vArray[4].Level:=0; vArray[4].Txt:='Give';

  VTV.BeginUpdate;
  VTV.Clear;
  for i := Low(vArray) to High(vArray) do
  begin
    if i = 0 then
      vLevelPrev:=0
    else
      vLevelPrev:=vArray[i-1].Level;

      Node:=AddNode(VTV,i,vArray[i].Level,vLevelPrev,Node,rTreeData(Data^));

//     if i = 0 then
//    begin
//      Node := VTV.AddChild(nil);
//      Data := VTV.GetNodeData(Node);
//    end
//    else
//    begin
//      if vArray[i].Level = 0 then
//        Node := VTV.AddChild(nil)
//      else if vArray[i].Level > vArray[i - 1].Level then
//        Node := VTV.AddChild(Node)
//      else if vArray[i].Level < vArray[i - 1].Level then
//      begin
//        Node := Node.Parent;
//        for j := 1 to (vArray[i - 1].Level - vArray[i].Level) do
//          Node := Node.Parent;
//        Node := VTV.AddChild(Node);
//      end
//      else
//      begin
//        Node := Node.Parent;
//        Node := VTV.AddChild(Node);
//      end;
//
//      Data := VTV.GetNodeData(Node);
//    end;

    // Create link to your data record into VST node
    Data.IndexInMyData := i;
    vArray[Data.IndexInMyData].NodePointer := Node;
  end;
  VTV.FullExpand;
  VTV.EndUpdate;
end;

end.

因此,Button1使用我的代码风格来构建节点。它确实有效。

Button2正在尝试调用AddNode过程来创建AddNode作为可重用代码。 它编译,但它在线上失败#:101:EAccessViolation:

enter image description here

我认为我的使用方式存在问题,分配指针和值...我没有超过这一行,所以我不知道代码的其余部分是否有效。

有关如何解决此问题的建议,如何使代码重复使用?

修改

如果我删除vData参数效果很好: 现在减少了代码:

VTV.BeginUpdate;
  VTV.Clear;
  vLevelPrev:=0
  for i := Low(vArray) to High(vArray) do
  begin
    if i > 0 then vLevelPrev:=vArray[i-1].Level;

    Node:=AddNode(VTV,i,vArray[i].Level,vLevelPrev,Node);

    // Create link to your data record into VST node
    Data := VTV.GetNodeData(Node);
    Data.IndexInMyData := i;
    vArray[Data.IndexInMyData].NodePointer := Node;
  end;
  VTV.FullExpand;
  VTV.EndUpdate;

和AddNode:

function AddNode(vTV: TvirtualStringTree; vI, vLevel, vLevelPrev:integer; vNode:PVirtualNode):PVirtualNode;
var j:integer;
begin
   if vI = 0 then
    begin
      Result := vTV.AddChild(nil);
    end
    else
    begin
      if vLevel = 0 then  Result := vTV.AddChild(nil)
      else if vLevel > vLevelPrev then  Result := vTV.AddChild(vNode)
      else if vLevel < vLevelPrev then
      begin
        Result := vNode.Parent;
        for j := 1 to (vLevelPrev - vLevel) do
          Result := Result.Parent;
        Result := vTV.AddChild(Result);
      end
      else
      begin
        Result := vNode.Parent;
        Result := vTV.AddChild(Result);
      end;

    end;
end;

无论如何通过在AddNode中处理数据来减少代码?

SOLUTION:

我将Data作为本地指针放入AddNode:

function AddNode(vTV: TvirtualStringTree; vI, vLevel, vLevelPrev:integer; vNode:PVirtualNode):PVirtualNode;
var j:integer;
    Data: ^rTreeData;
begin
   if vI = 0 then
      Result := vTV.AddChild(nil)
    else
    begin
      if vLevel = 0 then  Result := vTV.AddChild(nil)
      else if vLevel > vLevelPrev then  Result := vTV.AddChild(vNode)
      else if vLevel < vLevelPrev then
      begin
        Result := vNode.Parent;
        for j := 1 to (vLevelPrev - vLevel) do
          Result := Result.Parent;
        Result := vTV.AddChild(Result);
      end
      else
      begin
        Result := vNode.Parent;
        Result := vTV.AddChild(Result);
      end;
    end;
    Data := VTV.GetNodeData(Result);
    Data.IndexInMyData := vI;
end;

现在我有使用AddNode的最终简化代码:

vLevelPrev := 0;
  for i := Low(vArray) to High(vArray) do
  begin
    if i > 0 then
      vLevelPrev := vArray[i - 1].Level;
    Node := AddNode(VTV, i, vArray[i].Level, vLevelPrev, Node);
    vArray[i].NodePointer := Node;
  end;

1 个答案:

答案 0 :(得分:4)

AddNode调用Button2Click时,指针变量Data仍然未初始化并指向某个任意内存,该内存将被写入AddNode内部,导致访问违规。

我仍然不确定我是否理解,为什么你需要vData参数。将vData作为rTreeData记录的本地指针与Data中的Button1Click一样,并完全删除该参数。在Button2Click中使用I索引vArray