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 + -
显示快捷键?