New to Telerik UI for ASP.NET AJAXStart a free 30-day trial

Upload and display multiple attachments

DESCRIPTION

Upload and display multiple attachments in RadGrid using a GridTemplateColumn with RadAsyncUpload.

"Upload multiple attachments - showcase"

The built-in GridAttachmentColumn only uploads and displays one attachment at the time, see Grid - Column Types.

However, a solution to upload and display multiple attachments can be implemented additionally.

SOLUTION

For this scenario, you will need to have the Manual CRUD Operations implemented for RadGrid.

Moving on, adjust the Grid markup as follows:

  1. Add a GridTemplateColumn to RadGrid - This will contain the custom Controls to Upload and display the attachments
  2. Add an asp:PlaceHolder to the Column's ItemTemplate - A WebForms Control that will act as placeholder for the attachments' HyperLinks we add programmatically
  3. Add a RadAsyncUpload Control to the EditItemTemplate - This is required to Upload the files to the server

Example TemplateColumn definition

ASP.NET
<telerik:GridTemplateColumn HeaderText="Template Column with Attachments" DataField="Attachments">
    <ItemTemplate>
        <asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
    </ItemTemplate>
    <EditItemTemplate>
        <telerik:RadAsyncUpload ID="RadAsyncUpload1" runat="server" MultipleFileSelection="Automatic" OnFileUploaded="RadAsyncUpload1_FileUploaded">
        </telerik:RadAsyncUpload>
    </EditItemTemplate>
</telerik:GridTemplateColumn>

THE MAGIC

When inserting or updating, access the AsyncUpload in the Grid's Insert/Edit form and loop through the Uploaded Files collecting their names. Using the collection of file names, create a string containing all the file names separated by a coma.

Example

C#
// Access the AsyncUpload control
RadAsyncUpload asyncUpload = editedItem.FindControl("RadAsyncUpload1") as RadAsyncUpload;

// Get the Uploaded files
UploadedFileCollection uploadedFiles = asyncUpload.UploadedFiles;

// Create a new list of strings
List<string> fileNamesList = new List<string>();

// Loop through each uploaded file
foreach (UploadedFile fileToUpload in uploadedFiles)
{
    // add the filename to the list of strings
    fileNamesList.Add(GetNewFileName(fileToUpload.FileName, orderId));
}

// Finally, concatenate all values separated by a coma and update the database field with the new string
newValues["Attachments"] = string.Join(",", fileNamesList.ToArray()); // Result: File1,File2,File3, etc

When displaying, fetch the data from the database, split the string by the coma to get a collection of file names. After that, loop through each filename and create a HyperLink for it. That HyperLink will be added to the PlaceHolder's Controls collection.

Example

C#
protected void RadGrid1_ItemDataBound(object sender, GridItemEventArgs e)
{
    // ItemTemplate
    if (e.Item is GridDataItem)
    {
        GridDataItem item = (GridDataItem)e.Item;

        // Get the data key value
        int orderId = (int)item.GetDataKeyValue("OrderID");

        // find the related row in the database
        DataRow foundItem = SessionDataSource.Select(String.Format("OrderID = '{0}'", orderId)).FirstOrDefault();

        // get the list of file paths by splitting the values separated by coma
        List<string> filePaths = foundItem["Attachments"].ToString().Split(',').Select(path => path.Trim())
                                                                        .Where(path => !string.IsNullOrEmpty(path)).ToList();

        if (filePaths.Count > 0)
        {
            // access the placeholder 
            PlaceHolder ph1 = item.FindControl("PlaceHolder1") as PlaceHolder;

            // loop through each path
            foreach (string filePath in filePaths)
            {
                // Check if the file exists on the specific path
                if (File.Exists(Server.MapPath("~/Attachments/" + filePath)))
                {
                    // if so create Hyperlinks pointing to the Path
                    HyperLink link = new HyperLink();

                    link.NavigateUrl = "/Attachments/" + filePath;
                    link.Text = filePath;
                    link.Target = "_blank";

                    // add the hyperlink to the PlaceHolder
                    ph1.Controls.Add(link);
                }
                else
                {
                    // Notify user the file is missing from server
                    ph1.Controls.Add(new Literal() { Text = filePath + " (missing)" });
                }

                // add line breaks to display each record in a new line
                ph1.Controls.Add(new Literal() { Text = "</br>" });
            }
        }
    }
}

Last, but not least, handling the actual file upload. We want to customize the destination the files will be uploaded to as well as the names we want to use for the uploaded files.

