Create document set with custom properties using JSOM

This is article is not about something new, is about how you organize the code to create a document set with custom properties. Let’s imagine I have created a column “Employee” (people or group field type) and associate it with Document Set content type. Of course I want to assign a value to this field when I create a new document set. What I did was to get the pieces of information from the internet and consolidate my code based on these.

Shp.DocumentSet = function () {
    throw 'Cannot instantiate Shp.DocumentSet static class';
}

Shp.DocumentSet.createDocumentSet = function (listName, docSetName, web, properties, success, fail) {
    /// <signature>
    ///	    <summary>Create document set</summary>
    /// 	<param name="listName" type="String" optional="false" mayBeNull="false">Document library name</param>
    /// 	<param name="docSetName" type="String" optional="false" mayBeNull="false">Document set name</param>
    ///     <param name="web" type="SP.Web" optional="false" mayBeNull="true">Web</param>
    ///     <param name="success" type="Function" optional="false" mayBeNull="false">Success</param>
    ///     <param name="error" type="Function" optional="true" mayBeNull="false">Fail</param>
    /// </signature>

    var e = Function.validateParameters(arguments, [{ name: 'listName', type: String, optional: false, mayBeNull: false },
                                                     { name: 'docSetName', type: String, optional: false, mayBeNull: false },
                                                     { name: 'web', type: SP.Web, optional: false, mayBeNull: true },
                                                     { name: 'properties', type: Object, optional: false, mayBeNull: true },
                                                     { name: 'success', type: Function, optional: false, mayBeNull: false },
                                                     { name: 'fail', type: Function, optional: false, mayBeNull: true }], true);
    if (e) throw e;


    var ctx = (typeof web === 'undefined' || web === null) ? SP.ClientContext.get_current() : web.get_context();
    var web = (typeof web === 'undefined' || web === null) ? ctx.get_web() : web;
    var fail = fail || function (err) { alert(err); }

    Shp.DocumentSet._createDocumentSet(listName, docSetName, ctx, web, properties, success, fail);

};

Shp.DocumentSet._createDocumentSet = function (listName, docSetName, ctx, web, properties, success, fail) {

    var list = web.get_lists().getByTitle(listName);
    ctx.load(list);

    var parentFolder = list.get_rootFolder();
    ctx.load(parentFolder);



    var docSetContentTypeID = "0x0120D520";
    var docSetContentType = ctx.get_site().get_rootWeb().get_contentTypes().getById(docSetContentTypeID);
    ctx.load(docSetContentType);

    ctx.executeQueryAsync(function () {
        SP.DocumentSet.DocumentSet.create(ctx, parentFolder, docSetName, docSetContentType.get_id());
        var docSetFolder = web.getFolderByServerRelativeUrl(parentFolder.get_serverRelativeUrl() + '/' + docSetName);
        var docSetFolderItem = docSetFolder.get_listItemAllFields();
        if (properties != null) {
            for (var property in properties) {
                if (properties.hasOwnProperty(property) === true) {
                    docSetFolderItem.set_item(property, properties[property]);
                }
            }
        }
        docSetFolderItem.update();
        ctx.load(docSetFolderItem);
        ctx.executeQueryAsync(function () {
            success(docSetFolderItem);
        }, fail);
    },
    fail);

};

Shp.DocumentSet.registerClass('Shp.DocumentSet');

I have created a static class called Shp.DocumentSet with Shp.DocumentSet.createDocumentSet method, which accepts these parameters:

  • listName: document library name
  • docSetName: document set name
  • docSetName: SP.Web I want to use for current operation. If null, web for current context will be considered.
  • properties: an object containing custom properties I want to update. If null, no custom property will be updated.
  • success: function to be called on success. This will be called passing list item associated with document set folder as a parameter.
  • fail: function to be called on fail.

Let’s see how you can use it.

var docLibraryName = 'Doc library name';
var docSetName = 'Doc set name';
var spWeb = null;
var properties = {};
properties['Employee'] = employeeId;
var success = function(item) {
    // Do something with item
};

var fail = function (err) {
    // Do something with error
};

Shp.DocumentSet.createDocumentSet(docLibraryName, docSetName, spWeb, properties, success, fail);

Creating document set with custom property is now a simple a operation. I do not need write a lot of code each time I need to this.

Advertisements

Send list items to recycle bin with JavaScript CSOM

