Does Telerik Reporting support Business Object Datasources? - asp.net

I have a requirement to use business objects to call strongly typed table adapters in a three tier model. I also have a requirement to use Telerik reporting, which I didn't see any examples of online. I just see simple examples of creating a simple select to Northwind and connecting the report directly to that. I am using 2008 Q3 of Telerik reporting and my designer doesn't even look the same as the videos I've seen. I have two business objects: boReportHeader and boReportLines and they each have 4 methods that will be passed parameters from the Ui and need to fill the report header and detail section. I spent some time trying to use these from Telerik reporting from both the designer and code-behind and I haven't been successful. I was assuming this would be as straight forward as the RadGrid, but it doesn't appear to be. Anyone have experience with using multiple business object data sources with parameters as a datasource for Telerik Reporting? The main requirement for this project is to generate a PDF file that will be stored in the database as a BLOB file. If this is not possible with Telerik Reporting, does anyone have another tool to suggest other than Telerik Reporting?

Yes You can. See the code below.
namespace TelerikReporting {
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using Telerik.Reporting;
using Telerik.Reporting.Drawing;
public partial class Rep2 : Telerik.Reporting.Report {
public static int GetTotal(int male, int female) {
return (male + female);
}
public Rep2() {
InitializeComponent();
// I am initializing my DataClass.
MyData d = new MyData();
// Adding the DataSource.
this.DataSource = d.GetCityMFCount();
}
}
}
Finally, You call this report in the report Viewer.
protected void Page_Load(object sender, EventArgs e) {
Rep2 rep = new Rep2();
ReportViewer1.Report = rep;
}
Hope this Helps.
Liby George

Related

Error in Silverlight project in ASP.NET with LINQ

I want to make a simple Silverlight application in ASP.NET and LINQ. I have two talbe
Student :[student_id,student_name,address, phone,country_id] Country
:[country_id,country_name]
Thiw tow table is join by country_id.
I have inluced a LINQ Data Class in my project.
I have included a Silverlight-Enabled-WCF-Serfice. In this service I have made tow method and there code is like
[OperationContract]
public List<Country> LoadCountry()
{
var result = from coun in oLINQDataClassesDataContext.Countries
select coun;
return result.ToList();
}
[OperationContract]
public IList<Student> LoadStudent()
{
var result = from std in oLINQDataClassesDataContext.Students
select std;
return result.ToList();
}
Then I add a service reference of that WCF service. Then I include a DataGrid in my silverlight .xml file.
Now I want to show all the students in that DataGrid. For this I have written the following code
WCFServiceReference.WCFServiceClient oWCFServiceClient = new WCFServiceReference.WCFServiceClient();
public Home()
{
InitializeComponent();
oWCFServiceClient.LoadStudentCompleted += new EventHandler<WCFServiceReference.LoadStudentCompletedEventArgs>(oWCFServiceClient_LoadStudentCompleted);
oWCFServiceClient.LoadStudentAsync();
}
void oWCFServiceClient_LoadStudentCompleted(object sender, WCFServiceReference.LoadStudentCompletedEventArgs e)
{
dataGrid1.ItemsSource = e.Result;
}
Then I build the whole project and found no error. If I run the project then I found an error and it is--
An exception occurred during the operation, making the result invalid.
Check InnerException for exception details. at
System.ComponentModel.AsyncCompletedEventArgs.RaiseExceptionIfNecessary()
at
Silverlight.WCFServiceReference.LoadStudentCompletedEventArgs.get_Result()
at Silverlight.Home.oWCFServiceClient_LoadStudentCompleted(Object
sender, LoadStudentCompletedEventArgse) at
Silverlight.WCFServiceReference.WCFServiceClient.OnLoadStudentCompleted(Object
state)
If I remove county table form the LINQ class and remove LoadCountry() method form the service and call LoadStudent() method form silverlight form then it runs accurately and all the data is displayed in my DataGrid.
If I remove student table form the LINQ class and remove LoadStudent() method form the service then LoadCountry() method runs accurately. Both methods are not work if the present same time in LINQ & WCF Service .
NB: Both tables has data. If I run a SQL join query then it returns data
I can’t understand what the problem is.
Is there anyone to help me regarding this problem?
Thanks in advance.
Rashed
go to properties of dbml file and change the Serialization mode to unidirectional

