When you implement the namespace extension almost all code you
need to write is DataProvider events. DataProvider is the most important
component inside your NSE, it is a connecting layer between your
business logic and Windows Shell.
This article will describe how to use different data providers
and their principles of handing their common events. You can download
examples for this article by the following link: http://www.shellplus.com/examples/namespace-extension-example.html
How to use data providers
Three DataProviders are available now: TSxSimpleTreeProvider, TSxByHandProvider
and TSxDataSetProvider. To help in your choice we'll explain key
differences in using of each DataProvider:
TSxByHandProvider is the
most flexible and powerful data provider, but you need to write
a bit more code than with other providers ;-) Any of data storage
can be a source for your items and folders: remote database, hidden
folder on your hard disk, xml page on web server. Even some calendar
generated in runtime can be used for this purpose.
If you work with TSxByHandProvider you should implement OnPopulate
event to provide data from your custom data storage. It is a common
example:
procedure TSxPermanentModule1.ByHandProviderPopulate(
Sender: TSxByHandProvider; DataGrain: TSxByHandDataGrain;
GrainList: TList);
var
TempFolder:String;
SearchRec:TSearchRec;
TempGrain:TSxCustomDataGrain;
begin
if DataGrain.IsRoot then
TempFolder:='C:\'
else
begin
DataGrain.Data.Seek(0,0);
TempFolder:=DataGrain.SxData.ReadString;
end;
if FindFirst(IncludeTrailingBackSlash(TempFolder)+'*',faAnyFile,SearchRec)=0 then
try
repeat
if (SearchRec.Name='.') or (SearchRec.Name='..') then
Continue;
if (SearchRec.Attr and faDirectory)<>faDirectory then
Continue;
TempGrain:=Sender.CreateGrainInstance;
TempGrain.Name:=SearchRec.Name;
TempGrain.GrainType:=gtFolder;
TempGrain.SxData.WriteString(IncludeTrailingBackslash(TempFolder)+SearchRec.Name);
GrainList.Add(TempGrain);
until FindNext(SearchRec)<>0;
finally
SysUtils.FindClose(SearchRec);
end;
if FindFirst(IncludeTrailingBackSlash(TempFolder)+'*',faAnyFile,SearchRec)=0 then
try
repeat
if (SearchRec.Name='.') or (SearchRec.Name='..') then
Continue;
if (SearchRec.Attr and faDirectory)=faDirectory then
Continue;
TempGrain:=Sender.CreateGrainInstance;
TempGrain.Name:=SearchRec.Name;
TempGrain.GrainType:=gtItem;
TempGrain.SxData.WriteString(IncludeTrailingBackslash(TempFolder)+SearchRec.Name);
GrainList.Add(TempGrain);
until FindNext(SearchRec)<>0;
finally
SysUtils.FindClose(SearchRec);
end;
end;
|
As you see above, we provide a list of DataGrains that belongs
to the folder specified by DataGrain parameter.
First you check whether specified folder is a root of your namespace.
If not, you work with it in a way that was used to create it. It
is so because all data grains you work with inside of your NSE was
produced by the same event handler.
After that you compose a list of DataGrains (contents of required
folder) to the GrainList parameter. You should keep some unique
key (e.g. ID) inside the DataGrain to link it with your data inside
the custom storage. In our case it is just a full qualified path
to the file or folder.
TSxSimpleTreeProvider
is the most simple data provider of currently available providers.
It keeps a tree mapped namespace inside. You can specify its contents
in design time or in runtime. You can load and save tree data from/to
file or stream and does not need to implement OnPopulate event (it
does not have it).
|
Simple Tree Editor window (design time)
|
TSxDataSetProvider designed
to simplify work with DB-related data. E.g. you can put TClientDataSet,
TDataSource and any MIDAS provider on the SxPermanentModule and
link them using corresponding properties. As a result you get a
thin client for three-tier system.
TSxDataSetProvider have a few specific properties to setup the
data processing engine:
GrainSetup |
Contains settings to setup the
tree data processing engine |
RootKeyID |
Specifies an unique value, that
should be used to determine root of displayed tree. |
RootKeyID is a simple variant property, but GrainSetup property
requires more explanation:
FileClosedIconField |
Contains the name of field,
which value should be used as closed file icon*. |
FileClosedIconSubstitution |
Contains rules for transforming
field values to closed file icon indices. |
FileOpenIconField |
Contains the name of field,
which value should be used as open file icon*. |
FileOpenIconSubstitution |
Contains rules for transforming
field values to open file icon indices. |
FolderClosedIconField |
Contains the name of field,
which value should be used as closed folder icon. |
FolderClosedIconSubstitution |
Contains rules for transforming
field values to closed folder icon indices. |
FolderOpenIconField |
Contains the name of field,
which value should be used as open folder icon. |
FolderOpenIconSubstitution |
Contains rules for transforming
field values to open folder icon indices. |
GrainNameField |
Contains the name of field,
which value should be used as corresponding grain name. |
GrainTypeField |
Contains the name of field,
which value should be used as corresponding grain type. |
GrainTypeSubstitution |
Contains rules for transforming
field values to grain types. |
ParentKeyField |
Contains the name of field,
which values should be used as reference value to the parent
folder. |
PrimaryKeyField |
Contains the name of field,
which values should be used as primary key in database. |
The ParentKeyField should be a reference to PrimaryKeyField and
your data should have at least the following format:
PrimaryID
|
ParentID
|
GrainName
|
GrainType
|
1
|
0
|
Root Folder |
1
|
2
|
1
|
First Folder |
1
|
3
|
1
|
Second Folder
|
1
|
4
|
2
|
Item in FirstFolder |
2
|
5
|
3
|
Item in SecondFolder |
2
|
Handling of DataProvider common events
The rest of DataProvider events and properties related to clipboard
and drag'n'drop operations. This events are based on parent to all
data providers class and only difference - we will cast custom classes,
that we retrieve as event handler parameters, to classes specific
for our project. We'll use TSxSimpleProvider for examples.
There are two types of common data handing events:
In the Windows Shell data transfer scenarios (MSDN)
mentioned many different data formats, and all these formats are
supported by Shell+. Every event group has events for every data
format, so you can implement any or even all of them. E.g. if your
namespace extension is ready only (it should not accept files) you
can implement only "source" events, or if you namespace
extension should support drag'n'drop items only between folders
inside the same namespace, you can implement only private format
support events.
Target events group is a
set of events, that allows you accept files from drag'n'drop, copy/paste
operations and in several other cases.
OnDataGrainDrop event called
when user has dropped object on your NSE and source of this object
does support Private data format. Always the source is the same
NSE.
You receive dropped object representation (DataGrain parameter)
and representation of the folder, where new object was dropped
(FolderGrain parameter).
procedure TPermanentModule.SxSimpleTreeProviderDataGrainDrop(
Sender: TSxCustomProvider; FolderGrain, DataGrain: TSxCustomDataGrain);
var
PermanentFolder:TSxSimpleTreeDataGrain;
NewGrain:TSxSimpleTreeDataGrain;
begin
PermanentFolder:=TSxSimpleTreeProvider(Sender). LocatePermanentGrain(TSxSimpleTreeDataGrain(FolderGrain).GUID);
PermanentFolder.CreateNewChild(NewGrain);
PermanentFolder.Add(NewGrain);
NewGrain.Name:=DataGrain.Name;
NewGrain.GrainType:=DataGrain.GrainType;
SxSimpleTreeProvider.NotifyAddGrain(FolderGrain,NewGrain);
end;
|
OnNewGrainFromStream
event called when user has dropped object on your NSE and source
of this object does support CFSTR_FILECONTENTS or CFSTR_HDROP
data format or file was saved from Common Dialog to your NSE.
There are many applications allow support this data format.
You receive a folder, where new object was dropped (FolderGrain),
file information descriptor (FileInfo) and file data stream (FileData).
Just create a new data grain, whose parent is FolderGrain and
fill your storage with data for new grain.
procedure TPermanentModule.SxSimpleTreeProviderNewGrainFromStream(
Sender: TSxCustomProvider; FolderGrain: TSxCustomDataGrain;
FileInfo: TSxFileInfoDescriptor; FileData: TStream);
var
Temp:array [0..1000] of char;
Size:Integer;
PermanentFolder:TSxSimpleTreeDataGrain;
NewGrain:TSxSimpleTreeDataGrain;
begin
Size:=Min(1000,FileData.Size);
FillChar(Temp,1000,#0);
FileData.Read(Temp,Size);
PermanentFolder:=TSxSimpleTreeProvider(Sender). LocatePermanentGrain(TSxSimpleTreeDataGrain(FolderGrain).GUID);
PermanentFolder.CreateNewChild(NewGrain);
PermanentFolder.Add(NewGrain);
NewGrain.Name:=StrPas(Temp);
NewGrain.GrainType:=gtItem;
SxSimpleTreeProvider.NotifyAddGrain(FolderGrain,NewGrain);
end;
|
OnNewGrainFromFile event
called when user has dropped object on your NSE and source of
this object does support CFSTR_HDROP file format or file was saved
from Common Dialog to your NSE.
You receive the same FolderGrain and fully qualified path for
the source file. You will add new grain to your namespace and
fill internal data storage with new file contents.
procedure TPermanentModule.SxSimpleTreeProviderNewGrainFromFile(
Sender: TSxCustomProvider; FolderGrain: TSxCustomDataGrain;
FileName: WideString);
var
PermanentFolder:TSxSimpleTreeDataGrain;
NewGrain:TSxSimpleTreeDataGrain;
begin
PermanentFolder:=TSxSimpleTreeProvider(Sender). LocatePermanentGrain(TSxSimpleTreeDataGrain(FolderGrain).GUID);
PermanentFolder.CreateNewChild(NewGrain);
PermanentFolder.Add(NewGrain);
NewGrain.Name:=ExtractFileName(Filename);
NewGrain.GrainType:=gtItem;
SxSimpleTreeProvider.NotifyAddGrain(FolderGrain,NewGrain);
end;
|
OnHandleModify event called
when file representation of your data grain was updated by application,
where your grain opened through common dialog.
You receive DataGrain that need to be modified (DataGrain property)
and its contents in the stream (DataStream property). You need
just update your data storage with new contents from stream.
ProcedureTPermanentModule.SxSimpleTreeProviderHandleModify(
Sender: TSxCustomProvider; DataGrain: TSxCustomDataGrain;
DataStream: TStream);
var
ThisPermanentGrain:TSxSimpleTreeDataGrain; // Only TSxSimpleTreeView specific
Temp:array [0..1000] of char; // Just for example
begin
if DataStream.Size>1000 then
Exit;
FillChar(Temp,1000,0);
ThisPermanentGrain:=TSxSimpleTreeProvider(Sender). LocatePermanentGrain(TSxSimpleTreeDataGrain(DataGrain).GUID);
DataStream.Read(Temp,DataStream.Size);
ThisPermanentGrain.Name:=StrPas(Temp);
end;
|
OnHandleDelete event called
when file representation of your data grain was deleted. You can
handle this event by the following way:
procedure TPermanentModule.SxSimpleTreeProviderHandleDelete(
Sender: TSxCustomProvider; DataGrain: TSxCustomDataGrain);
begin
TSxSimpleTreeProvider(Sender).DeletePermanentGrain(DataGrain);
end;
|
Source events group is a
set of events, that allows your users to drag, copy and cut data
grains from your NSE to another sources (Explorer, Recycle Bin,
Outlook etc.)
OnGetGrainFileInfo event
called when your NSE or other application need to receive some
brief information on your file.
ProcedureTPermanentModule.SxSimpleTreeProviderGetGrainFileInfo(
Sender: TSxCustomProvider; DataGrain: TSxCustomDataGrain;
var FileInfo: TSxFileInfoDescriptor);
begin
FileInfo.FileName:=DataGrain.Name;
FileInfo.FileSize:=Length(DataGrain.Name);
end;
|
OnGetGrainFileContents
event called almost ever after OnGetGrainFileInfo event. You should
provide calling side with your data grain contents (from your
data storage). This data should be exact contents of the result
file.
ProcedureTPermanentModule.SxSimpleTreeProviderGetGrainFileContents(
Sender: TSxCustomProvider; DataGrain: TSxCustomDataGrain;
Stream: TMemoryStream);
var
Temp:array [0..100] of char;*
begin
StrPLCopy(@Temp,DataGrain.Name,100);
Stream.Write(Temp,Length(DataGrain.Name))
end;
|
* Note: never work with dynamicly allocated strings
in the stack, use GetMem or gpMalloc methods.
OnGetGrainFileName event
need to be handled when calling application supports HDROP data
format and your NSE can provide a fully qualified path to the
file, that is a true representation of DataGrain, which you will
get from event parameters.
ProcedureTSxPermanentModule1.ByHandProviderGetGrainFilename(
Sender: TSxCustomProvider; DataGrain: TSxCustomDataGrain;
var Filename: WideString);
begin
Filename:=DataGrain.Strings[0];
end;
|
Other events group is a set of
events, that used for handing common Shell tasks that does not related
to OLE.
OnRenameGrain event exposed
when someone makes an attempt to rename grain in your folder.
You can allow or discard changes by specifiyng approciate value
in the PerformRename property.
procedure TFormPermanentModule.SxSimpleTreeProviderRenameDataGrain(
Sender: TSxCustomProvider; ParentGrain, DataGrain: TSxCustomDataGrain;
var NewName: WideString; var PerformRename: Boolean);
var
permanentGrain:TSxSimpleTreeDataGrain;
begin
permanentGrain:=TSxSimpleTreeProvider(Sender). LocatePermanentGrain(TSxSimpleTreeDataGrain(DataGrain).GUID);
permanentGrain.Name:=NewName;
PerformRename:=True;
end; |
Rename verb always enabled if you have implemented this event
and always disabled if not. Also you can control Rename verb availability
using OnQueryRenameGrain event.
OnQueryRenameGrain
can be handled to enable or disable "Rename" verb for
specified data grain. Set AllowRename parameter to True if you
allow renaming of this item and to False if not. Note that handling
this event is not a rename operation, just Shell need know how
to display context menu item or button.
BeforeCompareGrains
event called to ask you: can specified grain to be compared by
specified column?
OnQueryDeleteGrain
event can be called to enable or disable "Delete" verb
for specified data grain. This event workin in pair with OnHandleDelete
event.
|
Last update
December 22, 2011
|
|