Writing JavaScript from a Custom Control - asp.net

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.

Related

Ajax Control Toolkit AsyncFileUploader Control and viewstate/session Issue

in my project i need to upload files so i decided to use the uploader provided by asp.net ajax controls AsyncFileUPloader control.
there are four blocks. every block contains two such uploaders
so i decided to utilize the power of asp.net web user controls.
i wrapped the required form fields in my user control called DesignUploader.ascx
now i have to put the four instances of this control on my aspx page
please refer the snap below
my problem starts here i have to insert the fileurl to the database and each of the block generates unique id and id value changes after uploading the file to the server. i noticed that viewstate does not work for me in case of asyncfileuploader it clears the viewstate because it does the secret postback to the server behind the scenes. now only option left for me is to use session but when user uploads the files in two blocks one after another then filepath from second/third consecutive blocks overwrite my session. i don't know how many blocks user can use to upload the designs he may use 1 only or he may use all four.
There would be a final submit button in the bottom of the page on click of which i have to insert data to database.
so when i tried to save the data to database the session stores the value of the recently uploaded file path for all the records my problem lies here
i don't know if i was able to describe my problem in correct manner or not please excuse me if it is not clear and post comment if required.
Note: I can not change the UI because client insists for this only :(
any quick work around would be appreciated much
Thanks
Devjosh
I believe you saving file path to session in a wrong way and it's impossible to recognize where is an error without code.
All the way, in my opinion better don't persist file path in session but use client side for that purpose instead. You can add two hidden fields to DesignUploader.ascx control and set their values in UploadedComplete event handler.
public partial class DesignUploader : System.Web.UI.UserControl
{
private static readonly string AppDataPath = HttpContext.Current.Server.MapPath("~/App_Data/");
public string FirstFilePath
{
get
{
return Server.UrlDecode( FirstFilePathHiddenField.Value);
}
}
public string SecondFilePath
{
get
{
return Server.UrlDecode(SecondFilePathHiddenField.Value);
}
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
FirstFileUpload.UploadedComplete += FirstFileUpload_UploadedComplete;
SecondileUpload.UploadedComplete += SecondileUpload_UploadedComplete;
}
void FirstFileUpload_UploadedComplete(object sender, AjaxControlToolkit.AsyncFileUploadEventArgs e)
{
var fullPath = Path.Combine(AppDataPath, Path.GetFileName(e.FileName));
FirstFileUpload.SaveAs(fullPath);
SaveFilePathToHiddenField(FirstFilePathHiddenField.ClientID, fullPath);
}
void SecondileUpload_UploadedComplete(object sender, AjaxControlToolkit.AsyncFileUploadEventArgs e)
{
var fullPath = Path.Combine(AppDataPath, Path.GetFileName(e.FileName));
SecondileUpload.SaveAs(fullPath);
SaveFilePathToHiddenField(SecondFilePathHiddenField.ClientID, fullPath);
}
private void SaveFilePathToHiddenField(string fieldId, string pathValue)
{
var script = string.Format("top.$get('{0}').value = '{1}';", fieldId, Server.UrlEncode(pathValue));
ScriptManager.RegisterStartupScript(this, this.GetType(), "setPath", script, true);
}
}

Notifying that all properties have changed on a ViewModel

I am working on a Silverlight application using V3 SP1 of MVVM Light Toolkit.
My application is fully French/English. All UI elements (buttons, labels, etc.) and all the data (models). I need dynamic language switching and this is fully implemented and works with anything coming from a resource file. What I am struggling with is the ViewModels.
The Models have language specific prperties (DescriptionEn, DescriptionFr) and an additional property call LocalizedDescription which uses the current culture to return call the language specific property.
When the language changes (via a button click) I raise and broadcast (via the Messenger) a property changed event.
In each of my ViewModels, I register to receive the property changed message for the language swap.
I want to notify all the properties of the ViewModel that something has changed.
From: http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged.aspx
The PropertyChanged event can indicate all properties on the object have changed by using either null or String.Empty as the property name in the PropertyChangedEventArgs.
However, since the toolkit abstracts the raising of the changed event with RaisePropertyChanged(...) I cannot get this to work. I have also examined the source of the tookit and discovered that RaisePropertyChanged calls VerifyPropertyName(..) which in turn returns an error is the property does not belong to the ViewModel. I also noticed that the VerifyPropertyName method is attributed with Conditional("DEBUG"), but even if I choose the Release configuration, the ArgumentException("Property not found") is still raised.
Does anyone know of a way to get this to work using the toolkit aside from manually calling RaisePropertyChanged for every property of the ViewModel?
Follow-up:
Based on the comment from Simon, I attempted to create my own class that extends ViewModelBase. I looked at the source on CodePlex and decided to create a single method called RaiseAllPropertyChanged(). It would simply be a copy of the RaisePropertyChanged(string propertyName) but without the parameter and without the call to VerifyPropertyName(...). I cannot get it to work. Here is what I have.
public class ViewModelBaseExtended : ViewModelBase
{
protected void RaiseAllPropertyChanged()
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(String.Empty));
}
}
}
But I get a compiler error: The event 'GalaSoft.MvvmLight.ViewModelBase.PropertyChanged' can only appear on the left hand side of += or -=. This is a copy of the code that is used in the ViewModelBase.
Can someone offer some advice as to how to get this to work?
Solution:
I copied all the code from ViewModelBase into a new class. I then added the method RaisePropertyChanged() mentioned above which instantiates the PropertyChangedEventArgs class with String.Empty. This is now the new subclass for my ViewModels.
Thanks again to Simon for leading the way!
In case you're reading this in 2016, you can use ObservableObject and notify that all of the properties have changed by doing:
RaisePropertyChanged(string.Empty);
Unfortunately this is not possible with the current code-base of MVVMLight
In the short term your have 2 options:
User your own custom base class. And by custom base class I mean "Do not inherit from the MVVMLight class".
Download and compile MVVMLight in Release mode. This will force the "VerifyPropertyName" method to be excluded. Of course then you don't get the value of property name checks.
I am sure Laurent Bugnion will have this fixed soon.
A lighter solution to this problem would have been to override RaisePropertyChanged(string propertyName) in your class :
protected override void RaisePropertyChanged(string propertyName)
{
if (propertyName != null)
{
base.RaisePropertyChanged(propertyName);
}
else
{
var handler = PropertyChangedHandler;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(null));
}
}
}