C#
// Upload the Files to a Specific Directory
protected void RadAsyncUpload1_FileUploaded(object sender, FileUploadedEventArgs e)
{
    RadAsyncUpload asyncUpload = (RadAsyncUpload)sender;

    // NamingContainer is the Grid's EditForm since the AsyncUpload is located inside the EditItemTemplate
    GridEditFormItem editFormItem = asyncUpload.NamingContainer as GridEditFormItem;

    int orderId = 1;

    // If Inserting there is no existing records for which we can extract the DataKeyValue
    if (editFormItem is GridEditFormInsertItem)
    {
        // This example demonstrates in-memory editing to avoid setting up a database.
        // In case of databases a new primary key value will be generated for inserted record
        DataRow[] allValues = SessionDataSource.Select("OrderID = MAX(OrderID)");

        if (allValues.Length > 0)
        {
            orderId = int.Parse(allValues[0]["OrderID"].ToString()) + 1;
        }
    }
    else
    {
        // For existing records, we can access the DataKeyValue
        orderId = (int)editFormItem.GetDataKeyValue("OrderID");
    }

    // Set the path where the Files will be uploaded to
    string newFilePath = string.Format("{0}/{1}", Server.MapPath("~/Attachments"), GetNewFileName(e.File.FileName, orderId));

    e.File.SaveAs(newFilePath); // Save the File to the new path/directory
}

// Helper function to format the File name.
private string GetNewFileName(string fileName, int orderId)
{
    // Include OrderID in the Filename
    return string.Format("OrderID_{0}_File_{1}", orderId, fileName);
}

Complete Code

Add the following code snippets to a WebForms page and you'll be ready to run it.

ASPX markup

ASP.NET
<asp:Label ID="Label1" runat="server" Text="Action:"></asp:Label>
<telerik:RadGrid ID="RadGrid1" runat="server" AllowPaging="True" Width="1000px"
    AutoGenerateEditColumn="true"
    AutoGenerateDeleteColumn="true"
    OnNeedDataSource="RadGrid1_NeedDataSource"
    OnInsertCommand="RadGrid1_InsertCommand"
    OnUpdateCommand="RadGrid1_UpdateCommand"
    OnDeleteCommand="RadGrid1_DeleteCommand"
    OnItemDataBound="RadGrid1_ItemDataBound">

    <MasterTableView AutoGenerateColumns="False" DataKeyNames="OrderID" CommandItemDisplay="Top" InsertItemDisplay="Top" InsertItemPageIndexAction="ShowItemOnLastPage">
        <Columns>
            <telerik:GridBoundColumn DataField="OrderID" DataType="System.Int32"
                FilterControlAltText="Filter OrderID column" HeaderText="OrderID"
                ReadOnly="True" SortExpression="OrderID" UniqueName="OrderID">
            </telerik:GridBoundColumn>
            <telerik:GridDateTimeColumn DataField="OrderDate" DataType="System.DateTime"
                FilterControlAltText="Filter OrderDate column" HeaderText="OrderDate"
                SortExpression="OrderDate" UniqueName="OrderDate">
            </telerik:GridDateTimeColumn>
            <telerik:GridNumericColumn DataField="Freight" DataType="System.Decimal"
                FilterControlAltText="Filter Freight column" HeaderText="Freight"
                SortExpression="Freight" UniqueName="Freight">
            </telerik:GridNumericColumn>
            <telerik:GridBoundColumn DataField="ShipName"
                FilterControlAltText="Filter ShipName column" HeaderText="ShipName"
                SortExpression="ShipName" UniqueName="ShipName">
            </telerik:GridBoundColumn>
            <telerik:GridBoundColumn DataField="ShipCountry"
                FilterControlAltText="Filter ShipCountry column" HeaderText="ShipCountry"
                SortExpression="ShipCountry" UniqueName="ShipCountry">
            </telerik:GridBoundColumn>
            <telerik:GridTemplateColumn HeaderText="Template Column with Attachments" DataField="Attachments">
                <ItemTemplate>
                    <asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
                </ItemTemplate>
                <EditItemTemplate>
                    <telerik:RadAsyncUpload ID="RadAsyncUpload1" runat="server" MultipleFileSelection="Automatic" OnFileUploaded="RadAsyncUpload1_FileUploaded">
                    </telerik:RadAsyncUpload>
                </EditItemTemplate>
            </telerik:GridTemplateColumn>
        </Columns>
    </MasterTableView>
</telerik:RadGrid>

C#/VB CodeBehind

C#
#region Properties for CRUD Operations
public DataTable SessionDataSource
{
    get
    {
        string sessionKey = "SessionDataSource";

        if (Session[sessionKey] == null || !IsPostBack)
        {
            Session[sessionKey] = OrdersTable();
        }
        return (DataTable)Session[sessionKey];
    }
}
#endregion

#region RadGrid Events for CRUD Operations

