Autor Beitrag
c43777
Hält's aus hier
Beiträge: 1


Delphi 2010
BeitragVerfasst: Fr 14.01.11 04:52 
Ich schreibe momentan an einer unit die mein Programmeinstellungen anzeigen soll
aber leider habe ich ein kleines Problem.


Wenn ich mit
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
procedure TForm1.FormCreate(Sender: TObject);
var i: integer;
begin
  DoubleBuffered:= true;

  for i := 1 to 10 do
    AddSettingsGroup('Test' + IntToStr(i), 'Test caption' + IntToStr(i), 'hinting' + IntToStr(i));

  //UpdateTreeView(Self);

  SelectGroup('Test3');

end;

mehrere Gruppen hinzufüge und dann auf ein Element im TreeView klicke bekomme ich fast immer einen Fehler
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TNewSettingsFormBaseClass.UpdateGroupPanel(Sender: TObject);
var tn: TTreeNode; sg: TMySettingsGroup;
begin
  tn:= FTreeView.Selected;
  if tn = nil then
    exit;

  sg:= PMySettingsGroup(tn.Data)^; /// <<<< hier

da der Pointer in "tn.Data" nicht mehr auf mein Arrayelement zeigt.
Delphi benutzt innerhalb von "AddSettingsGroup" zu teil andere Pointer für "FSettingsGroups".

Wenn ich aber "UpdateTreeView" ausführe gibt es keine Probleme und die Pointer stimmen,
beide verwenden "AddGroupItemToTreeView" um TreeNodes zu erzeugen.

Warum gibt es mit "AddSettingsGroup" Probleme mit "UpdateTreeView" aber nicht?

Hier die komplette unit, zum testen einfach eine neue Form erstellen und hiervon ableiten.
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
281:
282:
283:
284:
285:
286:
287:
288:
289:
290:
291:
292:
293:
294:
295:
296:
297:
298:
299:
300:
301:
302:
303:
304:
305:
306:
307:
308:
309:
310:
311:
312:
313:
314:
315:
316:
317:
318:
319:
320:
321:
322:
323:
324:
325:
326:
327:
328:
329:
330:
331:
332:
333:
334:
335:
336:
337:
338:
339:
340:
341:
342:
343:
344:
345:
346:
347:
348:
349:
350:
351:
352:
353:
354:
355:
356:
357:
358:
359:
360:
361:
362:
363:
364:
365:
366:
367:
368:
369:
370:
371:
372:
373:
374:
375:
376:
377:
378:
379:
380:
381:
382:
383:
384:
385:
386:
387:
388:
389:
390:
391:
392:
393:
394:
395:
396:
397:
398:
399:
400:
401:
402:
403:
404:
405:
406:
407:
408:
409:
410:
411:
412:
413:
414:
415:
416:
417:
418:
419:
420:
421:
422:
423:
424:
425:
426:
427:
428:
429:
430:
431:
432:
433:
434:
435:
436:
437:
438:
439:
440:
441:
442:
443:
444:
445:
446:
447:
448:
449:
450:
451:
452:
453:
454:
455:
456:
457:
458:
459:
460:
461:
462:
463:
464:
465:
466:
467:
468:
469:
470:
471:
472:
473:
474:
475:
476:
477:
478:
479:
480:
481:
482:
483:
484:
485:
486:
487:
488:
489:
490:
491:
492:
493:
494:
495:
496:
497:
498:
499:
500:
501:
502:
503:
504:
505:
506:
507:
508:
509:
510:
511:
512:
513:
514:
515:
516:
517:
518:
519:
520:
521:
522:
523:
524:
525:
526:
527:
528:
529:
530:
531:
532:
533:
534:
535:
536:
537:
538:
539:
540:
541:
542:
543:
544:
545:
546:
547:
548:
549:
550:
551:
552:
553:
554:
555:
556:
557:
558:
559:
560:
561:
562:
563:
564:
565:
566:
567:
568:
569:
570:
571:
572:
573:
574:
575:
576:
577:
578:
579:
580:
581:
582:
583:
584:
585:
586:
587:
588:
589:
590:
591:
592:
593:
594:
595:
unit NewSettingsFormBaseClass;

interface

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