Delete operations examples provided by Microsoft include how to delete list items using JavaScript CSOM. And everything is fine, with a small exception. “deleteObject” operation is completely deleting items and you cannot restore them from recycle bin. But instead you can use “recycle” method of SP.ListItem object. My article will show how to sent multiple items to recycle bin using JavaScript CSOM.

As usual, I am using Microsoft Ajax to organize my code in namespaces and classes:

/// <Reference Name="MicrosoftAjax.js" />
/// <Reference Path="jquery.js" />
Type.registerNamespace('Shp');

Shp.Lists = function () {
    throw 'Cannot instantiate Shp.Lists static class';
}

Shp.Lists.registerClass('Shp.Lists');

I will have a static class called “Lists” inside “Shp” namespace and with some help from jQuery (I will need it for deferred object) I will add my methods to complete the operations. In my case method will be called “RecycleItems”.

/// <Reference Name="MicrosoftAjax.js" />
/// <Reference Path="jquery.js" />

Type.registerNamespace('Shp');


Shp.Lists = function () {
    throw 'Cannot instantiate Shp.Lists static class';
}

Shp.Lists.RecycleItems = function (listName, listItems, web) {
    ///	 <summary>Recycle items from the list</summary>
    ///  <param name="listName" type="String" optional="false" mayBeNull="false">List name</param>
    ///  <param name="listItems" type="Array" elementType="Number" elementInteger="true" elementMayBeNull="false" optional="false" mayBeNull="false">Array with string representation of list items id</param>
    ///  <param name="web" type="SP.Web" optional="true" mayBeNull="false">Web</param>
    ///  <returns type="jQuery.deffered" />
    var e = Function.validateParameters(arguments, [{ name: 'listName', type: String, optional: false, mayBeNull: false },
                                                   { name: 'listItems', type: Array, elementType: Number, elementInteger: true, elementMayBeNull: false, optional: false, mayBeNull: false },
                                                   { name: 'web', type: SP.Web, optional: true, mayBeNull: false }], true);
    if (e) throw e;

    // Depending if I provided web parameter, I build internal variable to call internal method
    var ctx = (typeof web === 'undefined' || web === null) ? SP.ClientContext.get_current() : web.get_context();
    var web = (typeof web === 'undefined' || web === null) ? ctx.get_web() : web;

    // Call and return internal method result, which is a jQuery.deffered
    return Shp.Lists._DefferedRecycleItems(listName, listItems, ctx, web);

}

Shp.Lists._DefferedRecycleItems = function (listName, listItems, ctx, web) {
    // Implementation will go here
}

Shp.Lists.registerClass('Shp.Lists');

My method is validating parameters types and called an internal method if parameters are in expected format. Last parameter, which is SP.Web, is optional. If you do not provide it, code will use website that is associated with the client context. And entire implementation will go into my internal method.

/// <Reference Name="MicrosoftAjax.js" />
/// <Reference Path="jquery.js" />

Type.registerNamespace('Shp');


Shp.Lists = function () {
    throw 'Cannot instantiate Shp.Lists static class';
}

Shp.Lists.RecycleItems = function (listName, listItems, web) {
    ///	 <summary>Recycle items from the list</summary>
    ///  <param name="listName" type="String" optional="false" mayBeNull="false">List name</param>
    ///  <param name="listItems" type="Array" elementType="Number" elementInteger="true" elementMayBeNull="false" optional="false" mayBeNull="false">Array with string representation of list items id</param>
    ///  <param name="web" type="SP.Web" optional="true" mayBeNull="false">Web</param>
    ///  <returns type="jQuery.deffered" />
    var e = Function.validateParameters(arguments, [{ name: 'listName', type: String, optional: false, mayBeNull: false },
                                                   { name: 'listItems', type: Array, elementType: Number, elementInteger: true, elementMayBeNull: false, optional: false, mayBeNull: false },
                                                   { name: 'web', type: SP.Web, optional: true, mayBeNull: false }], true);
    if (e) throw e;

    // Depending if I provided web parameter, I build internal variable to call internal method
    var ctx = (typeof web === 'undefined' || web === null) ? SP.ClientContext.get_current() : web.get_context();
    var web = (typeof web === 'undefined' || web === null) ? ctx.get_web() : web;

    // Call and return internal method result, which is a jQuery.deffered
    return Shp.Lists._DefferedRecycleItems(listName, listItems, ctx, web);

}