// CREATE (Add New Record)
protected void RadGrid1_InsertCommand(object sender, GridCommandEventArgs e)
{
    GridEditableItem editedItem = e.Item as GridEditableItem;

    DataRow newRow = SessionDataSource.NewRow();

    //As this example demonstrates only in-memory editing, a new primary key value should be generated
    //This should not be applied when updating directly the database
    DataRow[] allValues = SessionDataSource.Select("OrderID = MAX(OrderID)");

    int orderId = 1;

    if (allValues.Length > 0)
    {
        orderId = int.Parse(allValues[0]["OrderID"].ToString()) + 1;
    }

    // OrderID is either 1 (if no records in the Datasource) or an incremented ID (if records exists)
    newRow["OrderID"] = orderId;

    //Set new values
    Hashtable newValues = new Hashtable();
    //The GridTableView will fill the values from all editable columns in the hash
    e.Item.OwnerTableView.ExtractValuesFromItem(newValues, editedItem);

    // Access the AsyncUpload control
    RadAsyncUpload asyncUpload = editedItem.FindControl("RadAsyncUpload1") as RadAsyncUpload;

    // Get the Uploaded files
    UploadedFileCollection uploadedFiles = asyncUpload.UploadedFiles;

    // Create a new list of strings
    List<string> fileNamesList = new List<string>();

    // Loop through each uploaded file
    foreach (UploadedFile fileToUpload in uploadedFiles)
    {
        // add the filename to the list of strings
        fileNamesList.Add(GetNewFileName(fileToUpload.FileName, orderId));
    }

    // Finally, concatenate all values separated by a coma and update the database field with the new string
    newValues["Attachments"] = string.Join(",", fileNamesList.ToArray());
    // Result: File1,File2,File3, etc

    try
    {
        foreach (DictionaryEntry entry in newValues)
        {
            newRow[(string)entry.Key] = entry.Value;
        }
    }
    catch (Exception ex)
    {
        Label1.Text += string.Format("<br />Unable to insert into Orders. Reason: {0}", ex.Message);
        e.Canceled = true;
        return;
    }

    SessionDataSource.Rows.Add(newRow);
    //Code for updating the database ca go here...
    Label1.Text += string.Format("<br />Order {0} inserted", newRow["OrderID"]);
}

// READ (data binding)
protected void RadGrid1_NeedDataSource(object sender, GridNeedDataSourceEventArgs e)
{
    (sender as RadGrid).DataSource = SessionDataSource;
}

// UPDATE
protected void RadGrid1_UpdateCommand(object sender, GridCommandEventArgs e)
{
    // EditForm item
    GridEditableItem editedItem = e.Item as GridEditableItem;

    // Get the Data key value
    int orderId = (int)editedItem.GetDataKeyValue("OrderID");

    //Locate the changed row in the DataSource
    DataRow[] changedRows = SessionDataSource.Select(string.Format("OrderID = {0}", editedItem.GetDataKeyValue("OrderID")));

    if (changedRows.Length != 1)
    {
        this.Label1.Text += "Unable to locate the Order for updating.";
        e.Canceled = true;
        return;
    }

    //Create a Hashtable
    Hashtable newValues = new Hashtable();

    // Extract values of current edited item
    e.Item.OwnerTableView.ExtractValuesFromItem(newValues, editedItem);

    // Access the AsyncUpload control
    RadAsyncUpload asyncUpload = editedItem.FindControl("RadAsyncUpload1") as RadAsyncUpload;

    // Get the Uploaded files
    UploadedFileCollection uploadedFiles = asyncUpload.UploadedFiles;

    // Create a new list of strings
    List<string> fileNamesList = new List<string>();

    // Loop through each uploaded file
    foreach (UploadedFile fileToUpload in uploadedFiles)
    {
        // add the filename to the list of strings
        fileNamesList.Add(GetNewFileName(fileToUpload.FileName, orderId));
    }

    // Finally, concatenate all values separated by a coma and update the database field with the new string
    newValues["Attachments"] = string.Join(",", fileNamesList.ToArray()); // Output: File1,File2,File3, etc


    changedRows[0].BeginEdit();
    try
    {
        foreach (DictionaryEntry entry in newValues)
        {
            changedRows[0][(string)entry.Key] = entry.Value;
        }
        changedRows[0].EndEdit();
    }
    catch (Exception ex)
    {
        changedRows[0].CancelEdit();
        Label1.Text += string.Format("Unable to update Orders. Reason: {0}", ex.Message);
        e.Canceled = true;
        return;
    }
}

// DELETE
protected void RadGrid1_DeleteCommand(object sender, GridCommandEventArgs e)
{
    GridDataItem dataItem = e.Item as GridDataItem;
    string ID = dataItem.GetDataKeyValue("OrderID").ToString();

    if (SessionDataSource.Rows.Find(ID) != null)
    {
        SessionDataSource.Rows.Find(ID).Delete();
    }
}
#endregion