type
  TNewSettingsFormBaseClass = class(TForm)
  private
    type

      TMySettingsType = ( // basic types
                          stInteger,        // Label + Slider
                          stFloat,          // ^^
                          stString,         // Edit
                          stBoolean,        // CheckBox

                          );

      TMySetting          = record
        SettingsType      : TMySettingsType;

        Name              : string;
        Caption           : string;
        Hint              : string;
        ImageIndex        : integer;

        DefaultValue      : string;
        Value             : string;
        NewValue          : string;

        MinValue          : string;
        MaxValue          : string;
        Increment         : string;

      end;
      PMySetting          = ^TMySetting;
      TMySettings         = Array of TMySetting;



      TMySettingsGroup    = record
        ///-----------------------
        ///  needed to add subgroups
        ParentName        : string;     // name of the parent group or empty
        TreeNode          : TTreeNode;  // the node in the treeview // do not set it free
        ParentNode        : TTreeNode;  // Parent node or nil
        ///  needed to add subgroups
        ///-----------------------

        Panel             : TPanel;

        Name              : string;
        Caption           : TCaption;
        Hint              : string;

        ImageIndex        : integer;
        ImageIndexSelected: integer;
        ImageIndexExpanded: integer;
        ImageLeft         : TPngImage;
        ImageTop          : TPngImage;

        Images            : TImageList;
        Settings          : TMySettings;
      end;
      PMySettingsGroup    = ^TMySettingsGroup;
      TMySettingsGroups   = Array of TMySettingsGroup;


    var
      FPanelLeft        : TPanel;
      FImageLeft        : TImage;
      FPanelTop         : TPanel;
      FImageTop         : TImage;
      FPanelSettings    : TPanel;
      FTreeView         : TTreeView;

      FImageList        : TImageList;

      FButtonOk         : TButton;
      FButtonCancel     : TButton;


      FLastIndex        : integer;
      FSettingsGroups: TMySettingsGroups;


    { Private declarations }


    procedure   MakePanel (var Panel : TPanel);
    procedure   MakeImage (var Image : TImage; const Parent: TWinControl);
    procedure   MakeButton(var Button: TButton);

    // groups
    function    GetGroupCount                                 : integer;
    function    GetGroupIndexByName (Name: string)            : integer;


    function    AddGroupItemToTreeView(var aGroup: TMySettingsGroup): TTreeNode;


    // group items


    procedure   CheckTreeViewItems;

  public
    { Public declarations }
    constructor Create(AOwner: TComponent);   override;
    destructor  Destroy;                      override;

    procedure   GotResized(Sender: TObject);

    // groups
    function    AddSettingsGroup(const aName, aCaption, aHint: stringconst aParentName: string = ''const aIcon: TFileName = ''const aIconSelected: TFileName = ''const aIconExpanded: TFileName = ''const aPngImageLeft: TFileName = ''const aPngImageTop: TFileName = ''): boolean;
    function    RmvSettingsGroup(const aName: string): boolean;

    procedure   SetParentChild(const aChildName: stringconst aParentName: string = ''); // sets parent child relation, leave parent empty for root node
    procedure   SelectGroup(const aName: string);

    property    GroupCount                    : integer read GetGroupCount;

    procedure   UpdateTreeView(Sender: TObject);

    // group items

    procedure   UpdateGroupPanel(Sender: TObject);
    procedure   UpdateGroupPanelOnKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
  end;



   /////////////////////////////////////////////////////////////////////////////
implementation
   /////////////////////////////////////////////////////////////////////////////


  /// ◙ MakePanel
  procedure TNewSettingsFormBaseClass.MakePanel(var Panel: TPanel); {$IfDef VER170} inline{$EndIf}
  begin
    Panel             := TPanel.Create(Self);
    Panel.Caption     := '';
    Panel.Parent      := Self;
    Panel.BevelOuter  := bvLowered;
  end;

  /// ◙ MakeImage
  procedure TNewSettingsFormBaseClass.MakeImage(var Image: TImage; const Parent: TWinControl); {$IfDef VER170} inline{$EndIf}
  begin
    Image             := TImage.Create(Self);
    Image.Parent      := Parent;
    Image.Align       := alClient;
  end;

  /// ◙ MakeButton
  procedure TNewSettingsFormBaseClass.MakeButton(var Button: TButton); {$IfDef VER170} inline{$EndIf}
  begin
    Button            := TButton.Create(Self);
    Button.Caption    := '';
    Button.Parent     := Self;
    Button.Width      := 128;
    Button.Height     := 32;
  end;

  /// ◙ AddGroupItemToTreeView
  function TNewSettingsFormBaseClass.AddGroupItemToTreeView(var aGroup: TMySettingsGroup): TTreeNode; {$IfDef VER170} inline{$EndIf}
  begin
    result:= FTreeView.Items.AddChild(aGroup.ParentNode, aGroup.Caption);

    result.ImageIndex           := aGroup.ImageIndex;
    result.SelectedIndex        := aGroup.ImageIndexSelected;
    result.ExpandedImageIndex   := aGroup.ImageIndexExpanded;

    result.Data                 := @aGroup;

    aGroup.ParentNode           := result.Parent;
    aGroup.TreeNode             := result;

    if aGroup.ParentNode <> nil then
    begin
      aGroup.ParentName:= PMySettingsGroup(aGroup.ParentNode.Data)^.Name;
    end;

  end;

  procedure TNewSettingsFormBaseClass.CheckTreeViewItems;
  var i, j: integer; b: boolean;
  begin
      // DEBUG:
      //  check if all treenode.data props are set correctly
      for i := 0 to FTreeView.Items.Count - 1 do
      begin

        b:= false;
        for j := 0 to GroupCount - 1 do
          if FTreeView.Items.Item[i].Data = @FSettingsGroups[j] then
          begin
            b:= true;
            break;
          end;

        if not b then
          raise Exception.Create('WTF');// This should NEVER happen!!!!

      end;

  end;


   /////////////////////////////////////////////////////////////////////////////
  ///
 ///  ◙  TNewSettingsFormBaseClass.Create