Shp.Lists._DefferedRecycleItems = function (listName, listItems, ctx, web) {

    var deferred = jQuery.Deferred();
    var oList = web.get_lists().getByTitle(listName);
    var recycledItems = [];

    // For each provided list item id we perform recycle method
    for (var i = 0; i < listItems.length; i++) {
        var oListItem = oList.getItemById(listItems[i]);
        var recycleItem = oListItem.recycle();
        recycledItems.push(recycleItem);
    }

    ctx.executeQueryAsync(function () {

        // In case of success, we refine the results into an array of GUID and use it with deferred.resolve
        Array.forEach(recycledItems, function (element, index, array) {
            array[index] = element['m_value']['_m_guidString$p$0'];
        }, null);

        deferred.resolve(recycledItems);

    }, function (sender, args) {

        // In case of fail, we use error message with deferred.reject
        deferred.reject(args.get_message());

    });

    return deferred.promise();

}

Shp.Lists.registerClass('Shp.Lists');

In my development tasks, code above provided an easy way to remove items from a list, being also capable to restore them. Without entering into details, I can show a basic example.

// Array containing ID of the list items to be sent to recycle bin
var deleteIds = ["1", "2", "3", "6"];

// Define delete operation
var deleteOps = Shp.Lists.RecycleItems('List Name', deleteIds);

// In case of fail we show error message
deleteOps.fail(function (error) {
    alert(error)
});

// In case of success, we show  identifier (GUID type) of the new recycle bin item
deleteOps.done(function (listItemsGuid) {
    for(var i = 0; i < listItemsGuid.length; i++)    {
        alert(listItemsGuid[i]);
    }
});

Hoping my code will help you, I will let you know continue your work. And thank you for ready my post. 🙂

Adding multiple items to a list in a single operation

For each operation you perform on SharePoint list using JavaScript client object model, client context is required to execute query asynchronous operation. But this doesn’t mean is an equal sign between what I have just called it operation and, as an example, each item you want to add to SharePoint list. You can add multiple items to the list and execute client context operation only once.

As in general I like JavaScript organized code, I have created a small file, presented below, which for the moment requires only jQuery to be loaded. There is a plan to extend it and integrate it into a bigger project, but for this example is sufficient to copy the code below only.

/// <Reference Name="MicrosoftAjax.js" />
/// <Reference Path="../jQuery/jquery.js" />