#region DataSource
private DataTable OrdersTable()
{
    DataTable dt = new DataTable();

    dt.Columns.Add(new DataColumn("OrderID", typeof(int)));
    dt.Columns.Add(new DataColumn("OrderDate", typeof(DateTime)));
    dt.Columns.Add(new DataColumn("Freight", typeof(decimal)));
    dt.Columns.Add(new DataColumn("ShipName", typeof(string)));
    dt.Columns.Add(new DataColumn("ShipCountry", typeof(string)));
    dt.Columns.Add(new DataColumn("Attachments", typeof(string)));

    dt.PrimaryKey = new DataColumn[] { dt.Columns["OrderID"] };

    for (int i = 0; i < 3; i++)
    {
        int index = i + 1;

        DataRow row = dt.NewRow();

        row["OrderID"] = index;
        row["OrderDate"] = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0).AddHours(index);
        row["Freight"] = index * 0.1 + index * 0.01;
        row["ShipName"] = "Name " + index;
        row["ShipCountry"] = "Country " + index;

        dt.Rows.Add(row);
    }

    return dt;
}
#endregion


protected string GetLinks(string filePaths)
{
    string[] paths = filePaths.Split(',');

    List<string> links = new List<string>();

    foreach (string path in paths)
    {
        links.Add(string.Format("<a href='{0}'>{1}</a>", "link", "filename"));
    }

    return string.Empty;
}

protected void RadGrid1_ItemDataBound(object sender, GridItemEventArgs e)
{
    // ItemTemplate
    if (e.Item is GridDataItem)
    {
        GridDataItem item = (GridDataItem)e.Item;

        // Get the data key value
        int orderId = (int)item.GetDataKeyValue("OrderID");

        // find the related row in the database
        DataRow foundItem = SessionDataSource.Select(String.Format("OrderID = '{0}'", orderId)).FirstOrDefault();

        // get the list of file paths by splitting the values separated by coma
        List<string> filePaths = foundItem["Attachments"].ToString().Split(',').Select(path => path.Trim())
                                                                        .Where(path => !string.IsNullOrEmpty(path)).ToList();

        if (filePaths.Count > 0)
        {
            // access the placeholder 
            PlaceHolder ph1 = item.FindControl("PlaceHolder1") as PlaceHolder;

            // loop through each path
            foreach (string filePath in filePaths)
            {
                // Check if the file exists on the specific path
                if (File.Exists(Server.MapPath("~/Attachments/" + filePath)))
                {
                    // if so create Hyperlinks pointing to the Path
                    HyperLink link = new HyperLink();

                    link.NavigateUrl = "/Attachments/" + filePath;
                    link.Text = filePath;
                    link.Target = "_blank";

                    // add the hyperlink to the PlaceHolder
                    ph1.Controls.Add(link);
                }
                else
                {
                    // Notify user the file is missing from server
                    ph1.Controls.Add(new Literal() { Text = filePath + " (missing)" });
                }

                // add line breaks to display each record in a new line
                ph1.Controls.Add(new Literal() { Text = "</br>" });
            }
        }
    }
}
// Upload the Files to a Specific Directory
protected void RadAsyncUpload1_FileUploaded(object sender, FileUploadedEventArgs e)
{
    RadAsyncUpload asyncUpload = (RadAsyncUpload)sender;

    // NamingContainer is the Grid's EditForm since the AsyncUpload is located inside the EditItemTemplate
    GridEditFormItem editFormItem = asyncUpload.NamingContainer as GridEditFormItem;

    int orderId = 1;

    // If Inserting there is no existing records for which we can extract the DataKeyValue
    if (editFormItem is GridEditFormInsertItem)
    {
        // This example demonstrates in-memory editing to avoid setting up a database.
        // In case of databases a new primary key value will be generated for inserted record
        DataRow[] allValues = SessionDataSource.Select("OrderID = MAX(OrderID)");

        if (allValues.Length > 0)
        {
            orderId = int.Parse(allValues[0]["OrderID"].ToString()) + 1;
        }
    }
    else
    {
        // For existing records, we can access the DataKeyValue
        orderId = (int)editFormItem.GetDataKeyValue("OrderID");
    }

    // Set the path where the Files will be uploaded to
    string newFilePath = string.Format("{0}/{1}", Server.MapPath("~/Attachments"), GetNewFileName(e.File.FileName, orderId));

    e.File.SaveAs(newFilePath); // Save the File to the new path/directory
}

// Helper function to format the File name.
private string GetNewFileName(string fileName, int orderId)
{
    // Include OrderID in the Filename
    return string.Format("OrderID_{0}_File_{1}", orderId, fileName);
}
In this article
DESCRIPTIONSOLUTIONComplete Code
Not finding the help you need?
Contact Support