///
constructor TNewSettingsFormBaseClass.Create(AOwner: TComponent);
begin
  inherited;

  MakePanel(FPanelLeft);
  MakeImage(FImageLeft, FPanelLeft);


  MakePanel(FPanelTop);
    FPanelTop.Alignment   := taLeftJustify;
    FPanelTop.Color       := clSkyBlue;
    FPanelTop.Font.Size   := 14;
    FPanelTop.Font.Style  := [fsBold];

  MakeImage(FImageTop, FPanelLeft);

  FImageList              := TImageList.Create(Self);

  FTreeView               := TTreeView.Create(Self);
  FTreeView.Parent        := Self;
  FTreeView.ReadOnly      := true;
  FTreeView.Images        := FImageList;
  //FTreeView.Font.Size     := 10;
  FTreeView.Font.Style    := [fsBold];
  FTreeView.OnClick       := UpdateGroupPanel;
  FTreeView.OnKeyUp       := UpdateGroupPanelOnKeyUp;


  FPanelSettings:= nil;


  MakeButton(FButtonOk);
  MakeButton(FButtonCancel);


  OnResize                := GotResized;
end;

   /////////////////////////////////////////////////////////////////////////////
  ///
 ///  ◙  TNewSettingsFormBaseClass.Destroy
///
destructor TNewSettingsFormBaseClass.Destroy;
begin

  inherited;
end;

   /////////////////////////////////////////////////////////////////////////////
  ///
 ///  ◙  TNewSettingsFormBaseClass.GetGroupCount
///
function TNewSettingsFormBaseClass.GetGroupCount: integer;
begin
  result:= Length(FSettingsGroups);
end;

   /////////////////////////////////////////////////////////////////////////////
  ///
 ///  ◙  TNewSettingsFormBaseClass.GetGroupIndexByName
///
function TNewSettingsFormBaseClass.GetGroupIndexByName(Name: string): integer;
var i, j: integer; s: string;
begin
  result:= -1;
  s     := UpperCase(Name);

  // start at FLastIndex, probably faster in most cases but not necessarily
  j:= 0;
  i:= FLastIndex;
  if i < 0 then
    i:= 0;

  while j < GroupCount do
  begin
    if UpperCase(FSettingsGroups[i].Name) = s then
    begin
      FLastIndex  := i;
      result      := i;
      break;
    end;

    Inc(i);
    Inc(j);

    if i = GroupCount then
      i:= 0;
  end;

end;

   /////////////////////////////////////////////////////////////////////////////
  ///
 ///  ◙  TNewSettingsFormBaseClass.GotResized
///
procedure TNewSettingsFormBaseClass.GotResized(Sender: TObject);
const cFrameSpace =   8// space around the form
      cSpace      =   4// space between components
      cMinWidth   = 720// min size X ◄ ►
      cMinHeight  = 410// min size Y ▲ ▼
      cHeader     =  24// windows header