(function () {

    var scriptName = 'SharePoint';


    function execute() {

        Type.registerNamespace('Sys.SharePoint');
        

        Sys.SharePoint.Data = function () {
            /// <summary>Sys.SharePoint.Data static class</summary>
            throw 'Cannot initialize Sys.SharePoint.Data static class';
        }        


        Sys.SharePoint.Data.AddItems = function (listName, listItems, web, success, fail) {
            /// <signature>
            ///     <summary>Add items to the list</summary>
            ///     <param name="listName" type="String" mayBeNull="false" optional="false">List name</param>
            ///     <param name="listItems" type="Array" mayBeNull="false" optional="false" elementType="Object" elementMayBeNull="false">List items</param>
            ///     <param type="SP.Web" mayBeNull="false" optional="true">Web</param>
            /// </signature>
            /// <signature>
            ///     <summary>Add items to the list</summary>
            ///     <param name="listName" type="String" mayBeNull="false" optional="false">List name</param>
            ///     <param name="listItems" type="Array" mayBeNull="false" optional="false" elementType="Object" elementMayBeNull="false">List items</param>
            ///     <param name="web" type="SP.Web" mayBeNull="true" optional="false">Web</param>
            ///     <param name="success" type="Function" mayBeNull="false" optional="false">Success</param>
            ///     <param name="fail" type="Function" mayBeNull="false" optional="true">Fail</param>
            /// </signature>
            var e1 = Function.validateParameters(arguments, [{ name: 'listName', type: String, mayBeNull: false, optional: false },
                                                             { name: 'listItems', type: Array, mayBeNull: false, optional: false, elementType: Object, elementMayBeNull: false },
                                                             { name: 'web', type: SP.Web, mayBeNull: false, optional: true }], true);
            var e2 = Function.validateParameters(arguments, [{ name: 'listName', type: String, mayBeNull: false, optional: false },
                                                             { name: 'listItems', type: Array, mayBeNull: false, optional: false, elementType: Object, elementMayBeNull: false },
                                                             { name: 'web', type: SP.Web, mayBeNull: true, optional: false },
                                                             { name: 'success', type: Function, mayBeNull: false, optional: false },
                                                             { name: 'fail', type: Function, mayBeNull: false, optional: true }], true);
            if (e1 !== null && e2 !== null)
                throw 'Invalid parameters calling AddItems';

            // Define context and web objects for operations
            var ctx = (typeof web === 'undefined' || web === null) ? SP.ClientContext.get_current() : web.get_context();
            var web = (typeof web === 'undefined' || web === null) ? ctx.get_web() : web;

            // Use jQuery deffered for operation
            if (e1 === null && e2 !== null) {
                return Sys.SharePoint.Data._defferedAddItems(listName, listItems, ctx, web);
            }

            // Choose success and fail methods to be executed when operation is ready
            if (e1 !== null && e2 === null) {
                var fail = fail || function (err) { alert(err); };
                Sys.SharePoint.Data._nonDefferedAddItems(listName, listItems, ctx, web, success, fail);
            }

            return null;
        }


        Sys.SharePoint.Data._defferedAddItems = function (listName, listItems, ctx, web) {
            /// <summary>Add items to the list using jQuery deffered (internal use)</summary>
            /// <param name="listName" type="String" mayBeNull="false" optional="false">List name</param>
            /// <param name="listItems" type="Array" mayBeNull="false" optional="false" elementType="Object" elementMayBeNull="false">List items</param>
            /// <param name="ctx" type="SP.ClientContext" mayBeNull="false" optional="false">Context</param>
            /// <param name="web" type="SP.Web" mayBeNull="false" optional="false">Web</param>

            var oList = web.get_lists().getByTitle(listName);
            var deffered = jQuery.Deferred();
            var results = [];


            // for each item of in list items we create item in SharePoint list
            for (var i = 0; i < listItems.length; i++) {

                var listItem = listItems[i];
                var listItemCreationInfo = new SP.ListItemCreationInformation();
                var oListItem = oList.addItem(listItemCreationInfo);

                // for each property of current item, we assign field values
                for (var field in listItem) {
                    oListItem.set_item(field, listItem[field]);
                }

                // update and load SharePoint list item and add it to results array
                oListItem.update();
                ctx.load(oListItem);
                results.push(oListItem);
            }


            // context will execute query async
            ctx.executeQueryAsync(function () {
                deffered.resolve(results);
            }, function (sender, args) {
                deffered.reject(args.get_message());
            });

            return deffered.promise();
        }

        Sys.SharePoint.Data._nonDefferedAddItems = function (listName, listItems, ctx, web, success, fail) {
            /// <summary>Add items to the list (internal use)</summary>
            /// <param name="listName" type="String" mayBeNull="false" optional="false">List name</param>
            /// <param name="listItems" type="Array" mayBeNull="false" optional="false" elementType="Object" elementMayBeNull="false">List items</param>
            /// <param name="ctx" type="SP.ClientContext" mayBeNull="false" optional="true">Context</param>
            /// <param name="web" type="SP.Web" mayBeNull="false" optional="false">Web</param>
            /// <param name="success" type="Function" mayBeNull="false" optional="false">Success</param>   
            /// <param name="fail" type="Function" mayBeNull="false" optional="false">Fail</param>   

            var oList = web.get_lists().getByTitle(listName);
            var results = [];


            // for each item of in list items we create item in SharePoint list
            for (var i = 0; i < listItems.length; i++) {

                var listItem = listItems[i];
                var listItemCreationInfo = new SP.ListItemCreationInformation();
                var oListItem = oList.addItem(listItemCreationInfo);

                // for each property of current item, we assign field values
                for (var field in listItem) {
                    oListItem.set_item(field, listItem[field]);
                }

                // update and load SharePoint list item and add it to results array
                oListItem.update();
                ctx.load(oListItem);
                results.push(oListItem);
            }

            // context will execute query async
            ctx.executeQueryAsync(function () {
                success(results);
            }, function (sender, args) {
                fail(args.get_message());
            });
        }


        Sys.SharePoint.Data.registerClass('Sys.SharePoint.Data');

    }

    

    if (window.Sys && Sys.loader) {
        Sys.loader.registerScript(scriptName, null, execute);
    }
    else {
        execute();
    }


})();

Deffered operation

