TreeView - компонент для показа dataset в виде дерева с сохранением
Автор: Валентин
WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> TreeView - компонент для показа dataset в виде дерева с сохранением
Цель создания: необходимость быстрого выбора товара из справочника в виде дерева.
Компонент для визуализации дерева из таблицы. привязка к полям не ведется.
Ключевое поле находится в node.stateindex.
Использует 4 иконки для узлов и позиций, где 0-невыбранный узел,
1- выбранный узел, 2- невыбранный пункт, 3- выбранный пункт.
Необходимо выбрать datasource. вписать id, parentid.
Заполнение методом MRRefresh.
Сохранение в файл методом
MRPSaveToFile(ProgPath+'NameTree.tree').
Загрузка из файла соответственно MRPLoadFromFile(ProgPath+'NameTree.tree').
Кроме того поддерживаются метода последовательно поиска в обоих направлениях.
Зависимости: Windows, Messages, SysUtils, Classes, Controls, ComCtrls,DB,DBCtrls
Автор: Валентин, visor123@ukr.net, Днепропетровск
Copyright: Собственная разработка.
Дата: 9 апреля 2003 г.
***************************************************** }
unit GRTreeView;
interface
uses
Windows, Messages, SysUtils, Classes, Controls, ComCtrls, DB, DBCtrls,
Dialogs;
type
TMRGroupRec = record
ID, MasterID, Level: integer;
MainName: string;
end;
TMRGroup = class(TPersistent)
private
fCount: integer;
protected
procedure SetCount(value: integer);
public
items: array of TMRGroupRec;
property Count: integer read fCount write SetCount;
constructor Create;
destructor destroy; override;
procedure Clear;
procedure Add(AID, AMasterID: integer; AMainName: string);
function GetIndexByMasterID(AMasterID: integer): integer;
end;
TGRTreeView = class(TTreeView)
private
{ Private declarations }
fDataSource: TDataLink;
fFeyField: TFieldDataLink;
fMasterFeyField: TFieldDataLink;
fNameField: TFieldDataLink;
// fRootName:string;
fSeparator: Char;
fLock: Boolean;
fSearchIndex: integer;
function GetBufStart(Buffer: PChar; var Level: Integer): PChar;
protected
{ Protected declarations }
function GetDataSource: TDataSource;
procedure SetDataSource(value: TDataSource);
function GetKeyField: string;
procedure SetKeyField(value: string);
function GetMasterKeyField: string;
procedure SetMasterKeyField(value: string);
function GetNameField: string;
procedure SetNameField(value: string);
procedure SetSeparator(value: char);
procedure GetImageIndex(Node: TTreeNode); override;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor destroy; override;
function MRRefresh: Boolean;
procedure MRPLoadFromFile(const FileName: string); overload;
procedure MRPLoadFromFile(const FileName: string; RootName: string);
overload;
procedure MRPLoadFromStream(Stream: TStream);
procedure MRPSaveToFile(const FileName: string);
procedure MRPSaveToStream(Stream: TStream);
function MRGetIndexByText(AText: string): integer;
function MRGetIndexByMasterID(MasterID: integer): integer;
function MRGetIndexByMasterIDRecurse(MasterID: integer): integer;
function MRSearchByText(AText: string; Next: Boolean = True; UseSearchIndex:
Boolean = false): integer;
published
{ Published declarations }
property Separator: char read fSeparator write SetSeparator;
property DataSource: TDataSource read GetDataSource write SetDataSource;
property KeyField: string read GetKeyField write SetKeyField;
property MasterField: string read GetMasterKeyField write SetMasterKeyField;
property NameField: string read GetNameField write SetNameField;
end;
procedure Register;
implementation
//var
// MGRGroup:array of TMRGroup;
procedure Register;
begin
RegisterComponents('Visor', [TGRTreeView]);
end;
{ TGRTreeView }
constructor TGRTreeView.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fDataSource := TDataLink.Create;
fFeyField := TFieldDataLink.Create;
fFeyField.Control := self;
fMasterFeyField := TFieldDataLink.Create;
fMasterFeyField.Control := self;
fNameField := TFieldDataLink.Create;
fNameField.Control := self;
fSeparator := '^';
fLock := false;
HideSelection := false;
fSearchIndex := -1;
end;
destructor TGRTreeView.destroy;
begin
fNameField.Free;
fNameField := nil;
fFeyField.Free;
fFeyField := nil;
fDataSource.Free;
fDataSource := nil;
inherited;
end;
function TGRTreeView.GetBufStart(Buffer: PChar; var Level: Integer): PChar;
begin
Level := 0;
while Buffer^ in [' ', #9] do
begin
Inc(Buffer);
Inc(Level);
end;
Result := Buffer;
end;
function TGRTreeView.GetDataSource: TDataSource;
begin
Result := fDataSource.DataSource;
end;
procedure TGRTreeView.MRPLoadFromFile(const FileName: string);
var
Stream: TStream;
FNT, FNR, Ex: string;
begin
if not FileExists(FileName) then
Exit;
Ex := ExtractFileExt(FileName);
if Ex = '' then
begin
FNT := ExtractFileName(FileName) + '.tree';
FNR := ExtractFileName(FileName) + '.ini';
end
else
begin
FNT := ExtractFileName(FileName);
FNT := Copy(FNT, 0, pos('.', FNT) - 1);
FNR := FNT + '.ini';
FNT := FNT + '.tree';
end;
FNT := ExtractFilePath(FileName) + FNT;
FNR := ExtractFilePath(FileName) + FNR;
Stream := TFileStream.Create(FNT, fmOpenRead);
try
MRPLoadFromStream(Stream);
finally
Stream.Free;
end;
end;
function TGRTreeView.MRGetIndexByText(AText: string): integer;
var
i: integer;
begin
if Items.Count = 0 then
begin
Result := -1;
Exit;
end;
for i := 0 to Items.Count - 1 do
begin
if Items.Item[i].Text = AText then
begin
Result := i;
Exit;
end;
end;
Result := -1;
end;
procedure TGRTreeView.MRPLoadFromFile(const FileName: string;
RootName: string);
var
FNT, FNR, Ex: string;
ANode: TTreeNode;
begin
if not FileExists(FileName) then
Exit;
Ex := ExtractFileExt(FileName);
if Ex = '' then
begin
FNT := ExtractFileName(FileName) + '.tree';
FNR := ExtractFileName(FileName) + '.ini';
end
else
begin
FNT := ExtractFileName(FileName);
FNT := Copy(FNT, 0, pos('.', FNT) - 1);
FNR := FNT + '.ini';
FNT := FNT + '.tree';
end;
FNT := ExtractFilePath(FileName) + FNT;
FNR := ExtractFilePath(FileName) + FNR;
if (not FileExists(FNT)) or (not FileExists(FNR)) then
begin
ANode := Items.Add(nil, RootName);
ANode.StateIndex := 0;
Self.MRPSaveToFile(FileName);
end
else
begin
MRPLoadFromFile(FileName);
end;
end;
procedure TGRTreeView.MRPLoadFromStream(Stream: TStream);
var
List: TStringList;
ANode, NextNode: TTreeNode;
ALevel, i, AStateIndex: Integer;
CurrStr, Buff: string;
begin
Items.Clear;
List := TStringList.Create;
Items.BeginUpdate;
try
try
List.Clear;
List.LoadFromStream(Stream);
ANode := nil;
for i := 0 to List.Count - 1 do
begin
CurrStr := GetBufStart(PChar(List[i]), ALevel);
AStateIndex := -1;
if pos(fSeparator, CurrStr) > 0 then
begin
Buff := Copy(CurrStr, pos(fSeparator, CurrStr) + 1, length(CurrStr) -
pos(fSeparator, CurrStr));
if Buff <> '' then
AStateIndex := StrToInt(Buff);
// Delete(CurrStr,pos(CurrStr,fSeparator),length(CurrStr)-pos(CurrStr,fSeparator)-1);
buff := Copy(CurrStr, 0, pos(fSeparator, CurrStr) - 1);
CurrStr := Buff;
end;
if ANode = nil then
begin
ANode := Items.AddChild(nil, CurrStr);
if AStateIndex <> -1 then
ANode.StateIndex := AStateIndex;
end
else if ANode.Level = ALevel then
begin
ANode := Items.AddChild(ANode.Parent, CurrStr);
if AStateIndex <> -1 then
ANode.StateIndex := AStateIndex;
end
else if ANode.Level = (ALevel - 1) then
begin
ANode := Items.AddChild(ANode, CurrStr);
if AStateIndex <> -1 then
ANode.StateIndex := AStateIndex;
end
else if ANode.Level > ALevel then
begin
NextNode := ANode.Parent;
while NextNode.Level > ALevel do
NextNode := NextNode.Parent;
ANode := Items.AddChild(NextNode.Parent, CurrStr);
if AStateIndex <> -1 then
ANode.StateIndex := AStateIndex;
end;
// else TreeViewErrorFmt(sInvalidLevelEx, [ALevel, CurrStr]);
end;
finally
Items.EndUpdate;
List.Free;
end;
except
Items.Owner.Invalidate; // force repaint on exception see VCL
raise;
end;
if Items.Count > 0 then
Items.Item[0].Expand(false);
end;
procedure TGRTreeView.MRPSaveToFile(const FileName: string);
var
Stream: TStream;
FNT, FNR, Ex: string;
begin
Ex := ExtractFileExt(FileName);
if Ex = '' then
begin
FNT := ExtractFileName(FileName) + '.tree';
FNR := ExtractFileName(FileName) + '.ini';
end
else
begin
FNT := ExtractFileName(FileName);
FNT := Copy(FNT, 0, pos('.', FNT) - 1);
FNR := FNT + '.ini';
FNT := FNT + '.tree';
end;
FNT := ExtractFilePath(FileName) + FNT;
FNR := ExtractFilePath(FileName) + FNR;
Stream := TFileStream.Create(FNT, fmCreate);
try
flock := True;
MRPSaveToStream(Stream);
finally
Stream.Free;
flock := false;
end;
end;
procedure TGRTreeView.MRPSaveToStream(Stream: TStream);
const
TabChar = #9;
EndOfLine = #13#10;
var
i: Integer;
ANode: TTreeNode;
NodeStr: string;
begin
if Items.Count > 0 then
begin
ANode := Items.Item[0];
while ANode <> nil do
begin
NodeStr := '';
for i := 0 to ANode.Level - 1 do
NodeStr := NodeStr + TabChar;
NodeStr := NodeStr + ANode.Text + fSeparator + IntToStr(ANode.StateIndex)
+ EndOfLine;
Stream.Write(Pointer(NodeStr)^, Length(NodeStr));
ANode := ANode.GetNext;
end;
end;
end;
function TGRTreeView.MRRefresh: boolean;
var
i: integer;
ANode, NextNode: TTreeNode;
MGroup: TMRGroup;
begin
if (fDataSource.DataSet = nil) or (KeyField = '') or (MasterField = '') or
(NameField = '') then
begin
Result := false;
Exit;
end;
if not fDataSource.DataSet.Active then
fDataSource.DataSet.Open
else
begin
fDataSource.DataSet.Close;
fDataSource.DataSet.Open;
end;
fDataSource.DataSet.DisableControls;
MGroup := TMRGroup.Create;
MGroup.Clear;
try
while not fDataSource.DataSet.Eof do
begin
MGroup.Add(DataSource.DataSet.FieldByName(KeyField).AsInteger,
DataSource.DataSet.FieldByName(MasterField).AsInteger,
DataSource.DataSet.FieldByName(NameField).AsString);
fDataSource.DataSet.Next;
end;
items.Clear;
Items.BeginUpdate;
fLock := True;
ANode := nil;
for i := 0 to MGroup.Count - 1 do
begin
if ANode = nil then
begin
ANode := Items.AddChild(nil, MGroup.Items[i].MainName);
ANode.StateIndex := MGroup.items[i].ID;
end
else if ANode.Level = (MGroup.items[i].Level) then
begin
ANode := items.AddChild(ANode.Parent, MGroup.items[i].MainName);
ANode.StateIndex := MGroup.items[i].ID;
end
else if ANode.Level = (MGroup.items[i].Level - 1) then
begin
ANode := Items.AddChild(ANode, MGroup.items[i].MainName);
ANode.StateIndex := MGroup.items[i].ID;
end
else if ANode.Level > MGroup.items[i].Level then
begin
NextNode := ANode.Parent;
while NextNode.Level > MGroup.items[i].Level do
NextNode := NextNode.Parent;
ANode := Items.AddChild(NextNode.Parent, MGroup.items[i].MainName);
ANode.StateIndex := MGroup.items[i].ID;
end;
{ else if ANode.Level > MGroup.items[i].Level then
begin
NextNode := ANode.Parent;
while NextNode.Level > MGroup.items[i].Level do
NextNode := NextNode.Parent;
ANode := Items.AddChild(NextNode.Parent, MGroup.items[i].MainName);
ANode.StateIndex:=MGroup.items[i].ID;
end;}
end;
finally
fDataSource.DataSet.First;
fDataSource.DataSet.EnableControls;
//ShowMessage('Tree count='+IntToStr(Items.Count)+' MGroup count='+IntToStr(MGroup.Count));
MGroup.Free;
fLock := false;
end;
Items.EndUpdate;
if Items.Count > 0 then
Items.Item[0].Expand(false);
Result := True;
end;
procedure TGRTreeView.SetDataSource(value: TDataSource);
begin
fDataSource.DataSource := value;
end;
function TGRTreeView.MRGetIndexByMasterID(MasterID: integer): integer;
var
i: integer;
begin
if Items.Count = 0 then
begin
Result := -1;
exit;
end;
for i := 0 to Items.Count - 1 do
begin
if Items.Item[i].StateIndex = MasterID then
begin
Result := i;
Exit;
end;
end;
Result := -1;
end;
function TGRTreeView.GetKeyField: string;
begin
Result := fFeyField.FieldName;
end;
function TGRTreeView.GetMasterKeyField: string;
begin
Result := fMasterFeyField.FieldName;
end;
function TGRTreeView.GetNameField: string;
begin
Result := fNameField.FieldName;
end;
procedure TGRTreeView.SetKeyField(value: string);
begin
fFeyField.FieldName := value;
end;
procedure TGRTreeView.SetMasterKeyField(value: string);
begin
fMasterFeyField.FieldName := value;
end;
procedure TGRTreeView.SetNameField(value: string);
begin
fNameField.FieldName := value;
end;
procedure TGRTreeView.SetSeparator(value: char);
begin
fSeparator := value;
end;
procedure TGRTreeView.GetImageIndex(Node: TTreeNode);
begin
if fLock then
Exit;
inherited;
if Node.getFirstChild <> nil then
begin
Node.ImageIndex := 0;
Node.SelectedIndex := 1;
end
else
begin
Node.ImageIndex := 2;
Node.SelectedIndex := 3;
end;
end;
function TGRTreeView.MRGetIndexByMasterIDRecurse(
MasterID: integer): integer;
var
i: integer;
begin
if Items.Count = 0 then
begin
Result := -1;
exit;
end;
for i := Items.Count - 1 downto 0 do
begin
if Items.Item[i].StateIndex = MasterID then
begin
Result := i;
Exit;
end;
end;
Result := -1;
end;
function TGRTreeView.MRSearchByText(AText: string; Next: Boolean = True;
UseSearchIndex: Boolean = false): integer;
var
i, iStart, iEnd: integer;
sel: TList;
f: boolean;
begin
if Items.Count = 0 then
begin
Result := -1;
fSearchIndex := -1;
Exit;
end;
if Next then
begin
if (UseSearchIndex) and (fSearchIndex <> -1) then
iStart := fSearchIndex + 1
else
iStart := 0;
iEnd := Items.Count - 1;
end
else
begin
if (UseSearchIndex) and (fSearchIndex <> -1) then
iStart := fSearchIndex - 1
else
iStart := Items.Count - 1;
iEnd := 0;
end;
i := iStart;
f := true;
repeat
if pos(AnsiUpperCase(AText), AnsiUpperCase(Items.Item[i].Text)) > 0 then
begin
Result := i;
fSearchIndex := i;
sel := TList.Create;
sel.Add(Items.Item[i]);
Select(Sel);
sel.Free;
Exit;
end;
if Next then
begin
inc(i);
if i > iEnd then
f := false;
end
else
begin
dec(i);
if i < iEnd then
f := false;
end;
until f <> true;
Result := -1;
fSearchIndex := -1;
end;
{ TMRGroup }
procedure TMRGroup.Add(AID, AMasterID: integer; AMainName: string);
var
idx: integer;
begin
inc(fCount);
SetLength(items, fCount);
items[fCount - 1].ID := AID;
items[fCount - 1].MasterID := AMasterID;
items[fCount - 1].MainName := AMainName;
idx := GetIndexByMasterID(AMasterID);
if idx = -1 then
begin
items[idx].Level := 0;
end
else
begin
items[fCount - 1].Level := items[idx].Level + 1;
end;
end;
procedure TMRGroup.Clear;
begin
items := nil;
fCount := 0;
end;
constructor TMRGroup.Create;
begin
inherited;
fCount := 0;
end;
destructor TMRGroup.destroy;
begin
items := nil;
inherited;
end;
function TMRGroup.GetIndexByMasterID(AMasterID: integer): integer;
var
i: integer;
begin
if (fCount = 0) then
begin
Result := -1;
Exit;
end;
for i := 0 to fCount - 1 do
begin
if items[i].ID = AMasterID then
begin
Result := i;
Exit;
end;
end;
Result := -1;
end;
procedure TMRGroup.SetCount(value: integer);
begin
fCount := value;
end;
end.
|