begin
  if Width < cMinWidth then
    Width:= cMinWidth;
  if Height < cMinHeight then
    Height:= cMinHeight;

  FPanelLeft.Left      := cFrameSpace;
  FPanelLeft.Top       := cFrameSpace;
  FPanelLeft.Width     := 32;
  FPanelLeft.Height    := Height - (cFrameSpace shl 1) - cHeader;

  FTreeView.Left       := FPanelLeft.Left + FPanelLeft.Width + cSpace;
  FTreeView.Top        := cFrameSpace;
  FTreeView.Width      := 192;
  FTreeView.Height     := FPanelLeft.Height;

  FPanelTop.Left       := FTreeView.Left + FTreeView.Width   + cSpace;
  FPanelTop.Top        := cFrameSpace;
  FPanelTop.Width      := Width - FPanelTop.Left - cSpace - cFrameSpace;
  FPanelTop.Height     := FPanelLeft.Width;

  if FPanelSettings <> nil then
  begin
    FPanelSettings.Left  := FTreeView.Left + FTreeView.Width   + cSpace;
    FPanelSettings.Top   := FPanelTop.Top  + FPanelTop.Height  + cSpace;
    FPanelSettings.Width := Width - FPanelSettings.Left - cSpace - cFrameSpace;
    FPanelSettings.Height:= Height - FPanelSettings.Top - cFrameSpace - FButtonOk.Height - cSpace - cHeader;
  end;

  FButtonCancel.Top    := Height - FButtonCancel.Height - cSpace - cHeader;
  FButtonCancel.Left   := Width  - FButtonOk.Width      - cSpace - cFrameSpace;

  FButtonOk.Top        := FButtonCancel.Top;
  FButtonOk.Left       := FButtonCancel.Left - cSpace - FButtonCancel.Width;
end;

   /////////////////////////////////////////////////////////////////////////////
  ///
 ///  ◙  TNewSettingsFormBaseClass.AddSettingsGroup
///
function TNewSettingsFormBaseClass.AddSettingsGroup(const aName, aCaption, aHint: stringconst aParentName: string = ''const aIcon: TFileName = ''const aIconSelected: TFileName = ''const aIconExpanded: TFileName = ''const aPngImageLeft: TFileName = ''const aPngImageTop: TFileName = ''): boolean;

  function LoadIcon(const aIconFileName: stringvar ImageIndex: integer; const DefaultImageIndex: integer = -1): boolean;
  var ico: TIcon;
  begin
    result:= FileExists(aIconFileName);
    if result then
    begin
      ico:= TIcon.Create;
      ico.LoadFromFile(aIconFileName);
      ImageIndex:= FImageList.AddIcon(ico);
    end else
      ImageIndex:= DefaultImageIndex;
  end;

var i, j: integer;
begin
  result:= false;
  i:= GetGroupIndexByName(aName);
  if i > -1 then
    exit; // name already exists

  i:= GroupCount;
  SetLength(FSettingsGroups, i +1);
  FLastIndex:= i;

//  with FSettingsGroups[i] do

/// BUG: !!!here is a bug somewhere!!!
///  if UpdateTreeView is used after adding items it works, but why???

  //begin
    // make panel
    MakePanel(FSettingsGroups[i].Panel);
    FSettingsGroups[i].Panel.Visible:= false;

    // set strings
    FSettingsGroups[i].Name    := aName;
    FSettingsGroups[i].Caption := aCaption;
    FSettingsGroups[i].Hint    := aHint;

    // load images
    LoadIcon(aIcon        , FSettingsGroups[i].ImageIndex);
    LoadIcon(aIconSelected, FSettingsGroups[i].ImageIndexSelected, FSettingsGroups[i].ImageIndex);
    LoadIcon(aIconExpanded, FSettingsGroups[i].ImageIndexExpanded, FSettingsGroups[i].ImageIndex);


    if FileExists(aPngImageLeft) then
    begin
      FSettingsGroups[i].ImageLeft:= TPngImage.Create;
      FSettingsGroups[i].ImageLeft.LoadFromFile(aPngImageLeft);
    end else
      FSettingsGroups[i].ImageLeft:= nil;

    if FileExists(aPngImageTop) then
    begin
      FSettingsGroups[i].ImageTop:= TPngImage.Create;
      FSettingsGroups[i].ImageTop.LoadFromFile(aPngImageTop);
    end else
      FSettingsGroups[i].ImageTop:= nil;


    FSettingsGroups[i].Images:= TImageList.Create(Self);
    SetLength(FSettingsGroups[i].Settings, 0);

    // parent child
    FSettingsGroups[i].ParentName  := aParentName;
    FSettingsGroups[i].ParentNode  := nil;

    // get parent node
    if FSettingsGroups[i].ParentName <> '' then
    begin
      j:= GetGroupIndexByName(FSettingsGroups[i].ParentName);
      if j > -1 then  // must exist
        FSettingsGroups[i].ParentNode:= FSettingsGroups[j].TreeNode;

    end;

    // add TreeNode
    FSettingsGroups[i].TreeNode    := AddGroupItemToTreeView(FSettingsGroups[i]);

  //end;

  //UpdateTreeView(Self);

  result:= FSettingsGroups[i].TreeNode <> nil;

  CheckTreeViewItems; /// does not work :( (but works with "UpdateTreeView(Self);")