Let’s take a look at the following example.

	var items = [];
	items.push({ Title: 'Title 1' });
	items.push({ Title: 'Title 2' });
	items.push({ Title: 'Title 3' });
	
	var addItems = Sys.SharePoint.Data.AddItems('List Name', items);
	
	addItems.fail(function(error) {
		alert(fail);
	});
	
	addItems.done(function(results) {
		alert('Success');
	});

You can notice I have used an object for each item I wanted to create and properties have the same name as internal fields. I have add them to an array and pass it as a parameter to Sys.SharePoint.Data.AddItems method. And instead of using classic example, I have used jQuery deffered object to write code easier.

If web parameter is not specified, the web for current context is used. However, it can be specified as additional parameter.

	var context = new SP.ClientContext("https://mydomain.com/sites/toolkit");
	var web = context.get_web();
	
	
	var items = [];
	items.push({ Title: 'Title 13' });
	items.push({ Title: 'Title 12' });
	items.push({ Title: 'Title 10' });
	
	var addItems = Sys.SharePoint.Data.AddItems('List Name', items, web);
	
	addItems.fail(function(error) {
		alert(fail);
	});
	
	addItems.done(function(results) {
		alert('Success');
	});

Classic way

However, you may prefer to use the classic way and pass success and fail callback functions as parameters.

	var context = new SP.ClientContext("https://mydomain.com/sites/toolkit");
	var web = context.get_web();
	
	
	var success = function(results) {
		alert('Operation completed and insert ' + results.length + ' items');
		alert('First item title is ' + results[0].get_item('Title'));
		
	}
	
	
	var fail = function(error) {
		alert(error);
	}
	
	
	var items = [];
	items.push({ Title: 'Title 100' });
	items.push({ Title: 'Title 101' });
	items.push({ Title: 'Title 102' });
	
	Sys.SharePoint.Data.AddItems('List Name', items, web, success, fail);

Conclusion

Sys.SharePoint.Data.AddItems is a method with two signatures. If you want to use jQuery.Deffered object to work with results, you need pass these parameters

  • listName as string and mandatory
  • listItems as array of objects (not null and not optional)
  • web as SP.Web (not null but optional). If not specified, it is considered to be web for current client context.

The second signature accepts five parameters.

  • listName as string and mandatory
  • listItems as array of objects (not null and not optional)
  • web as SP.Web (can be null, but not optional). If not specified, it is considered to be web for current client context.
  • success as function (not null and not optional). It should accept as parameter an array containing created items.
  • fail as function (not null, but optional). It should accept as a parameter a string representing error message.

CSOM is able to upload large files

When you search for custom solutions to upload files to SharePoint, you will probably be advised to use REST API offer by SharePoint 2013, which is fine. The reason why people advise you so is mainly because CSOM is not able to upload files larger than 2Mb. Which is partial true. There is a solution to upload large files using CSOM and I will show you below how to do it.

Solutions I choose is based on Silverlight. I know you will tell me it is obsolete. But for the moment is still on the market, available at least till 2021 and is sometimes a good solution. So I see no reasons why you cannot use it. So what I achieved looks like below and if you like what you see, continue to read. 🙂
Silverlight Uploader

Solution markup

Solution contains a textbox, where selected file name will be display, a label to display error or success message, a button to open file dialog window to choose the file you want to upload and another button to start upload process. Without too many comments, here is the markup.

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit" x:Class="HPFileUploader.MainPage"
    mc:Ignorable="d" Height="113" Width="800">
    <toolkit:TwilightBlueTheme  HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top">
        <Grid x:Name="LayoutRoot" Background="White" Margin="0,4,0,0" Height="113" VerticalAlignment="Top" Width="800">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="13*"  />
                <ColumnDefinition Width="787*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>

            <Button Content="Choose" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Left" Margin="225,10,0,0" VerticalAlignment="Top" Width="75" x:Name="btnUpload" Click="btnUpload_Click" RenderTransformOrigin="0.52,-0.864"/>
            <TextBox  Grid.Column="0" Grid.Row="0" HorizontalAlignment="Left" IsReadOnly="True"  x:Name="txtFileName" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="220" Grid.ColumnSpan="2"/>
            <Button Grid.Column="1" Grid.Row="0"   Content="Start Upload" HorizontalAlignment="Left" Margin="305,10,0,0" VerticalAlignment="Top" Width="92" x:Name="startUpload" Click="startUpload_Click"/>

            <sdk:Label HorizontalAlignment="Left" x:Name="lblError" VerticalAlignment="Center" VerticalContentAlignment="Center" Height="28" Margin="0,11,0,17" Grid.Row="1"  Width="680" Grid.ColumnSpan="2">

            </sdk:Label>
      </Grid>
    </toolkit:TwilightBlueTheme>