ASP.NET 2.0, SQL Server Express 2008, Sync Framework 1.0 C# Simplest Scenario Example

Good Day Everyone,
I know, the versions I am talking about are rather obsolete by now but that's the tools I am stuck to work with at the workplace. This is my first question on StackOverflow, and I hope I will get the formatting right hehe ;-) Please pardon me for the long text, I am used to give a lot of details and, in a sense, I feel the more details I provide the more accurate might be the answers ;-)
In almost 10 years working in IT, I've always been able to find answers to my questions (i.e. solutions to my problems) by Googling well chosen keywords and expressions. Well, it looks like the aforementioned Sync Framework is either not very well known to the Internet community, or it is a real pain to try to understand its simplest concepts for most mortals. After extensive research I have to found a single, simple example of syncing SQL Express using Sync Framework 1.0 and the C# language, not even on MSDN! I am fairly new to ASP.NET / C# but I understand the concepts and I have a working web application that successfully stores and retrieve data from an SQL Server 2008 database. It has been in use by clients for two years now. We now have a requirement for the clients to be able to bring their data offline and be able to update it offline and then sync with the server. UPDATEs, INSERTs and DELETEs will occur at both ends.
What I am trying to find is VERY simple (or so I thought): C# code examples that uses SQL Server Change Tracking information (NOT custom Change Tracking) to sync the server (SQL Server 2008) and client computers (SQL Server 2008 Express, NOT Compact Edition). The simplest case would be a single table with few columns. I am fairly confident to understand the SQL Server part and I've prepared both sides of the database to receive sync requests from the client web application (enabled Change Tracking, PrimaryKeyID has data type GUID, the application's user account on the server has VIEW_CHANGE_TRACKING permission, etc. etc.)
I know it's the web application that serves as the interface between the two and that manage the sync session (in C#). I was quite naive to think that the only thing left to do was to provide the two connection strings, tell what tables are to sync and specify a bidirectional sync. Apparently, that's more complicated than that hehe. In a desperate attempt, I have tried to base my code on the following from Microsoft and adapt it to SQL Express (the example is for Compact). I am close to admit defeat and shamefully lower my head :-(
http://msdn.microsoft.com/en-us/library/bb726015%28v=sql.100%29.aspx
Based on the above (second section "Complete Example Using SQL Server Change Tracking"), I removed everything I do not need: things related to the password, the statistics, and the code applying changes to the data. I've also removed MS' numerous comment lines for clarity. I've manually applied changes at the SQL Server itself at both ends executing scripts in SSMS (and therefore there must be Change Tracking info that was generated and that is usable when the web app will request the sync). QUESTION 1: Am I wrong in saying so? Finally, I changed some stuff in an attempt to use objects relevant to SQL Express instead of Compact.
QUESTION 2: The code at Microsoft is apparently able to tell whether it is the initial (first) or subsequent sync of this replica. I don't have a clue how it can!
In the end, the code left in its simplest form is as below (with QUESTIONS 3, 4, 5 ;-), but shows some errors. I thank you VERY MUCH in advance for your help. Any comments and/or suggestions are welcome. I am sure if/when this is resolved it will benefit quite a lot of people out there. I will continue to research on it on my end (the boss won't give me a choice ;-) and I promise I will post the solution here if I ever succeed in syncing!
Thanks and have a great day everyone!
Kindest Regards,
Zyxy
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.IO;
//using System.Data.SqlServerCe;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.Server;
//using Microsoft.Synchronization.Data.SqlServerCe;
namespace some_namespace
{
public class SyncProgram
{
public SyncProgram()
{
// empty constructor
}
public static bool MainSync() // Entry point, say, called by a Sync button on an ASPX page.
{
bool boolSyncRes = false; // tells whether sync was a success or not
// Initial sync: they create a new instance of the Orchestrator.
ZyxySyncOrchestrator zyxySyncOrchestrator = new ZyxySyncOrchestrator();
// Subsequent synchronization.
// They don't. there was only irrelevant stats stuff here.
boolSyncRes = true;
return boolSyncRes;
}
}
public class ZyxySyncOrchestrator : SyncOrchestrator
{
public ZyxySyncOrchestrator()
{
Utility util = new Utility();
this.LocalProvider = new ZyxyServerSyncProvider(); // QUESTION 3: ??? cannot implicitly convert type DbServerSyncProvider to Microsoft.Synchronization.SyncProvider
//Instantiate a server synchronization provider and specify it
//as the remote provider for this synchronization agent.
this.RemoteProvider = new ZyxyServerSyncProvider(); // cannot implicitly convert type DbServerSyncProvider to Microsoft.Synchronization.SyncProvider
// QUESTION 4: Is the following code actually creating the base (user) table ZyxySync
// (as opposed to its change tracking metadata table)??
// I wasn't sure whether this part of the code on Microsoft's webpage was part of
// populating the db with sample data and structure or if it's really meant to deal with
// the change tracking metadata.
SyncTable zyxySyncTable = new SyncTable("ZyxySync");
zyxySyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
zyxySyncTable.SyncDirection = SyncDirection.DownloadOnly;
this.Configuration.SyncTables.Add(zyxySyncTable);
}
}
//Create a class that is derived from Microsoft.Synchronization.Server.DbServerSyncProvider.
public class ZyxyServerSyncProvider : DbServerSyncProvider
{
public ZyxyServerSyncProvider()
{
Utility util = new Utility();
SqlConnection serverConn = new SqlConnection(util.ServerConnString);
this.Connection = serverConn;
//Retrieve a new anchor value from the server. We use a timestamp value
//that is retrieved and stored in the client database.
//During each sync the new and last anchor values are used to determine the set of changes
SqlCommand selectNewAnchorCommand = new SqlCommand();
string newAnchorVariable = "#" + SyncSession.SyncNewReceivedAnchor;
selectNewAnchorCommand.CommandText =
"SELECT " + newAnchorVariable + " = change_tracking_current_version()";
selectNewAnchorCommand.Parameters.Add(newAnchorVariable, SqlDbType.BigInt);
selectNewAnchorCommand.Parameters[newAnchorVariable].Direction = ParameterDirection.Output;
selectNewAnchorCommand.Connection = serverConn;
this.SelectNewAnchorCommand = selectNewAnchorCommand;
//Create a SyncAdapter for the ZyxySync table by using
//the SqlSyncAdapterBuilder.
// Specify a name for the SyncAdapter that matches the
// the name specified for the corresponding SyncTable.
SqlSyncAdapterBuilder zyxyBuilder = new SqlSyncAdapterBuilder(serverConn);
zyxyBuilder.TableName = "dbo.ZyxySync";
zyxyBuilder.ChangeTrackingType = ChangeTrackingType.SqlServerChangeTracking;
SyncAdapter zyxySyncAdapter = zyxyBuilder.ToSyncAdapter();
zyxySyncAdapter.TableName = "ZyxySync";
this.SyncAdapters.Add(zyxySyncAdapter);
}
}
// Class derived from Microsoft.Synchronization.Data.Server.DbServerSyncProvider
// QUESTION 5: Or should have I used the two below? I believe they only apply to SQL Compact...
//Microsoft.Synchronization.Data.ClientSyncProvider
//Microsoft.Synchronization.Data.ServerSyncProvider
//http://msdn.microsoft.com/en-us/library/microsoft.synchronization.data.clientsyncprovider%28v=sql.100%29.aspx
//http://msdn.microsoft.com/en-us/library/microsoft.synchronization.data.server.dbserversyncprovider%28d=printer,v=sql.100%29.aspx
public class ZyxyClientSyncProvider : DbServerSyncProvider
{
public ZyxyClientSyncProvider()
{
Utility util = new Utility();
SqlConnection clientConn = new SqlConnection(util.ClientConnString);
this.Connection = clientConn;
}
}
public class Utility
{
public string ClientConnString
{
get { return #"Data Source=localhost\LocalExpressInstance;Initial Catalog=DatabaseName;User ID=UserName;Password=WontTellYou;"; }
}
public string ServerConnString
{
get { return #" Data Source=ServerName\ServerInstance;Initial Catalog=DatabaseName;User ID=UserName;Password=WontTellYou;"; }
}
}
}
the SyncOrchestrator will not work with the DBServerSyncProvider.
In Sync Framework, there are two types of database providers: the offline provider and the peer-to-peer/collaboration providers. (they both work in offline scenario so thats confusing).
the offline provider is used in hub-spoke topologies. Only the client tracks what was synched. The server doesnt even know its part of a sync. this is the same provider used by the Local Database Cache Project item in Visual Studio. The only supported databases out of the box is SqlCeClientSyncProvider and DBServerSyncProvider and uses the SyncAgent to synchronize.
the peer-to-peer provider can be used in a peer-to-peer sync as well as hub-spoke scenarios. Each peer maintains metadata on what was synched. This uses the much newer SyncOrchestrator/SqlCeSyncProvider/SqlSyncProvider(works with SQL Server, Express, LocalDB, and SQL Azure). This uses a custom change tracking.
you can't interchange the providers used by SyncAgent and SyncOrchestrator. You can'r reuse the SQL commands either because they differ in the way they track, select, apply changes and record what was synched.
Ok I managed to make it work so here is a SIMPLE code sample that works (in my case anyway). In addition to the above steps (enabling Change Tracking, setting the right user permissions, etc.), what I did not understand is the following:
1) I found out that I can set it up so that the Sync Framework and sync session is all managed on the client side. Without any dependency on what's installed on the server, I was able to use SF 2.1 instead of the old 1.0. That helped a lot.
2) In preparation for the sync session, one must first PROVISION the database so that it is ready for sync. What I did is to run the following C# with the client connection string (so that it provisions the client db) and then run it again with the server connection string (so that it provisions the server db). This is a run once program (on both sides) to prepare the db. You do NOT run it for every sync session you establish.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Text.RegularExpressions;
using System.Security.Principal;
using System.IO;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.Server;
using Microsoft.Synchronization.Data.SqlServer; // SF 2.1
using Microsoft.Synchronization.SimpleProviders; // SF 2.1
using Microsoft.Synchronization.MetadataStorage; // SF 2.1
// ZYXY: Based on:
// http://msdn.microsoft.com/en-us/library/ff928603.aspx
// NOTES:
// - Microsoft Sync Framework 2.1 redistributable package must be installed on Client computers but is not required on the Server, as long as a server-side synchronization setup is performed by a client computer.
// This is a run once program.
namespace DISS_Database_Sync_Provisioning_Console
{
class Program
{
static void Main(string[] args)
{
SqlConnection sqlConn = new SqlConnection("Data Source=ServerName\\InstanceName;Initial Catalog=SomeDatabase;User ID=SOmeUser;Password=SomePassword;");
Console.Write("Provisioning database...");
// define a new scope named DISS_Sync_Scope
DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("DISS_Sync_Scope");
// get the description of the ZyxySync table
DbSyncTableDescription tableDesc = SqlSyncDescriptionBuilder.GetDescriptionForTable("dbo.ZyxySync", sqlConn);
// add the table description to the sync scope definition
scopeDesc.Tables.Add(tableDesc);
// create a server scope provisioning object based on the DISS_Sync_Scope
SqlSyncScopeProvisioning sqlProvision = new SqlSyncScopeProvisioning(sqlConn, scopeDesc);
// skipping the creation of base table since table already exists
sqlProvision.SetCreateTableDefault(DbSyncCreationOption.Skip);
// start the provisioning process
sqlProvision.Apply();
sqlConn.Close();
sqlConn.Dispose();
Console.Write("\nDatabase has been successfully configured for synchronization. Please press any key to exit.");
Console.Read();
}
}
}
3) Below is the code that is run everytime the synchronization is launched (e.g. when the user click their "Synchronize" button in their web application.)
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Security.Principal;
using System.IO;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.Server;
using Microsoft.Synchronization.Data.SqlServer; // SF 2.1
using Microsoft.Synchronization.SimpleProviders; // SF 2.1
using Microsoft.Synchronization.MetadataStorage; // SF 2.1
namespace diss_ssmb
{
public class SyncProgram
{
public SyncProgram()
{
// empty constructor
}
public static bool MainSync() // Entry point, say, called by a Sync button on an ASPX page.
{
bool boolSyncRes = false; // tells whether sync was a success or not
// Initial sync: they create a new instance of the Orchestrator.
ZyxySyncOrchestrator zyxySyncOrchestrator = new ZyxySyncOrchestrator();
// Subsequent synchronization.
// They don't. there was only irrelevant stats stuff here.
boolSyncRes = true;
return boolSyncRes;
}
}
public class ZyxySyncOrchestrator : SyncOrchestrator
{
public ZyxySyncOrchestrator()
{
Utility util = new Utility();
this.LocalProvider = new ZyxyClientSyncProvider();
//Instantiate a server synchronization provider and specify it
//as the remote provider for this synchronization agent.
this.RemoteProvider = new ZyxyServerSyncProvider();
SyncTable zyxySyncTable = new SyncTable("ZyxySync");
zyxySyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
zyxySyncTable.SyncDirection = SyncDirection.Bidirectional;
// this.Configuration.SyncTables.Add(zyxySyncTable);
this.Synchronize();
}
}
public class ZyxyServerSyncProvider : SqlSyncProvider
{
public ZyxyServerSyncProvider()
{
Utility util = new Utility();
SqlConnection serverConn = new SqlConnection(util.ServerConnString);
this.Connection = serverConn;
this.ScopeName = "DISS_Sync_Scope";
//Retrieve a new anchor value from the server. We use a timestamp value
//that is retrieved and stored in the client database.
//During each sync the new and last anchor values are used to determine the set of changes
SqlCommand selectNewAnchorCommand = new SqlCommand();
string newAnchorVariable = "#" + SyncSession.SyncNewReceivedAnchor;
selectNewAnchorCommand.CommandText =
"SELECT " + newAnchorVariable + " = change_tracking_current_version()";
selectNewAnchorCommand.Parameters.Add(newAnchorVariable, SqlDbType.BigInt);
selectNewAnchorCommand.Parameters[newAnchorVariable].Direction = ParameterDirection.Output;
selectNewAnchorCommand.Connection = serverConn;
// this.SelectNewAnchorCommand = selectNewAnchorCommand; // SF 2.1 commented out because SelectNewAnchorCommand isn't there.
SqlSyncAdapterBuilder zyxyBuilder = new SqlSyncAdapterBuilder(serverConn);
zyxyBuilder.TableName = "dbo.ZyxySync";
zyxyBuilder.ChangeTrackingType = ChangeTrackingType.SqlServerChangeTracking;
SyncAdapter zyxySyncAdapter = zyxyBuilder.ToSyncAdapter();
zyxySyncAdapter.TableName = "ZyxySync";
// this.SyncAdapters.Add(zyxySyncAdapter); // SF 2.1 commented out because SelectNewAnchorCommand isn't there.
}
}
public class ZyxyClientSyncProvider : SqlSyncProvider
{
public ZyxyClientSyncProvider()
{
Utility util = new Utility();
SqlConnection clientConn = new SqlConnection(util.ClientConnString);
this.Connection = clientConn;
this.ScopeName = "DISS_Sync_Scope";
}
}
public class Utility
{
public string ClientConnString
{
get { return #"Some connection string such as in the above code sample"; }
}
public string ServerConnString
{
get { return #"Some serverconnection string such as in the above code sample"; }
}
}
}
4) The above successfully synched bidirectionally when INSERTs, UPDATEs and DELETEs occurred simultaneously at both ends between two consecutive sync session, HOWEVER, when no conflict had to be resolved (e.g. when the same record is updated at both ends). I yet have to do further testing in cases when there are conflicts that have to be resolved. How is the Sync Framework resolving such conflicts by default? I assume we can adjust these settings to tell it to establish the winner based either on...
- a timestamp value
- the replicaID
- the user role
- the transaction type
- ...
Anyways I truly hope that helps somebody, because I had a real hard time to figure it out from the web! Good luck!
Zyxy

How can I emulate ErrorProvider in .NET Compact Framework?

Since there is no ErrorProvider class in .NETCF, how can I implement similar functionality (not necessarily exactly like ErrorProvider)?
I am using all the regular databinding constructs to bind controls to a datatable, using the DataRow.RowError property and DataRow.SetColumnError method, but I can't find events on any of DataTable, BindingManagerBase, etc. that I can hook into to receive any sort of notification.
Am I stuck calling a method to manually iterate through all the controls on my form and change some look/feel of the bound control?
Thanks,
MrB
The ErrorProvider class seems pretty basic - actually, a little too basic. If you have Red Gate Reflector, I would recommend disassembling the class and looking at it. Otherwise, create a Dictionary<Control, String>.
Here is a quick idea on creating your own provider:
Dictionary<Control, String> ErrorSet = new Dictionary<Control, String>();
public void SetError(Control control, String message)
{
// code for adding error information
ErrorSet.Add(control, message);
}
public String GetError(Control control)
{
// code for retrieving error information
return ErrorSet[control];
}
public String Clear()
{
// code for clearing all errors
}
I don't have R-G reflector here or I would provide more sample methods. But this ought to provide some sort of sample to work from.

Creating a lot of .aspx pages to add data to various tables. Is there a better way?

I'm working on a CRUD site with a lot of very similar forms for adding new data. In other words:
AddMovie.aspx, AddGame.aspx, AddBeer.aspx, AddAdd.aspx
I keep thinking to myself, "Self, it would be really nice to have a single Add.aspx instead of re-writing so many similar pages - plus all those OOP nerds would think I'm cool since I'm re-using instead of copy/pasting!"
So assuming I'm on the right track, if I were to go with the single Add.aspx page, how could I represent all sets of fields for each object? I thought about a bunch of panels or divs that I could hide/show, but not sure I really like that solution. Is there a better way to do it or should I just give up and go back to the multiple bad ol' AddObject.aspx pages?
Also, this is a plain ol' (3.5) web forms app. No ASP.NET MVC goodness for this one, sadly. It seems like there should be such a trivial solution and that I'm just over-thinking things, but I can't come up with one and so I turn to Stack Overflow. :)
Maybe you should look at ASP.NET dynamic data or the subsonic project. Both allow to build CRUD-type website very fast because they support "scaffolding" (the edit pages are generated automatically based on your database model).
A better way would perhaps be to have a single page called Add.aspx and then based on the querystring you send it (i.e. Add.asps?type=game) you could customize the form and the logic for the particular type of object your are trying to work with.
Another option is to subclass Page, and then make your pages inherit from it, so you've got one place with all common functionality.
For example:
public abstract class BasePage<T> : System.Web.UI.Page
{
protected T GetObjectById(int objectId)
{
// return new T();
}
protected void SaveObject(T obj)
{
// Save object to DB here
}
protected void DeleteObjectById(int objectId)
{
// Delete object
}
protected abstract void PopulateUI(T obj);
protected override void OnLoad(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
int objectId = Convert.ToInt32(Request.QueryString.Get("id"));
T obj = GetObjectById(objectId);
PopulateUI(obj);
}
}
}
Your pages would then inherit from this:
public class AddGame : BasePage<Game>
{
protected override void PopulateUI(Game game)
{
// Populate the UI with game information
GameNameTextBox.Text = game.Name;
PublisherNameTextBox.Text = game.Publisher.Name;
// etc
}
}
This should make creating the pages much quicker and easier, and gives you a bit more control over how data is retrieved and saved.

Writing JavaScript from a Custom Control

I'm new to writing custom controls. I have MyCustomControl.cs and in my Render method I want to render out about 50 lines of JavaScript. What's the best way to do this, use the writer?
protected override void Render(HtmlTextWriter writer)
{
writer.write(#"<script type....rest of opening tag here");
writer.Write(#"
function decode(s)
{
return s.replace(/&/g, ""&"")
.replace(/"/g, '""')
.replace(/'/g, ""'"")
.replace(/</g, ""<"")
.replace(/>/g, "">"");
};"
);
I plan on having around 6 more writer.Write to write out some more sections here. Is that the best approach to actually perform the writing of JavaScript in this manor?
or should I use ClientScript.RegisterClientScriptBlock? So what's the best practice or common way people are writing javascript from a custom control? (I'm not talking about a user control here!!, custom control/Class!)
I also want to keep any indentation for readability once it's spit out/rendered on the client when viewing source.
The answer I'm providing is just taking regular postbacks into account. All the below can be applied using the ScriptManager and its respective methods to do the same.
There's a couple of ways to do it. You can load a web resource and reference it
// The Scripts namespace in this case would actually be a physical folder in your YourNamespace.CustomControlsNamespace
// namespace.
// Also the /Scripts/YourJavaScriptFile.js needs to have it's Build Action property set to Embedded Resource
[assembly: WebResource("YourNamespace.CustomControlsNamespace.Scripts.YourJavaScriptFile.js", "text/javascript")]
namespace YourNamespace.CustomControlsNamespace
{
public CustomControl()
{
...
}
...
protected override OnPreRender(EventArgs e)
{
...
Type type = typeof(CustomControl);
string scriptUrl = Page.ClientScript.GetWebResourceUrl(type, "Acuity.Web.UI.WebControls.Scripts.accdaterange.js");
string key = "yourKey";
if (!Page.ClientScript.IsClientScriptIncludeRegistered(type, key))
{
control.Page.ClientScript.RegisterClientScriptInclude(type, key, scriptUrl);
}
...
}
...
}
You could also reference an external script in your custom control.
namespace YourNamespace.CustomControlsNamespace
{
public CustomControl()
{
...
}
...
protected override OnPreRender(EventArgs e)
{
...
Type type = typeof(CustomControl);
string scriptUrl = Page.ResolveClientUrl("~/yourScriptsFolder/yourExternalScript.js");
string key = "yourKey";
if (!Page.ClientScript.IsClientScriptIncludeRegistered(type, key))
{
control.Page.ClientScript.RegisterClientScriptInclude(type, key, scriptUrl);
}
...
}
...
}
Depends how you want to package it. The advantage of the embedded resource is that you're guaranteed that this script is always with the assembly that your custom control is in. You will most likely have to add some inline JavaScript to wireup your custom control. Try and do this as little as possible. You can do this using Page.ClientScript.RegisterClientScriptBlock(...). You also want to avoid hard-coding what the auto-generated client Ids are in your scripts. They should be passed in as parameters to client-side objects.
Also, once all looks good, you should compress/minify you external JavaScript files. I use YuiCompressor from Yahoo! but there are several others out there.
You should also invest some time into looking at using a JavaScript framework such as jQuery to do a lot of the grunt work. That's it for now. I might add some more later, but these are my words of wisdom for now.

Resources