How to customize Google charts from code using C#? - c#

I am using google charts for my asp.net web application. I have successfully got the chart working in my application. But I want to know how to add animation, change the color of the column chart from code. In javascript I know it can be done using below option -
var options = {
width: 400,
height: 240,
title: 'Sample Data',
colors: ['#e0440e', '#f6c7b6'],
is3D:true
};
I tried calling the same in c# using string builder but chart itself doesnot load. Here is my code-
private void BindCourseChart()
{
//to bind course chart
DataTable dt = new DataTable();
try
{
dt = GetData_Course();
str.Append(#"<script type=text/javascript> google.load( *visualization*, *1*, {packages:[*corechart*],callback:drawChart});
function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Title');
data.addColumn('number', 'Users enrolled');
**//Here I am adding role=style for customizing**
data.addColumn({type: 'string', role: 'style'});
data.addRows(" + dt.Rows.Count + ");");
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
str.Append("data.setValue( " + i + "," + 0 + "," + "'" + dt.Rows[i] ["Title"].ToString() + "');");
str.Append("data.setValue(" + i + "," + 1 + "," + dt.Rows[i]["MenteeCount"].ToString() + ") ;");
}
str.Append(" var chart = new google.visualization.ColumnChart(document.getElementById('course_div'));");
str.Append(" chart.draw(data, {width: 650, height: 300, title: 'Course Enrollment', color:#0092CB,");
str.Append("hAxis: {title: 'Course Title', titleTextStyle: {color: 'green'}}");
str.Append("}); }");
str.Append("</script>");
lt_course.Text = str.ToString().TrimEnd(',').Replace('*', '"');
}
Can anyone help me in this regard how to add colors,or animation to column charts .
Thanks in advance

If interested, I made a Google Chart class awhile back that I've used in a few projects:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Google
{
/// <summary>
/// Summary description for GoogleChart
/// </summary>
public class GoogleChart
{
// Fields
private string data = "";
private string javascript;
// Properties
public string elementId { get; set; }
public int height { get; set; }
public string title { get; set; }
public int width { get; set; }
// ChartTypes
public enum ChartType
{
BarChart,
PieChart,
LineChart,
ColumnChart,
AreaChart,
BubbleChart,
CandlestickChart,
ComboChart,
GeoChart,
ScatterChart,
SteppedAreaChart,
TableChart
}
// Methods
public GoogleChart()
{
this.title = "Google Chart";
this.width = 730;
this.height = 300;
this.elementId = "chart_div";
}
public void addColumn(string type, string columnName)
{
string data = this.data;
this.data = data + "data.addColumn('" + type + "', '" + columnName + "');";
}
public void addRow(string value)
{
this.data = this.data + "data.addRow([" + value + "]);";
}
public string generateChart(ChartType chart)
{
this.javascript = "<script type=\"text/javascript\" src=\"https://www.google.com/jsapi\"></script>";
this.javascript = this.javascript + "<script type=\"text/javascript\">";
this.javascript = this.javascript + "google.load('visualization', '1.0', { 'packages': ['corechart']});";
this.javascript = this.javascript + "google.setOnLoadCallback(drawChart);";
this.javascript = this.javascript + "function drawChart() {";
this.javascript = this.javascript + "var data = new google.visualization.DataTable();";
this.javascript = this.javascript + this.data;
this.javascript = this.javascript + "var options = {";
this.javascript = this.javascript + "'title': '" + this.title + "',";
object javascript = this.javascript;
this.javascript = string.Concat(new object[] { javascript, "'width': ", this.width, ", 'height': ", this.height, "};" });
string str = this.javascript;
this.javascript = str + "var chart = new google.visualization." + chart.ToString() + "(document.getElementById('" + this.elementId + "'));";
this.javascript = this.javascript + "chart.draw(data, options);";
this.javascript = this.javascript + "} </script>";
return this.javascript;
}
}
}
You can then use it by doing the following:
private void GenerateQuickStats()
{
GoogleChart chart = new GoogleChart();
chart.title = "Quick Stats";
chart.width = 250;
chart.height = 200;
chart.addColumn("string", "Year");
chart.addColumn("number", "Value");
chart.addColumn("number", "Profit");
chart.addRow("'2014', 2000, 1000");
// asp literal
ltChart.Text = chart.generateChart(GoogleChart.ChartType.ColumnChart);
}

Since David addressed the color issue, I'll tackle the animations. Animating the chart on first draw is a bit complicated; you have to draw it once with a zero'd out data set, and then draw it again with the real data set. Here's some javascript (that you can add to your string builder, replacing the chart creation and drawing lines) that will animate the chart:
var view = new google.visualization.DataView(data);
view.setColumns([0, {
sourceColumn: 1,
calc: function () {return 0;}
}, 2]);
var chart = new google.visualization.ColumnChart(document.getElementById('course_div'));
google.visualization.events.addOneTimeListener(chart, 'ready', function () {
chart.draw(data, {
width: 650,
height: 300,
title: 'Course Enrollment',
color: '#0092CB',
hAxis: {
title: 'Course Title',
titleTextStyle: {
color: 'green'
}
},
animation: {
duration: 1000
}
});
});
chart.draw(view, {
width: 650,
height: 300,
title: 'Course Enrollment',
color: '#0092CB',
hAxis: {
title: 'Course Title',
titleTextStyle: {
color: 'green'
}
},
animation: {
duration: 1000
}
});
[Edit - attempt at C# example]
private void BindCourseChart()
{
//to bind course chart
DataTable dt = new DataTable();
try
{
dt = GetData_Course();
str.Append(#"
<script type=text/javascript>
google.load( *visualization*, *1*, {packages:[*corechart*], callback:drawChart});
function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Title');
data.addColumn('number', 'Users enrolled');
**//Here I am adding role=style for customizing**
data.addColumn({type: 'string', role: 'style'});
data.addRows(" + dt.Rows.Count + ");
");
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
str.Append("data.setValue( " + i + "," + 0 + "," + "'" + dt.Rows[i] ["Title"].ToString() + "');");
str.Append("data.setValue(" + i + "," + 1 + "," + dt.Rows[i]["MenteeCount"].ToString() + ") ;");
}
str.Append(#"
var view = new google.visualization.DataView(data);
view.setColumns([0, {
sourceColumn: 1,
calc: function () {return 0;}
}, 2]);
var chart = new google.visualization.ColumnChart(document.getElementById('course_div'));
google.visualization.events.addOneTimeListener(chart, 'ready', function () {
chart.draw(data, {
width: 650,
height: 300,
title: 'Course Enrollment',
color: '#0092CB',
hAxis: {
title: 'Course Title',
titleTextStyle: {
color: 'green'
}
},
animation: {
duration: 1000
}
});
});
chart.draw(view, {
width: 650,
height: 300,
title: 'Course Enrollment',
color: '#0092CB',
hAxis: {
title: 'Course Title',
titleTextStyle: {
color: 'green'
}
},
animation: {
duration: 1000
}
});
}
</script>
");
lt_course.Text = str.ToString().TrimEnd(',').Replace('*', '"');
}

I believe you are missing quotation marks around your color...
At the end of this line:
str.Append(" chart.draw(data, {width: 650, height: 300, title: 'Course Enrollment', color:#0092CB,");
you need apostrophes around the hex color:
str.Append(" chart.draw(data, {width: 650, height: 300, title: 'Course Enrollment', color:'#0092CB',");
If that doesn't work, compare the javascript that your app generates with your manually created code.

Related

Blazor with JsRuntine Interop

I'm trying to invoke a javascript function in a Blazor component, but with no success.
The error is:
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Could not find 'FullCalendarInterop.init' ('FullCalendarInterop' was undefined).
Error: Could not find 'FullCalendarInterop.init' ('FullCalendarInterop' was undefined).
at https://localhost:7065/_framework/blazor.webassembly.js:1:328
at Array.forEach ()
at a.findFunction (https://localhost:7065/_framework/blazor.webassembly.js:1:296)
at _ (https://localhost:7065/_framework/blazor.webassembly.js:1:2437)
at https://localhost:7065/_framework/blazor.webassembly.js:1:3325
at new Promise ()
Component structure:
Razor page:
#using Microsoft.JSInterop
#inject IJSRuntime JSRuntime
protected override async Task OnAfterRenderAsync(bool firstRender)
{
var options = new CalendarOptions
{
Id = Id,
DefaultView = View,
CalendarEvents = Events.Where(r => r.Status == CalendarEventStatus.Active).ToList()
};
var dotNetObjectReference = DotNetObjectReference.Create(this);
var fullCalendarInterop = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./Shared/InputCalendar.razor.js");
await fullCalendarInterop.InvokeVoidAsync("FullCalendarInterop.init", options, dotNetObjectReference);
// I already just tried^
// await JSRuntime.InvokeVoidAsync("FullCalendarInterop.init", options, DotNetObjectReference.Create(this));
await base.OnAfterRenderAsync(firstRender);}
Javascript
var FullCalendarInterop = function () {
return {
//main function to initiate the module
init: function (options, dotNetReference) {
var calendarEl = document.getElementById(options.id);
var calendar = new window.FullCalendar.Calendar(calendarEl, {
plugins: ['interaction', 'dayGrid', 'timeGrid', 'list'],
isRTL: window.KTUtil.isRTL(),
header: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek'
},
height: 800,
contentHeight: 750,
aspectRatio: 3, // see: https://fullcalendar.io/docs/aspectRatio
views: {
dayGridMonth: { buttonText: 'month' },
timeGridWeek: { buttonText: 'week' },
timeGridDay: { buttonText: 'day' },
listWeek: { buttonText: 'list' }
},
defaultView: options.defaultView,
defaultDate: options.defaultDate,
editable: true,
eventLimit: true, // allow "more" link when too many events
navLinks: true,
events: options.calendarEvents,
eventRender: function (info) {
var event = $(info.event);
var element = $(info.el);
var view = $(info.view);
if (info.event.extendedProps && info.event.extendedProps.description) {
if (element.hasClass('fc-day-grid-event')) {
element.data('content', info.event.extendedProps.description);
element.data('placement', 'top');
window.KTApp.initPopover(element);
} else if (element.hasClass('fc-time-grid-event')) {
element.find('.fc-title').append('<div class="fc-description">' + info.event.extendedProps.description + '</div>');
} else if (element.find('.fc-list-item-title').lenght !== 0) {
element.find('.fc-list-item-title').append('<div class="fc-description">' + info.event.extendedProps.description + '</div>');
}
}
element.find(".fc-bg").css("pointer-events", "none");
element.find(".fc-list-item-title.fc-widget-content").prepend(
"<span style='position: absolute; right: 5px;'>" +
"<button class='btn btn-icon btn-xs btn-circle btn-light' " +
"style='height: 16px; width: 16px;' id='calendar_del_" + event.id + "'>" +
"<i class='icon-xs text-dark-50 flaticon2-cross'></i></button></span>");
element.find(".fc-content").append("<span style='position: absolute; top: 5px; right: 5px;'>" +
"<button class='btn btn-icon btn-xs btn-circle btn-light' " +
"style='height: 16px; width: 16px;' id='calendar_del_" + event.id + "'>" +
"<i class='icon-xs text-dark-50 flaticon2-cross'></i></button></span>");
element.find("#calendar_del_" + event.id).click(function () {
var eventId = event[0]._def.defId;
var eventIdentifier = event[0]._def.extendedProps.identifier;
//Remove popover
removeContent(".popover.fade.show.bs-popover-top");
//$(".popover.fade.show.bs-popover-top").remove();
dotNetReference.invokeMethodAsync('CalendarEventDeleted', eventIdentifier);
//$("#candidate_calendar").fullCalendar('removeEvents', eventId);
});
},
viewSkeletonRender: function (info) {
var view = $(info.view);
var defaultView = view[0].type !== null ? view[0].type : "";
dotNetReference.invokeMethodAsync('SetDefaultView', defaultView);
//alert(defaultView);
}
});
calendar.render();
}
};
}
Modify your .JS file like so:
var FullCalendarInterop = function () {
return {
//main function to initiate the module
init: function (options, dotNetReference) {
... Trimmed for brevity ...
calendar.render();
}
};
}();
export { FullCalendarInterop };
I added (); to the end of your FullCalendarInterop function and then added the export. That should work for you.
As it says its a null reference error as you are not initializing JSmodule,
IJSObjectReference fullCalendarInterop;
fullCalendarInterop = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./Shared/InputCalendar.razor.js");

Populate chart.js from data table / SQL result set

Based on an SQL result set, I intend to populate a data table, meant to serve as datasource for a chart (using chart.js).
Any idea on how should I make this work using C# / ASP.NET as I did not found any working examples?
Many thanks
I've created the following Javascript method which is called from the backend
function FillBarChart(names, values, canvasId, graphTitle, labels) {
var color = Chart.helpers.color;
var colors = '#' + Math.floor(Math.random() * 16777215).toString(16);
var collors = 'hsla(' + (Math.floor(Math.random() * 360) + ', 70%, 70%, 1)');
var chartData = {
labels: names.split(','),
datasets: [
{
label: labels,
backgroundColor: collors,
borderColor: color("#0061ff"),
borderWidth: 1,
data: values.split(',')
}
]
};
var ctx = document.getElementById(canvasId).getContext('2d');
window.barChart = new Chart(ctx, {
type: 'bar',
data: chartData,
options: {
responsive: true,
legend: {
position: 'top',
},
title: {
display: true,
text: graphTitle
}
}
});
}
The C# lines that trigger this are the following:
StringBuilder javascriptCommands = new StringBuilder();
HtmlGenericControl canvas = new HtmlGenericControl();
canvas.TagName = "canvas";
canvas.Attributes["Height"] = CanvasHeight;
canvas.ClientIDMode = ClientIDMode.Static;
canvas.ID = "canvasSales";
string graphNames = "Previous Year,Current Year";
string graphValues = MainSession.salesPY + "," + MainSession.salesCY;
charts.Controls.Add(canvas);
javascriptCommands.Append("FillBarChart(`" + graphNames + "`,`" + graphValues + "`,`" + canvas.ID + "`,`" + "Grand Total Sales" + "`,`" + "Total Sales Graph" + "`);");
ScriptManager.RegisterStartupScript(Page, Page.GetType(), "mykey", javascriptCommands.ToString(), true);
MainSession.salesPY and MainSession.salesCY are the needed values, brought from SQL in my case and concatenated as strings, sepparated with a comma.

Get next 8 rows from database on button click using linq to sql and web method

I am creating a webpage in which I fetch top 8 rows from database on page load. I put load more button on my bottom of my web page. What I want is when I click on load more button it shows me next new 8 rows and skip previous records and if there is no new record found then show me nothing.
Below is my code which I was trying but it was repeating same duplicate records.
//Below event is fetching top 8 rows on page load
function viewAllEvents() {
$.ajax({
url: "Event.aspx/viewEvents",
data: null,
contentType: "Application/json; charset=utf-8",
responseType: "json",
method: "POST",
success: function (response) {
var x = response.d;
for (var i = 0; i < x.length; i++) {
$("#tabss > .event-container > .row").append(
"<div class='col-md-3'><div class='event'><div class='eventsimg'><img src= " + '../MediaUploader/' + x[i].EVE_IMG_URL + " alt=''></div><div class='event-content'><h3 class='title'>" + x[i].EVE_NAME + "</h3><p>" + x[i].EVE_DESCRIPTION + "</p><input type='button' id=" + i + " class='btn btn-pri' style='padding: 9px 9px;font-size: 12px;' onClick='eveReq(" + i + ", " + x[i].ID + ", " + x[i].EVE_CAT_ID + ");' value='Send Request' /><input type='button' class='btn btn-pri' style='padding: 9px 9px;font-size: 12px;margin-left: 2px;' value='Read More' /></div><div class='links clearfix'></div></div></div>"
);
}
},
error: function (xhr) {
alert(xhr.status);
},
Failure: function (response) {
alert(response);
}
});
}
//Below event is for when load more button is clicked
function addTabs() {
$.ajax({
url: "Event.aspx/addTab",
data: null,
contentType: "Application/json; charset=utf-8",
responseType: "json",
method: "POST",
success: function (response) {
var x = response.d;
for (var i = 0; i < x.length; i++) {
$("#tabss > .event-container > .row").append(
"<div class='col-md-3'><div class='event'><div class='eventsimg'><img src= " + '../MediaUploader/' + x[i].EVE_IMG_URL + " alt=''></div><div class='event-content'><h3 class='title'>" + x[i].EVE_NAME + "</h3><p>" + x[i].EVE_DESCRIPTION + "</p><input type='button' id=" + i + " class='btn btn-pri' style='padding: 9px 9px;font-size: 12px;' onClick='eveReq(" + i + ", " + x[i].ID + ", " + x[i].EVE_CAT_ID + ");' value='Send Request' /><input type='button' class='btn btn-pri' style='padding: 9px 9px;font-size: 12px;margin-left: 2px;' value='Read More' /></div><div class='links clearfix'></div></div></div>"
);
}
},
error: function (xhr) {
alert(xhr.status);
},
Failure: function (response) {
alert(response);
}
});
}
Below is my web methods:
[WebMethod]
public static List<EVENT> viewEvents()
{
var slist = new List<EVENT>();
var db = new BLUEPUMPKINEntities();
db.Configuration.LazyLoadingEnabled = false;
slist = db.EVENTS.OrderByDescending(eve => eve.ID).Take(8).ToList();
return slist;
}
[WebMethod]
public static List<EVENT> addTab()
{
var slist = new List<EVENT>();
var db = new BLUEPUMPKINEntities();
db.Configuration.LazyLoadingEnabled = false;
slist = db.EVENTS.OrderByDescending(eve => eve.ID).Skip(8).Distinct().ToList();
return slist;
}
Though I didn't use your code but I believe if you can go through this example below then you'll know what to do:
First declare a global variable which will count the record returned so far:
private int recordCount = 0;
Then in the click event do the following:
//My sample data
int[] data = { 1, 2, 3, 4, 5, 6, 7, 8 };
var results = data.Skip(recordCount).Take(2);
//Increment recordCount by the count of the results return above
recordCount+= results.Count();
if (results.Count() > 0)
{
//return results
}

Why my code is repeated in async upload file web api

In upload async file :
for example if i uploading 3 file Debug.WriteLine(info.FullName); and _db.Files.Add(New FileDto{FileName=info.Name}); repeated 50 time.means 50 record insert in database.
Why and how to avoid it?
what is my problem?
public Task<IEnumerable<FileDesc>> Post()
{
string folderName = "uploads";
string PATH = HttpContext.Current.Server.MapPath("~/" + folderName);
string rootUrl = Request.RequestUri.AbsoluteUri.Replace(Request.RequestUri.AbsolutePath, String.Empty);
if (Request.Content.IsMimeMultipartContent())
{
var streamProvider = new CustomMultipartFormDataStreamProvider(PATH);
var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith<IEnumerable<FileDesc>>(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
var fileInfo = streamProvider.FileData.Select(i =>
{
var info = new FileInfo(i.LocalFileName);
Debug.WriteLine(info.FullName);//************Repeated******
_db.Files.Add(New FileDto{FileName=info.Name});//************Repeated******
return new FileDesc(info.Name, rootUrl + "/" + folderName + "/" + info.Name, info.Length / 1024);
});
return fileInfo;
});
return task;
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
}
}
[Update]
CustomMultipartFormDataStreamProvider :
public class CustomMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
public CustomMultipartFormDataStreamProvider(string path)
: base(path)
{
}
public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
{
var name = !string.IsNullOrWhiteSpace(headers.ContentDisposition.FileName) ? headers.ContentDisposition.FileName : "NoName";
return name.Replace("\"", string.Empty);
}
}
[Update]
$('#fileupload').fileupload({
dataType: "json",
url: "/media/upload",
limitConcurrentUploads: 1,
sequentialUploads: true,
progressInterval: 100,
maxChunkSize: 10000,
add: function (e, data) {
$('#filelistholder').removeClass('hide');
data.context = $('<div />').text(data.files[0].name).appendTo('#filelistholder');
$('<div class="progress progress-striped active"><div style="width: 0%;" class="progress-bar progress-bar-warning"></div></div>').appendTo(data.context);
$('#btnUploadAll').click(function () {
data.submit();
});
},
done: function (e, data) {
data.context.text(data.files[0].name + '... Completed');
$('<div class="progress progress-striped active"><div style="width: 100%;" class="progress-bar progress-bar-success"></div></div>').appendTo(data.context);
},
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
$('#overallbar').css('width', progress + '%');
},
progress: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
data.context.find('.progress-bar').css('width', progress + '%');
}
});
Your Debug.WriteLine is in the projection function of streamProvider.FileData.Select, so it is executed as many times as there are items in streamProvider.FileData.