Intercept Unity 2.0 HandlerAttribute without an interface

I'm a first-time user of the AOP features of Unity 2.0 and would like some advice. My goal is to be able to log method calls in an ASPX page, like so:
public partial class Page2 : Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
[Log]
private void Testing()
{
}
}
Here is the code for the LogAttribute:
public class LogAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new LogHandler(Order);
}
}
Now the LogHandler:
public class LogHandler : ICallHandler
{
public LogHandler(int order)
{
Order = order;
}
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
string className = input.MethodBase.DeclaringType.Name;
string methodName = input.MethodBase.Name;
string preMethodMessage = string.Format("{0}.{1}", className, methodName);
System.Diagnostics.Debug.WriteLine(preMethodMessage);
return getNext()(input, getNext);
}
public int Order { get; set; }
}
The problem I have is how to use the [Log] attribute. I've seen plenty of example of how to configure the interception settings, for example:
container.AddNewExtension<Interception>();
container.Configure<Interception>().SetDefaultInterceptorFor<ILogger>(new InterfaceInterceptor());
But this implies that I have an interface to intercept, which I don't. I have the ASPX page which uses the [Log] attribute.
so how can I configure Unity to make use of the [Log] attribute? I've done this before using PostSharp and would like to be able to use Unity to do the same.
Cheers.
Jas.
You're unfortunately not going to get this to work in an ASP.NET page with Unity interception.
Unity interception uses a runtime interception model. Depending on the interceptor you choose, you'll either get a subclass with virtual method overrides to call the call handlers (VirtualMethodInterceptor) or a separate proxy object (Interface or TransparentProxyInterceptor) which execute the call handlers and then forward to the real object.
Here's the issue - ASP.NET controls creation and calls to your page, and there's no easy way to hook into them. Without controlling the creation of the page object, you can't use the VirtualMethodInterceptor, because that requires that you instantiate a subclass. And you can't use the proxy version either, because you need ASP.NET to make calls through the proxy.
PostSharp gets around this because it's actually rewriting your IL at compile time.
Assuming you could hook into the creation of the page object, you'd have to use the VirtualMethodInterceptor here. It's a private method, so you want logging on "self" calls (calls from one method of the object into another method on the same object). The proxy-based interceptors can't see those, since the proxy is a separate instance.
I expect there is a hook somewhere to customize how ASP.NET creates object - BuildManager maybe? But I don't know enough about the details, and I expect it'll require some pretty serious hacking to get work.
So, how do you get around this? My recommendation (actually, I'd recommend this anyway) is to use the Model-View-Presenter pattern for your ASP.NET pages. Make the page object itself dumb. All it does is forward calls to a separate object, the Presenter. The Presenter is where your real logic is, and is independent of the details of ASP.NET. You get a huge gain in testability, and you can intercept calls on the presenter without all the difficulty that ASP.NET gives you.

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.

Resources