RadControls version
|
All
|
.NET version |
All |
Visual Studio version |
All
|
programming language |
C#/Javascript
|
browser support |
all browsers supported by RadControls
|
As .NET programmers we are used to creating classes that can be reused, right? And if we are working in the web space, we do that with controls - UserControls (.ascx controls) especially - a lot too.
But have you noticed how messy things can get if you have a UserControl based on service-side controls that also use a lot of JavaScript?
Lets look at an example. The following is a trivial control based on the RadTextBox that refuses entry unless the value starts with a predefined character...
Let's put it on a page...
If we run it (Webform1.aspx in the attached project) then it does what we expect.
Hurrah for us.
OK, now let's create a page with 2 instances of the control on it, but with different values for the 'StartingCharacterSet'...
and run it (Webform2.aspx in the attached project).
Try entering a word beginning with "A" in the first text box. It doesn't work does it? What about if you try a word beginning with "B" in the 2nd one? Yes?
Try entering a "B" word in the first textbox. It accepts it doesn't it?
We can see why if we look at the generated HTML source...
Our page has 2 functions with the same name. Each definition encountered is treated as a redefinition of the existing function; after all, we can only have a single function with a given name. So, whilst each of our TextBox controls is configured differently, when the page is run, they call the same version of the JavaScript function.
There are a number of ways around this problem.
I'm going to show you how I do it.
We need a JavaScript 'class' we can associate with our UserControl, one that can have multiple instances on the same page. At the same time we want to ensure that we aren't sending duplicate data down the wire to the client (bandwidth is getting cheaper, but still, waste is waste, right?)
What I do is start by creating a JavaScript object named for the UserControl is it to work with. What's more, I - generally - create it in a namespace that matches the namespace the UserControl lives in. I use this 'template' to get me started ...
I'm simply basing my JavaScript control on 'Sys.UI.Control', so a quick search and replace gets me this ...
Before we move on, lets have a quick look at the constructor.
This article tells us that if we inherit from Sys.UI.Control (or from another object that, ultimately, inherits from Sys.UI.Control) then we should pass it a DOM element that the instantiated object will be associated with.
We want this (JavaScript) object to be associated with our UserControl. To that end, I 'wrap' the UserControl in a simple span by adding this to the code-behind of the UserControl ...
I can then $get this control and pass it to the JavaScript object's constructor.
As I do this a lot, I have a base class that all of my UserControls inherit from and this override lives there so I don't have to worry about forgetting it.
OK. Next thing to go is to call the JavaScript object's constructor. We do this in the markup of the UserControl using the
Sys.Component $create method.
We add the call to $create in a function called by the
Sys.Application's init event.
To do this we add code like this to our UserControl's markup...
Before continuing, we need to acknowledge a couple of roadblocks and how we get around them. The first of these is the fact that our original version of our UserControl referenced a server value
and we can't do that if our JavaScript object's source is in a separate file. However, we can still reference that server value in our UserControl and pass it on to our JavaScript object either in the constructor or after the object has been created.
So lets add a property to our JavaScript object...
Now we need to assign a value to it when the object is instantiated.
The $create method takes a number of parameters. The first is the type being created and the 2nd is a JSON object that describes the object's properties and their values. So, we can rewrite the constructor like this ...
As an alternative, we might want to create an object separately, assign that object to a variable and pass that in to the constructor, or we might simply want to assign values to the property after the constructor has been called. To do this we need to $find the object so, we could rewrite the constructor like this ...
Next add any methods we want in our control object. In this case we want something to handle the OnValueChanging event so we add a method...
That's it, right?
Not quite. We still need to modify the UserControl's markup to point to the relevant method on our control object. This is where we hit another roadblock. We need to reference a method in out UserControl's markup on a object instance that doesn't yet exist. Here we need to use a namespaced function. We use this functionality like a static method in C#.
This article on StackOverflow has a nice clear description of this. So, we modify our control object to include this ...
And our UserControl's markup to look like this ...
What's happening here is the ClientEvent for the RadTextBox is pointing to the
static method RadControlsWebApp1.Controls.TextControl2.OnValueChanging. In the implementation of this method we call get_parent() on the sender parameter. Because of the way we defined the control object to be associated with the UserControl the parent is our control object. Having now got a reference to the specific instance, we can call a method on it.
All we need to do now is ensure that our page, the one containing our UserControl, includes a reference to the script file containing the source for our control object and we're done.
Clearly this is a trivial example, but the principle's apply regardless of how many controls you have in your UserControl. As an example, a search control in a web application might contain multiple textboxes, radio or checkbox buttons, comboboxes and pickers. It might even include other UserControls. This technique will allow you to write client-side code that is associated with your server-side controls in a way that is much more manageable than just adding endless JavaScript code to your .ascx UserControls
The example project
does not include the Telerik DLLs;
--
Stuart