mvctypes.pas

来自「本系统前端界面采用WINDOWS 窗口风格」· PAS 代码 · 共 569 行 · 第 1/2 页

PAS
569
字号
unit MVCTypes;

{ (c) 2000 Marian Aldenh鰒el
           Hainstra遝 8
           53121 Bonn
           +49 228 6203366
           Fax: +49 228 624031
           marian@mba-software.de

  Free: You may use this code in every way you find it useful or fun.

  Say you have a hierarchical (a flat list is a special case of such
  a hierarchy :-)) structure in memory or a file. Now you need to
  visualize that structure or give the user the ability to edit it.

  Traditionally you have two options:

  1) Store the hierarchy in a TTreeView and should you need more
     data than a simple hierarchy store pointers to that data in the
     Data-property of the TTreeNode.

     This is an indecent mix of structure and visualization. The minute
     you need to access and process that structure whithout displaying
     it you either need to duplicate it or find yourself using
     invisible TTreeView - so called TTreeViewNotViews :-).

  2) Fill a TreeView with your Data and take very good care to
     propagate each and every change and edit from the TTreeView to
     your Data.

     This is a real pain in many parts of the body.

  Using Mike Lischkes fantastic virtual tree you can use a more modern
  approach:

  Make a descendant of the Treeview that "knows" about your structure.
  The Tree can be linked to your data and will now automatically
  follow changes and vice-versa. I call this the "assign-and-forget"-
  Method.

  This demo ist a simplified version of a component I use in one of my
  current projects. Read the code to find out about it.

  Note that in this demo the definition and implementation of both
  Structure and Tree are kept in a single unit. This is just for
  ease of reading and distribution - in a real-world project it is
  a good idea to keep them separate even if it means exposing fields
  of the Nodes you would not need to expose otherwise. That way it
  is easier to replace the visualization or change the implementation
  of the structure without compromising the system.

  }


interface

uses Windows,Messages,SysUtils,Graphics,VirtualTrees,Classes,StdCtrls,
     Controls,Forms,ImgList;