ajaxToolkit:AjaxFileUpload capture file description

I'm trying to get the FileDescription asp:textbox to save into a db when the user clicks upload but it's coming back blank. what am I doing wrong?
this is in my upload.aspx file
<ajaxToolkit:AjaxFileUpload ID="AjaxFileUpload1"
ThrobberID="myThrobber" OnUploadComplete="AjaxFileUpload1_UploadComplete"
ContextKeys=""
AllowedFileTypes="jpg,jpeg,doc,xls"
MaximumNumberOfFiles="1"
runat="server"/>
</div>
File Description<asp:TextBox ID="FileDescription" Width="200" runat="server" ></asp:TextBox>
and this is in my upload.cs file
protected void AjaxFileUpload1_UploadComplete(object sender, AjaxControlToolkit.AjaxFileUploadEventArgs e)
{
string sFileDescription = FileDescription.Text;
string filePath = "~/" + e.FileName;
AjaxFileUpload1.SaveAs(filePath);
}
Let's add individual descriptions for each uploaded file. To do this you need to download AjaxControlToolkit sources from Codeplex (here is a link to download: Latest toolkit sources and modify three files:
AjaxFileUpload.Item.pre.js
AjaxFileUpload.Control.pre.js
AjaxFileUpload.css
New content of the AjaxFileUpload.Item.pre.js file:
/// <reference path="../../../client/microsoftajax.extended/common/common.pre.js" />
Type.registerNamespace("Sys.Extended.UI.AjaxFileUpload");
Type.registerNamespace("AjaxFileUpload");
Sys.Extended.UI.AjaxFileUpload.Item = function (parentId, fileItem, onRemoveItem) {
this._deleteButton = null;
this._parentId = parentId;
this._inputElementValue = fileItem.value;
this._id = fileItem.id;
this._slices = fileItem.slices;
this._sliceIndex = 0;
this._fileInfoContainer = null;
this._fileStatusText = null;
this._isUploaded = false;
this._isUploading = false;
this._fileSize = 0;
this._fileName = "";
this._fileType = "";
this._bytesUploaded = 0;
this._fileComment = null;
this._ui = this.initUI(onRemoveItem);
};
Sys.Extended.UI.AjaxFileUpload.Item.prototype = {
initUI: function (onRemoveItem) {
var self = this, file = this._inputElementValue, utils = new Sys.Extended.UI.AjaxFileUpload.Utils(),
isHtml5Support = utils.checkHtml5BrowserSupport(),
// generate unique id for each item
id = this._id,
// create line item container
container = $common.createElementFromTemplate({
nodeName: "div",
properties: {
id: this._parentId + '_FileItemContainer_' + id,
},
cssClasses: ['ajax__fileupload_fileItemInfo']
}),
// create file info/status container
fileInfoContainer = $common.createElementFromTemplate({
nodeName: "div",
properties: {
id: this._parentId + '_FileInfoContainer_' + id,
style: {
display: 'inline-block'
}
}
}),
// create file info placeholder
fileInfoText = $common.createElementFromTemplate({
nodeName: "span",
properties: {
id: this._parentId + '_FileItemInfo_' + id
},
cssClasses: ['ajax__fileupload_fileItemInfo']
}),
// create file status placeholder
fileStatusText = $common.createElementFromTemplate({
nodeName: "span",
properties: {
id: this._parentId + '_FileItemStatus_' + id
},
cssClasses: ['uploadstatus']
}),
commentContainer = $common.createElementFromTemplate({
nodeName: 'div',
cssClasses: ['ajax__fileupload_fileItem_commentContainer']
}),
fileComment = $common.createElementFromTemplate({
nodeName: "input",
properties: {
id: this._parentId + '_FileItemComment_' + id,
type: 'text',
style: {
width: '100%'
}
},
cssClasses: ['ajax__fileupload_fileItem_commentInput']
}),
// init remove button
deleteButton = $common.createElementFromTemplate({
nodeName: "div",
properties: {
id: this._parentId + '_FileItemDeleteButton_' + id
},
cssClasses: ['removeButton']
});
this._fileName = utils.getFileName(file);
if (isHtml5Support) {
this._fileSize = file.size;
var fType = file.type ? '<span class="filetype">(' + file.type + ')</span>' : '';
fileInfoText.innerHTML = '<span class="filename">' + this._fileName + '</span> '
+ fType
+ ' - <span class="filesize">' + utils.sizeToString(file.size) + '</span> ';
this._fileType = file.type;
} else {
fileInfoText.innerHTML = '<span class="filename">' + this._fileName + '</span>';
this._fileType = utils.getFileType(file);
}
commentContainer.innerHTML = '<label class="ajax__fileupload_fileItem_commentLabel" for="' + this._parentId + '_FileItemComment_' + id + '">Description:</label>';
commentContainer.appendChild(fileComment);
fileInfoContainer.appendChild(fileInfoText);
fileInfoContainer.appendChild(fileStatusText);
fileInfoContainer.appendChild(commentContainer);
$common.setText(deleteButton, Sys.Extended.UI.Resources.AjaxFileUpload_Remove);
$addHandlers(deleteButton, {
'click': Function.createDelegate(this, function () {
onRemoveItem(self);
})
});
// build the line item
if (Sys.Browser.agent == Sys.Browser.InternetExplorer && Sys.Browser.version <= 8) {
container.appendChild(deleteButton);
container.appendChild(fileInfoContainer);
}
else {
container.appendChild(fileInfoContainer);
container.appendChild(deleteButton);
}
this._fileInfoContainer = fileInfoContainer;
this._deleteButton = deleteButton;
this._fileStatusText = fileStatusText;
this._fileComment = fileComment;
return container;
},
setStatus: function (fileStatusText, text) {
$common.setText(this._fileStatusText, ' (' + text + ')');
this._fileInfoContainer.setAttribute('class', fileStatusText + 'State');
},
disabled: function (on) {
if (on)
this._deleteButton.disabled = this._fileComment.disabled = 'disabled';
else
this._deleteButton.disabled = this._fileComment.disabled = '';
},
hide: function () {
this._deleteButton.style.visibility = 'hidden';
this._fileComment.readOnly = true;
},
destroy: function () {
$common.removeElement(this._inputElementValue);
$common.removeElement(this._deleteButton);
$common.removeElement(this._fileComment);
$common.removeElement(this._ui);
},
get_inputElementValue: function () {
return this._inputElementValue;
},
appendNodeTo: function (parent) {
parent.appendChild(this._ui);
},
removeNodeFrom: function (parent) {
parent.removeChild(this._ui);
},
get_fileComment: function () {
return this._fileComment.value;
}
};
In this code added new class field _fileComment, new function get_fileComment and modified initUI, disabled, hide and destroy functions. After these changes, each file item will have individual textbox for file description.
After that, change a bit AjaxFileUpload.Control.pre.js file. Rewrite the doneAndUploadNextFile function as below:
doneAndUploadNextFile: function (fileItem) {
/// <summary>
/// Mark fileItem as uploaded, and upload next file in queue.
/// </summary>
/// <param name="fileItem">Uploaded File</param>
// send message to server to finalize this upload
var xhr = new XMLHttpRequest(),
self = this;
xhr.open("POST", "?contextKey="+ this._contextKey +"&done=1&guid=" + fileItem._id + "&comment=" + fileItem.get_fileComment(), true);
xhr.onreadystatechange = function (e) {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
// Mark as done and invoke event handler
self.raiseUploadComplete(Sys.Serialization.JavaScriptSerializer.deserialize(xhr.responseText));
// Upload next file
self._processor.startUpload();
} else {
// finalizing is error. next file will not be uploaded.
self.setFileStatus(fileItem, 'error', Sys.Extended.UI.Resources.AjaxFileUpload_error);
self.raiseUploadError(xhr);
throw "error raising upload complete event and start new upload";
}
}
};
xhr.send(null);
}
And the last step is a AjaxFileUpload.css file. Change heigh css rile in .ajax__fileupload_fileItemInfo class definition and add three additional classes for description:
.ajax__fileupload_fileItemInfo {
line-height: 20px;
height: 44px;
margin-bottom: 2px;
overflow: hidden;
}
.ajax__fileupload_fileItem_commentContainer {
display: table;
width: 100%;
}
.ajax__fileupload_fileItem_commentLabel {
display: table-cell;
width: 1px;
white-space: nowrap;
padding-right: 5px;
}
.ajax__fileupload_fileItem_commentInput {
display: table-cell;
width: 100%;
}
After these changes rebuild toolkit solution and use custom dlls.
Now you can get posted description from query string in OnUploadComplete event handler: var comment = Request.QueryString["comment"];

Categories

Resources