In the first part I showed the implementation of the backup logic, the second part was about integration with the a Custom File Content Provider.
You remember:
- Backup functionality (make copies of deleted / overwritten files) (DONE)
- Copy files with drag and drop (the control does a move for this) (DONE)
- Protect files from being deleted (DONE)
- Add server side events when a file / directory is selected (the control is almost pure client Ajax)
The events…
The telerik RadFileExplorer is an Ajax control. This means – everything runs at the client :)
Of course it does not – but there is little communication and (with the current version) there are only server events available for things like deleting a file or so.
Or let me tell it the other way – exactly the things I’m interested in do not fire server events.
So I first took a look at the client events and there I found what I needed. The telerik RadFileExplorer has a rich client event model and so it was easy to find the two events I was looking for.
All I had to do was to handle these events and route them to the server.
So it comes to java script (I prefer C# :)):
Snippet created with CBEnhancer <script language="javascript" type="text/javascript"> //<![CDATA[ var bIsRefreshing =
false;
function RefreshAfterFileRestore(sender, args) {
setTimeout(
"TimerEnd()", 600);
//used to avoid request while other request is still in progress }
function TimerEnd() {
bIsRefreshing =
true;
//block double refreshes var fX = $find(
"<%= rfxFiles.ClientID %>");
fX.refresh(fX.get_currentDirectory());
}
function OnFolderLoaded(sender, args) {
if (bIsRefreshing) {
//avoid refresh when initiated from a refresh bIsRefreshing =
false;
return;
}
var item = args.get_item();
//inform the server that a new directory is the current one TellServerAboutSelectedElement(
"DIR:" + item.get_path());
//signal dir got loaded }
function OnClientItemSelected(sender, args) {
var item = args.get_item();
if (item.get_type() == Telerik.Web.UI.FileExplorerItemType.File) {
args.set_cancel(
true);
TellServerAboutSelectedElement(
"FILE:" + item.get_path());
}
else {
//if you want the directory selected in grid as "current directory do this //TellServerAboutSelectedElement("DIR:" + item.get_path()); //signal no file selected!! //in this case I just want to tell that no file is selected TellServerAboutSelectedElement(
"FILE:");
}
}
//not active - setting the element in page load is commented out function ResponseEnded(sender, args) {
var v = args.EventTargetElement;
}
function RequestStarted(sender, args) {
var v = args.EventTargetElement;
}
function TellServerAboutSelectedElement(argument) {
var ajaxManager = $find(
"<%= RadAjaxManager.GetCurrent(Page).ClientID %>");
ajaxManager.ajaxRequest(argument);
}
//]]> </script>
This may look a bit complicated – but it isn’t.
First of all – ignore the RefreshAfterFileRestore and TimerEnd functions – I’ll explain them later.
There are two interesting events here – OnFolderLoaded (the FileExplorer displays a new folder) and OnClientItemSelected (the user clicked on a file or folder).
In the case of a folder load I simply pass “DIR: FolderPath” to the server.
If an other Item is selected I pass either the filepath or an empty path for the file if the user selected a folder.
The reason – files are different – they can be previewd or things like this while folders are (in my case) not interesting as long as they are not opened. If someone thinks different – the code to handle selected folders is here in comments.
The server communication function is easy since telerik offers a thing called RadAjax – a framework which makes it very easy to handle Ajax things.
What I do here – I get the RadAjaxManager control and call a function which makes an Ajax Request to the server.
On the Server the things look like this:
protected void OnControlsl_AjaxRequest(object sender, Telerik.Web.UI.AjaxRequestEventArgs e) {
if (e.Argument.StartsWith("FILE:")) {
CurFile = e.Argument.Replace("FILE:", ""); //keep selection in hiden form file
RaisePostBackEvent("FILE");
}
else { //if it is not a file it must be directory
CurDir = e.Argument.Replace("DIR:", ""); //keep selection in hiden form file
RaisePostBackEvent("DIR");
CurFile = ""; //no file selected after loading a directory
RaisePostBackEvent("FILE"); //signal that file did change
rgBackupFiles.Rebind(); //and load backup files for this directory
}
}
This is the server side function which get’s call via Ajax. I do the following – if a file is selected I store it’s path.
If a folder is selected there is no file selected – so I clear the file and store the directory.
Important – to preserve these values among postbacks I use hidden controls so the values are kept in viewstate.
You may have noticed that I call a function “RaisePostBackEvent” – this is the bridge to the events my control fires.
#region event handling
#region FileSelected
public event EventHandler FileSelected;
#endregion
#region DirectorySelected
public event EventHandler DirectorySelected;
#endregion
#region IPostBackEventHandler Member
public void RaisePostBackEvent(string eventArgument) {
if (eventArgument == "FILE") {
OnFileSelected(new EventArgs());
return;
}
OnDirSelected(new EventArgs());
}
protected virtual void OnFileSelected(EventArgs e) {
if (FileSelected != null) {
FileSelected(this, e);
}
}
protected virtual void OnDirSelected(EventArgs e) {
if (DirectorySelected != null) {
DirectorySelected(this, e);
}
}
#endregion
#endregion
This is the complete event handling. It is done by implementing the IPostBackEventHandler interface (one function :=)) and the rest is “event handling as usual”. Check if the handler has a consumer – if so fire the event.
In the Ajax handler is a code line
rgBackupFiles.Rebind(); //and load backup files for this directory
}
}
Yes – I did not explain it – this rebinds a datagrid – because – backup files is one idea – restore them makes the thing useful.
But I’ll describe this in the next post.
Let me first finish here and show how the control is used – and how the events are consumed.
Snippet created with CBEnhancer <uc1:XFileExplorer ID="xfeOne" runat="server" BackupURL="/XBackup" BackupEnabled="True" DoCopy="True" ProtectedFiles="/XFiles/Styles/XFEStyles.css,/XFiles/Styles/Main.css,/XFiles/Styles/bkg/PageBKG.jpg" ViewPaths="~/XFiles,~/XImages,~/XDocs" DeletePaths="~/XFiles,~/XImages,~/XDocs" UploadPaths="~/XFiles,~/XImages,~/XDocs" OnFileSelected="xfeOne_OnFileSelected" OnDirectorySelected="xfeOne_OnDirSelected" />
This control is hosted in an aspx page.
The last Line contains the event binding and before I make those settings I told before – BackupEnabled, ProtectedFiles and so on.
By the way – ViewPaths, DeletePaths and UploadPaths are original properties from the telerik RadFileExplorer which I simply pass to this control which I host in my usercontrol.
Now as the events are bound here – lets have a look how they are consumed.
But first I have to show the other controls on this page (just a sample that these events make sense):
Snippet created with CBEnhancer <tr> <td style="width:
300px;
text-align:
center;
font-weight:
bold;
background-color:
#E0E0E0"> Current directory
</td> <td style="width:
220px;
text-align:
center;
font-weight:
bold;
background-color:
#E0E0E0"> Current file
</td> </tr> <tr> <td style="width:
300px;
"> <asp:Label ID="lblCurDir" runat="server" /> </td> <td style="width:
220px;
"> <asp:Label ID="lblCurFile" runat="server" /> </td> </tr> <tr> <td style="width:
300px;
"> <asp:HyperLink ID="hlZIPDir" runat="server" Visible="false" NavigateUrl="/DoDownload.ashx?Action=DIR" Text="Download Directory as ZIP" Target="_blank" /> </td> <td style="width:
220px;
"> <asp:HyperLink ID="hlDownloadFile" runat="server" Visible="false" NavigateUrl="/DoDownload.ashx?Action=FILE" Text="Download File" Target="_blank" /> </td> </tr> <tr> <td style="text-align:
center" colspan="2"> <asp:Image ID="imgPreview" runat="server" Visible="false"/> </td> </tr>
This is part of a table holding two labels to display the current directory and file (selected in FileExplorer) as well as two hyperlinks to a http handler which handles the download of files / folder ZIPs.
Now let us take a look at the server side.
protected void xfeOne_OnFileSelected(object sender, EventArgs e) {
string strCurFile = xfeOne.CurFile;
lblCurFile.Text =strCurFile;
if (string.IsNullOrEmpty(strCurFile)) {
//clear parameter for download handler
Session["CurFile"] = null;
hlDownloadFile.Visible = false;
imgPreview.Visible = false;
return;
}
hlDownloadFile.Visible = true;
Session["CurFile"] = strCurFile;
//try to set preview for images
string strExtension = Path.GetExtension(strCurFile).ToUpper();
if (strExtension == ".PNG" || strExtension == ".JPG" || strExtension == ".GIF") {
imgPreview.ImageUrl = strCurFile;
imgPreview.Visible = true;
}
else {
imgPreview.Visible = false;
}
}
protected void xfeOne_OnDirSelected(object sender, EventArgs e) {
string strCurDir = xfeOne.CurDir;
lblCurDir.Text = strCurDir;
if (string.IsNullOrEmpty(strCurDir)) {
Session["CurDir"] = null;
hlZIPDir.Visible = false;
return;
}
hlZIPDir.Visible = true;
Session["CurDir"] = strCurDir;
}
Very simple handlers – of course it is a sample to show the control nothing more.
Those handlers check if there is a value (remember – file can be empty if the user selectes a folder).
Then I remember the values in session set the labels – and show (hide) the links to the handler.
And if it is an image file – I set the value of the image control to display it.
So far we have a control looking like this one and a few “informational” controls for the current paths.
And we have “magic” behind the thing – backup / protection / copy instead of move.
We are almost done – but of course backup makes no sense when there is no restore.
I’ll describe this in the next section – and this will also give an answer to the question about the two JScript functions shown (but not explained) above.