type { TMVCNode is the encapsulation of a single Node in the structure.
       This implementation is a bit bloated because in my project
       everything is in memory at all times.
       In such an implementation there is not much "virtual" about the
       tree anymore - still it's of incredible usefulness as you will
       see. }
     TMVCNode=class(TObject)
       private
         { Here's the data associated with a single Node in the
           tree. This structure defines a caption and a subcaption, add
           whatever defines your data completely. }
         FParent:TMVCNode;
         FChildren:TList;
         FCheckState:TCheckState;
         FCaption,FSubCaption:string;
         { The FIncidence-Field is special in the way that it's
           value is never displayed directly but used to
           graphically alter the node's display. In my project it
           is a "weight" showing the number of hits from a
           database. Here it is displayed next to the caption
           as a colored speck. }
         FIncidence:integer;

        { Here's where we start to think of visualization. This
          field points to the VirtualNode in a virtual tree that
          is associated with the Node - if there is one,
          otherwise it is NIL, there may be no virtual node
          allocated for the TMVCNode or no tree linked. }
         FVirtualNode:PVirtualNode;

         { Here are reader/writer-methods for the properties that
           define our data. We need set-properties because we want
           to update linked nodes directly. }
         procedure SetCaption(aCaption:string);
         procedure SetSubCaption(aSubCaption:string);
         procedure SetCheckState(aCheckState:TCheckState);
         procedure SetIncidence(aValue:integer);
         function GetChildCount:integer;
         function GetChild(n:integer):TMVCNode;
       public
         constructor Create;
         destructor Destroy; override;

         { Take a look at our data and pick an icon from the
           Imagelist to be displayed in the tree. }
         function GetImageIndex:integer; virtual;
         { Tell the tree to invalidate the node it displayes the
           information for this Node in. It will be repainted
           next }
         procedure InvalidateVirtualNode;

         { properties exposing our internal data to the world.
           set-methods are given so the Node can always invalidate
           its node. }
         property CheckState:TCheckState read FCheckState write SetCheckState;
         property Caption:string read FCaption write SetCaption;
         property SubCaption:string read FSubCaption write SetSubCaption;
         property Incidence:integer read FIncidence write SetIncidence;

         property Parent:TMVCNode read FParent;
         property ChildCount:integer read GetChildCount;
         property Child[n:integer]:TMVCNode read GetChild;
         function CreateChild:TMVCNode;
         procedure RemoveChild(n:integer);
         procedure DestroyChild(n:integer);

         { This field is only exposed because I advise you to put
           the Tree-Code in a separate unit and then you won't get
           far privatizing it. Allowing public write-access to it
           is a bit hairy though, you should never write to the
           field outside of this or the Tree-unit.
           This is where a friend-declaration is missing from OP}
         property VirtualNode:PVirtualNode read FVirtualNode write FVirtualNode;
       end;

     { TMVCTree keeps the TMVCNodes. It also maintains the link to a
       virtual treeview. }
     TMVCTree=class
       private
         { This is the Root of the Tree. In this Demo that Root is there
           purely to hold the structure together, it is never displayed -
           just like TVirtualTrees own Root. }
         FRoot:TMVCNode;

         { The Viewer-Field points to any component or object
           used to visualize or edit this structure. It is
           not really used in this demo, in a real application
           you will find situations where you have to find out
           whether you are linked and if so where to.

           Why is the Viewer declared as TObject and not as
           the specific class? Two reasons:

           1) The viewer should be implemented in a different
              unit, either there or here you will have to
              cast or you will build a circular reference. I
              choose to cast here because

           2) You may want to have _different_ viewers for
              the same structure. Keeping that in mind you
              may even want to change the declaration to
              a list of linked viewers... }
         FSettingViewer:integer;
         FViewer:TObject;

         { Access-methods to expose the list in a type safe
           way. }

         { A set-method that updates the link to a viewer. }
         procedure SetViewer(aViewer:TObject);

       public
         constructor Create;
         destructor Destroy; override;

         property Root:TMVCNode read FRoot;

         { Assign to this to create or break the link with
           a viewer. If you are about to add, remove or edit
           a zillion Nodes you can call BeginUpdate and
           EndUpdate. In this demo they just do the same for
           any assigned viewer - Caution: The demo does not
           make provisions for the case where you call
           BeginUpdate and then switch to another viewer! }
         property Viewer:TObject read FViewer write SetViewer;
         procedure BeginUpdate;
         procedure EndUpdate;
       end;

     { Here's the Viewer. I have descended from the base class to maximize
       the functionality that is moved to our code, should you be happy
       with any of the predeclared descendants use of them. }
     TMVCEditLink=class;
     TMVCTreeView=class(TBaseVirtualTree)
       private
         { This is a pointer to the structure associated with
           this viewer. }
         FTree:TMVCTree;
         FInternalDataOffset: Cardinal;               // offset to the internal data

         { Make and break the link with a list }
         procedure SetTree(aTree:TMVCTree);

         { These are for direct access to our structure
           through the viewer. You can use them to find
           the TMVCNode that corresponds to a selected
           VirtualNode for instance. }
         function GetMVCNode(VirtualNode:PVirtualNode):TMVCNode;
         procedure SetMVCNode(VirtualNode:PVirtualNode; aNode:TMVCNode);

          function GetOptions: TVirtualTreeOptions;
          procedure SetOptions(const Value: TVirtualTreeOptions);
       protected
         { Overridden methods of the tree, see their implementation for
           details on what they do and why they are overridden. }
         function DoGetNodeWidth(Node: PVirtualNode; Column: TColumnIndex; Canvas: TCanvas = nil): Integer; override;
         procedure DoPaintNode(var PaintInfo: TVTPaintInfo); override;
         procedure DoInitChildren(Node:PVirtualNode;var ChildCount:Cardinal); override;
         procedure DoInitNode(aParent,aNode:PVirtualNode;
                              var aInitStates:TVirtualNodeInitStates); override;
         procedure DoFreeNode(aNode:PVirtualNode); override;
         procedure DoGetImageIndex(Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
           var Ghosted: Boolean; var Index: Integer); override;
         procedure DoChecked(aNode:PVirtualNode); override;
         function DoCreateEditor(Node: PVirtualNode; Column: TColumnIndex): IVTEditLink; override;
         function InternalData(Node: PVirtualNode): Pointer;
         function InternalDataSize: Cardinal;

         function GetOptionsClass: TTreeOptionsClass; override;
       public
         constructor Create(AOwner: TComponent); override;
         destructor Destroy; override;

         { Properties for the link to a list and the individual Node.
           these form the interface to the application. See the main form
           to check it out. }
         property Tree:TMVCTree read FTree write SetTree;
         property MVCNode[VirtualNode:PVirtualNode]:TMVCNode read GetMVCNode;

         function GetNodeText(aNode:TMVCNode;
                              aColumn:integer):string;
         procedure SetNodeText(aNode:TMVCNode;
                               aColumn:integer;
                               aText:string);
       published
         { We descend from the base class, publish whatever you want to.
           The demo only needs the Header, it is initialized in the fixed
           panel-code. }
         property TreeOptions: TVirtualTreeOptions read GetOptions write SetOptions;
         property Header;
         property Images;
         property OnChange;
       end;

   TMVCEdit=class(TCustomEdit)
              private
                FLink:TMVCEditLink;
                procedure WMChar(var Message: TWMChar); message WM_CHAR;
                procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
                procedure WMKillFocus(var Msg: TWMKillFocus); message WM_KILLFOCUS;
              protected
                procedure AutoAdjustSize;
                procedure CreateParams(var Params:TCreateParams); override;
              public
                constructor Create(Link:TMVCEditLink); reintroduce;
              end;

   TMVCEditLink=class(TInterfacedObject,IVTEditLink)
                   private
                     FEdit:TMVCEdit;                    // a normal custom edit control
                     FTree:TMVCTreeView;               // a back reference to the tree calling
                     FNode:PVirtualNode;               // the node to be edited
                     FColumn:Integer;                  // the column of the node
                   public
                     constructor Create;
                     destructor Destroy; override;

                     function BeginEdit: Boolean; stdcall;
                     function CancelEdit: Boolean; stdcall;
                     function EndEdit: Boolean; stdcall;
                     function GetBounds: TRect; stdcall;
                     function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
                     procedure ProcessMessage(var Message: TMessage); stdcall;
                     procedure SetBounds(R:TRect); stdcall;

                     property Tree:TMVCTreeView read FTree;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?