Looks fantastic

May 27, 2014 at 1:48 PM
First of all, thank you for this add-on, file attachments are a pretty standard requirement and solutions for this seem to have been a mixed bag in the past. I look forward to giving this a shot soon. If possible I would like to see some examples (C#) of how you would implement this. I have a platform I am working on that I would like to add an easier solution for attachments. I am currently using Elyls Extensions for and though it works it does somewhat confuse users. You have to add a new record and then you can attach below. I would also like to hyperlink the filename to download or preview the file.

Thanks,

Josh
Coordinator
May 29, 2014 at 9:53 PM
Josh,

THANKS!

I have been pretty busy, but at some point I'll add a Lightswitch app that demonstrates how to use this extension. Until then, I do my work in C#, so I'm happy to provide any code I've actually used as an example. Unfortunately I don't know Elyls Extensions so I cannot compare to it.

More or less, to upload a file:
  1. Follow the steps in Getting Started to get the extension downloaded and set up for any particular Lightswitch app. This includes setting up a database with a FileTable in it. An example of an SQL script that can create such a database is here, in an earlier release (the example DB hasn't changed).
  2. Create a Lightswitch screen capable of inserting a new FileNode. That screen should be based on FileNodes.lsml.
  3. Add a new button with a corresponding method to the command bar. I called mine SelectFile. This is obviously an example - call it whatever you want.
  4. Override the code for the button. Following is a code example (note - I added two using statements in addition to the sample code in SelectFile_Execute():
using System;
using System.Linq;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections.Generic;
using Microsoft.LightSwitch;
using Microsoft.LightSwitch.Framework.Client;
using Microsoft.LightSwitch.Presentation;
using Microsoft.LightSwitch.Presentation.Extensions;

// Added to support File dialogs
using System.Windows.Controls;
using Microsoft.LightSwitch.Threading;


namespace LightSwitchApplication
{
    public partial class CreateNewFileNode
    {
        partial void CreateNewFileNode_InitializeDataWorkspace(global::System.Collections.Generic.List<global::Microsoft.LightSwitch.IDataService> saveChangesTo)
        {
            // Write your code here.
            this.FileNodeProperty = new FileNode();
        }

        partial void CreateNewFileNode_Saved()
        {
            // Write your code here.
            this.Close(false);
            Application.Current.ShowDefaultScreen(this.FileNodeProperty);
        }

        partial void SelectFile_Execute()
        {
            Dispatchers.Main.Invoke(() =>
            {
                var dlg = new OpenFileDialog();
                if (dlg.ShowDialog() == true)
                {
                    this.FileNodeProperty.Data = File.ReadAllBytes(dlg.File.FullName);
                    this.FileNodeProperty.Name = dlg.File.Name;
                }
            });
        }
    }
}
The Name is the only property you have to specify. The only other properties you can specify are the Data (the actual data content of the file) and the file location (by specifying ParentPathLocator) -- and if you don't specify the location, it will end up at the root of the FileTable. Everything else is actually a persisted calculated column when the file gets stored in the FileTable. The FileType is set for you (based on the type of file you select), Path is based on where you put the file when you created it (and you can move it), Size is again based on the file you upload, and IsFolder will be false unless you set it to True (in which case data will be ignored, and you're going to get an error from the database itself if you try to create a FileNode that is a folder and give it data).

You would specify the file location by first retrieving a FileNode representing the folder in which you want to store a file or another folder, and second, setting the ParentPathLocator on the new file or folder FileNode to the same value as the PathLocator of the file or folder FileNode that should contain the new item. So if you already had that value, you could specify it here as well.

There is no step 5. Just run it and it should work.

I suppose if you wanted to link to this FileNode in another table in your own database, the best way to do that would be to store the resulting FileNode.ID (which is a GUID that is generated when the FileNode is created). The GUID for the new FileNode is given to you on the details screen where you go after creating the node; so, you could capture it there. There is a built in GetNodeByID query that you can use to get the FileNode whenever you need it. The GUID will never change, but the PathLocator and ParentPathLocator will change based on where the item is in the directory structure.

Hope this helps. Any other questions, feel free to ask. And - please rate this extension over on The Visual Studio Gallery!
Coordinator
May 29, 2014 at 10:09 PM
Oh, also, as far as previewing the file, I am using something called Document Toolkit to view files right within the application. It will handle most file types but it does not have full support for PDF, which you can get around by launching the PDF viewer on the system on which you're running (from a Silverlight Desktop application) using only a couple of lines of code.

Document Toolkit for Lightswitch is a very easy and feature rich extension - well worth the price the developer is asking.

That, in turn, is based on the open source Document Toolkit for Silverlight, which you can find here on CodePlex - in fact, here.

If you look at his Getting Started, the page that tests the Document Viewer is opening the file from the file system, and setting the source for the document viewer to the resulting file selected in a file dialog (very similar approach to the one I used above to select a file to upload). Instead, you can point the DocumentReader to a byte array. Very specifically, you only have to change this:
                    using (var reader = DocumentReader.Create(dlg.File))
                    { this.Document = reader.ToBinary(); }
... to this:
                    using (var reader = DocumentReader.Create(theFileNode.Data))
                    { this.Document = reader.ToBinary(); }
... and you're off to the races.

Of course, you can use whatever you want to view files, and you can download them right to the file system and open them using an external viewer. You can also pass the file bytes right into anything that will accept them for viewing -- I am just picking on Document Toolkit because it's something I've used.

Also - the FileNode is available to you in Silverlight Desktop clients, Silverlight Web clients, and HTML clients. So if there is some more appropriate control you want to use for viewing in a given environment - no worries, just pass the FileNode.Data to whatever needs the data.

HTH

R/Don//
Coordinator
May 30, 2014 at 10:22 PM
A complete walkthrough, with code examples, using the latest (1.0.4) release, is now available in the documentation section, right here.

This will walk you through the process of:
  • creating a screen that you could use to create new FileNodes
  • customizing how FileNodes are summarized
  • allowing users to select a target folder in which to store the resulting FileNode
Please let me know if this helps, or if you have further questions. Hopefully this shows off a good bit of the Data Source Extension's functionality.
R/Don//
Marked as answer by DonYeske on 7/13/2014 at 3:02 PM