end;

   /////////////////////////////////////////////////////////////////////////////
  ///
 ///  ◙  TNewSettingsFormBaseClass.RmvSettingsGroup
///
function TNewSettingsFormBaseClass.RmvSettingsGroup(const aName: string): boolean;
var i, j: integer;
begin
  // check
  result:= false;
  i:= GetGroupIndexByName(aName);
  if i < 0 then
    exit;

  // free candy
  with FSettingsGroups[i] do
  begin
    Panel.Free;
    ImageLeft.Free;
    ImageTop.Free;
    Images.Free;
    SetLength(Settings, 0);

    //kill the children
    for j := 0 to GroupCount - 1 do
      if UpperCase(FSettingsGroups[j].ParentName) = UpperCase(aName) then
        RmvSettingsGroup(FSettingsGroups[j].Name);

    // remove evidence
    FTreeView.Items.Delete(TreeNode);
  end;

  // shl
  for i := i to GroupCount - 2 do
    FSettingsGroups[i]:= FSettingsGroups[i +1];

  // reduce
  SetLength(FSettingsGroups, Length(FSettingsGroups) -1);

  result:= true;
end;

   /////////////////////////////////////////////////////////////////////////////
  ///
 ///  ◙  TNewSettingsFormBaseClass.SetParentChild
///
procedure TNewSettingsFormBaseClass.SetParentChild(const aChildName: stringconst aParentName: string = '');
var i, j: integer;
begin
  i:= GetGroupIndexByName(aChildName);
  if i < 0 then
    exit;


  FSettingsGroups[i].ParentName:= aParentName;


  if aParentName = '' then
  begin
    // remove parent
    FSettingsGroups[i].ParentNode := nil;

  end else begin
    // set new parent
    j:= GetGroupIndexByName(aParentName);

    // parent must exist
    if j < 0 then
      exit;

    // update treeview and record
    FSettingsGroups[i].ParentNode:= FSettingsGroups[j].TreeNode;

    FTreeView.Items.Delete(FSettingsGroups[i].TreeNode);

    FSettingsGroups[i].TreeNode:= AddGroupItemToTreeView(FSettingsGroups[i]);

  end;

end;

   /////////////////////////////////////////////////////////////////////////////
  ///
 ///  ◙  TNewSettingsFormBaseClass.SelectGroup
///
procedure TNewSettingsFormBaseClass.SelectGroup(const aName: string);
var i: integer;
begin
  i:= GetGroupIndexByName(aName);
  if i < 0 then
    exit;

  FTreeView.Selected:= FSettingsGroups[i].TreeNode;
end;

   /////////////////////////////////////////////////////////////////////////////
  ///
 ///  ◙  TNewSettingsFormBaseClass.UpdateTreeView
///
procedure TNewSettingsFormBaseClass.UpdateTreeView(Sender: TObject);
var i: integer;
begin
  FTreeView.Items.BeginUpdate;
  FTreeView.Items.Clear;

  for i := 0 to GroupCount - 1 do
  begin
    AddGroupItemToTreeView(FSettingsGroups[i]);
  end;

  FTreeView.Items.EndUpdate;

  CheckTreeViewItems; /// works :)
end;

   /////////////////////////////////////////////////////////////////////////////
  ///
 ///  ◙  TNewSettingsFormBaseClass.UpdateGroupPanel
///
procedure TNewSettingsFormBaseClass.UpdateGroupPanel(Sender: TObject);
var tn: TTreeNode; sg: TMySettingsGroup;
begin
  tn:= FTreeView.Selected;
  if tn = nil then
    exit;

  sg:= PMySettingsGroup(tn.Data)^;

  // hide old panel
  if FPanelSettings <> nil then
    FPanelSettings.Visible:= false;

  // set new panel
  FPanelSettings    := PMySettingsGroup(tn.Data)^.Panel;
  FPanelTop.Caption := ' ' + PMySettingsGroup(tn.Data)^.Caption;
  if FPanelSettings <> nil then
  begin
    GotResized(Sender);
    FPanelSettings.Visible:= true;
  end;

end;

procedure TNewSettingsFormBaseClass.UpdateGroupPanelOnKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  UpdateGroupPanel(Sender);
end;

end.