</UserControl>

Add SharePoint client reference

If you choose to create a standalone Silverlight project, you will need to add some libraries and references to your project. The easiest way it to you use NuGet and add Microsoft SharePoint Foundation 2010 Client Object Model package.
FileUploader - Manage NuGet Packages

Once you add the package you need to be sure the following references are set.
2015-01-28 22_00_52-HPFileUploader - Microsoft Visual Studio (Administrator)

Code behind

And here is essence of this article.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.SharePoint.Client;
using System.IO;
using System.Text;
using System.Xml.Linq;
using System.Windows.Browser;


namespace FileUploader
{
    public partial class MainPage : UserControl
    {

        private FileStream fs;
        private string fName = "";

        public MainPage()
        {
            InitializeComponent();
        }

        /// <summary>
        /// This method is handling click even for choose file button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnUpload_Click(object sender, RoutedEventArgs e)
        {

            this.lblError.Content = "";            

            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Multiselect = false;

            if (dlg.ShowDialog() == true)
            {
                fs = dlg.File.OpenRead();
                fName = dlg.File.Name.ToString();
                this.txtFileName.Text = fName;
            }
            else
            {
                fs = null;
                fName = "";
                this.txtFileName.Text = "";
            }
        }

        /// <summary>
        /// Start upload button click
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void startUpload_Click(object sender, RoutedEventArgs e)
        {
            if (this.fs == null || this.fName == "") return;

            using (ClientContext ctx = new ClientContext("https://sharepoint"))
            {

                this.btnUpload.IsEnabled = false;
                this.startUpload.IsEnabled = false;
                this.startUpload.Content = "Wait...";

                Web web = ctx.Site.RootWeb;
                List oList = web.Lists.GetByTitle("Attachments");
                FileCreationInformation fci = new FileCreationInformation();
                fci.ContentStream = fs;
                fci.Overwrite = true;
                fci.Url = "https://sharepoint/Attachments/" + fName;
                Microsoft.SharePoint.Client.File file = oList.RootFolder.Files.Add(fci);
                ctx.ExecuteQueryAsync(
                    (object s, ClientRequestSucceededEventArgs args) =>
                    {
                        this.Dispatcher.BeginInvoke(() =>
                        {
                            this.lblError.Content = "File was upload succesfully";
                            this.txtFileName.Text = "";
                            this.fName = "";
                            this.fs = null;
                            this.btnUpload.IsEnabled = true;
                            this.startUpload.IsEnabled = true;
                            this.startUpload.Content = "Start Upload";
                            HtmlPage.Window.Invoke("RefreshFilesList");
                        });
                    },
                    (object s, ClientRequestFailedEventArgs args) =>
                    {
                        this.Dispatcher.BeginInvoke(() =>
                        {
                            this.lblError.Content = "Request failed. " + args.Message + "\n" + args.StackTrace;
                        });
                    }
               );
            }
        }
    }
}

I am not going to explain all the code, but I will tell you what exactly is doing the trick. Giving a general example, the essence of the code can be reduced to this:

       // Create file stream and file name variables
        FileStream fs;
        String fName;

        // Create file dialog and use to assign value to the variable
        OpenFileDialog dlg = new OpenFileDialog();
        dlg.Multiselect = false;

        if (dlg.ShowDialog() == true)
        {
            fs = dlg.File.OpenRead();
            fName = dlg.File.Name.ToString();
            this.txtFileName.Text = fName;
        }

        // Upload file
         using (ClientContext ctx = new ClientContext("https://sharepoint"))
         {
            Web web = ctx.Site.RootWeb;
            List oList = web.Lists.GetByTitle("Attachments");
            FileCreationInformation fci = new FileCreationInformation();
            // Here is the magic. Set ContentStream property of FileCreationInformation before execute query is doing the trick
            fci.ContentStream = fs;
            fci.Overwrite = true;
            fci.Url = "https://sharepoint/Attachments/" + fName;
            Microsoft.SharePoint.Client.File file = oList.RootFolder.Files.Add(fci); 
            ctx.ExecuteQueryAsync(success, fail) ;
         }  

I hope this will help. Happy coding! 🙂