The Code Page
A series of development tips and code examples by Gabriel McAdams
Feb 10

There are a lot of really great controls in the ASP.Net AJAX Control Toolkit.  For those of you who are unfamiliar with the toolkit, it is an open source project that is built on top of the Microsoft ASP.Net AJAX framework (the framework was formerly known by its code-name: Atlas).  It contains over 40 controls that you can use to build highly responsive and interactive Ajax-enabled Web applications.  It is a joint effort between Microsoft and the development community.

Today, I’m going to talk about one of the controls specifically.  The HTMLEditor control.  This control allows you to create and edit html content directly from within your browser.  There are a lot of toolbar buttons available, and you can edit your content using the WYSIWYG (What you see is what you get) pane or the html text pane, and view the results using the Preview pane.

The control allows a good deal of customization.  By modifying the CSS stylesheet, you can change the look and feel of the editor.  By creating your own editor class and inheriting the editor control, you can specify which toolbar buttons should be available, and which panes to enable.  Unfortunately, you can’t easily change the state of a toolbar button on startup.  A user on stackoverflow.com asked if it were possible to pre-set the ‘right to left direction’ toggle button from the server.  The control’s server-side properties do not allow this, but I was able to help him find a solution.  Here is what we found:

When you add the editor control on your page like this:

<HTMLEditor:Editor runat="server" Id="editor" />

You end up with a few lines like this at the bottom of your page (one for each toolbar button, one for the toolbar itself, and one – like this one - for the editor):

Sys.Application.add_init(function() {
    $create(AjaxControlToolkit.HTMLEditor.Editor, null, null, { "changingToolbar": "ctl00_SampleContent_editor_ctl01", "editPanel": "ctl00_SampleContent_editor_ctl02" }, $get("ctl00_SampleContent_editor"));
});

Notice that the line starts with a Sys.Application.add_init function call.  This adds an event handler to the init event (the init event is raised after all scripts have been loaded but before page objects are created).  This is when the objects are created.  If we want to set the state of a button, we have to at least wait until the button is created.

While looking at the JavaScript code for the editor, I found that although the editor is created during init, it is not actually initialized until the load phase (it must ensure that all of its objects are created before it can initialize itself).  The way it ensures that all objects are created before it initializes is by calling the Sys.Application.add_load function (the load event is raised after all scripts have been loaded and the objects in the application have been created and initialized).

We need to attach our own code after the editor has been initialized.  This means that we need to inject code AFTER the load event, but before the user interacts with it.  To do this, add this code to your page, or to an external JavaScript file referenced by the page.  When adding this script to the page, be sure to add it below the ScriptManager.  By adding the ScriptManager to your page, you’re including the ASP.Net AJAX framework (which is needed in order to access the Sys namespace).

Sys.Application.add_load(myFunction);
 
function myFunction() {
    window.setTimeout(myOnLoad, 0);
}

This will tell the framework to call the myFunction during the load event.  We can’t customize the editor or its buttons yet, though, because it may not be finished with its initialization.  Remember, we have to inject our code AFTER the load event.  So our ‘myFunction’ function calls window.setTimeout.

By using setTimeout, we are setting up myOnLoad to be called in 0 milliseconds.  This seems pointless, but its not.  Before myOnLoad can run, it has to wait until the current stack has completed (then it waits the specified amount of time).  The current stack includes all load event handlers).  When myOnLoad is called, we can be sure that the editor has been initialized, and it is ready to be modified.

In order to modify the control, we need to access its JavaScript object.  The framework exposes methods to find components in the page.  Here is what we’ll need in order to find the editor and its toolbar buttons:

var editor = $find('<%=editor.ClientID %>');

We start by using the $find function, which returns the specified Component object.  You may have noticed the server tags in the above code.  When you add a control to a page in ASP.Net, it ensures a unique id in the page by prefixing the id with text that is based on the control tree (for example, in the sample page, the editor’s id is ‘ctl00_SampleContent_editor’).  The id rendered is not yet known at design time.  In order to render the correct id in my script at runtime, I use this server tag and reference the ClientID property of the editor control.

If you added this script into an external JavaScript file, then you wont have access to server side properties and the server tags will not be replaced by anything.  Therefore, you will have to do something different to get the id of the editor.  Instead, add this script to your page:

var myEditorId = '<%=editor.ClientID %>';

and then change the above code to this:

var editor = $find(myEditorId);

Once we have access to the editor, we need to use it to get access to its toolbar, and then to its buttons.

var editor = $find('<%=editor.ClientID %>');
var toolbar = editor.get_changingToolbar();
var toolbarButtons = toolbar.get_buttons();

The above code will result in an array of objects that represent the toolbar buttons in the editor.  From here, we can loop through the buttons until we find what we’re looking for.  Once we find it, we can change its properties or run methods against it.

Here is the entire script:

// Attach a handler to the load event.
Sys.Application.add_load(myFunction);
 
function myFunction() {
    //Setup myOnLoad to run AFTER the load event handlers have all completed
    window.setTimeout(myOnLoad, 0);
}
 
function myOnLoad() {
    //Find the instance of editor
    var editor = $find('<%=editor.ClientID %>');
    //Access the editor's toolbar
    var toolbar = editor.get_changingToolbar();
    //Get an array of toolbar buttons
    var toolbarButtons = toolbar.get_buttons();
    //Loop through the buttons array looking for the one we need
    for (var i = 0; i < toolbarButtons.length; i++) {
        //If this button is the right to left direction button, then toggle
        if (toolbarButtons[i] instanceof AjaxControlToolkit.HTMLEditor.ToolbarButton.Rtl) {
            //first, make sure the button has an edit panel set
            toolbarButtons[i].set_activeEditPanel(editor.get_editPanel());
            //Call the callMethod function (toggles the button)
            toolbarButtons[i].callMethod();
        }
    }
}

In this case, we were looking for the ‘right to left direction’ button.  We found it by checking each button to see if it was an instance of the ToolbarButton.Rtl object.

The first line after that sets the active edit panel.  The control itself will do this, but not until later.  We need this to be done before we can call the callMethod function (which is used to change the toggle state of a toggle button in the editor’s toolbar).

So that’s it.  We’ve changed the default state of the toolbar button.  Check back soon for more ways to customize the controls that are part of the toolkit.


Feb 03

Often, T-SQL developers will ask if there is a way to access some kind of a call stack at runtime.  Either the full call stack or simply the previous calling procedure.  Some uses for this would be to debug a procedure, or to restrict users from updating a table unless it was done through a particular stored procedure.

As of SQL Server 2008, no feature exists.  While searching the web for such a feature, I came across a feature request from 2006 by Erland Sommarskog.  The feature has not yet been added, so we’ll just have to make do with workarounds.

Mentioned as a workaround to the feature request above, is the use of SET CONTEXT_INFO and CONTEXT_INFO().  SET CONTEXT_INFO has been available since SQL Server 2000, but it may not be widely known. It sets 128 bytes of binary information tied to the current session.  You can use CONTEXT_INFO() to retrieve this value anytime during the same session and see what you’ve set.  NOTE: The CONTEXT_INFO() function is not available in SQL Server 2000, but you can retrieve the value from the CONTEXT_INTO column in the sysprocesses table (WHERE SPID = @@SPID).

I decided to see what I could create that would be easiest to implement, yet provide as much information as possible.  Here is what I have come up with:

I decided that since I was trying to make a call stack, that using CONTEXT_INFO as a stack (LIFO) was the best option.  Being a stack, I needed two procedures.  CallStackPush, and CallStackPop.  I also created a table valued function called CallStackView.

The idea is that you add a call to CallStackPush at the top of each of your stored procedures

EXEC dbo.CallStackPush @@PROCID

and a call to CallStackPop at the bottom.

EXEC dbo.CallStackPop

Then, anywhere you need it, you can call CallStackView to see the call stack.

SELECT * FROM dbo.CallStackView()

Which produces this:

SQL Output

In this case, SP_1 calls SP_2, which calls SP_3.  Within SP_3, I added the statement to  select from the CallStackView() function.

Given the small amount of space available, I was unable to add the parameter values into the call stack.  The way I implemented it, I was able to show the schema, procedure name, and anything else available in the system views and tables based on the object_id of the stored procedures.  This method allows for the nesting of up to 32 procedures (given the 4 byte (integer) object_id of stored procedures).  I seriously doubt anyone will ever see nesting levels that big.  If you do, you have some serious design changes to make.

The CallStackPush function contains only 3 lines of code.

DECLARE @BIN VARBINARY(128)
 
SELECT @BIN = CONVERT(BINARY(4), @PROCID) + ISNULL(CONTEXT_INFO(), CAST('' AS VARBINARY(1)))
 
SET CONTEXT_INFO @BIN

In the function, I convert the @PROCID parameter (INT) to a 4 byte binary and tack it onto the beginning of the stack.  Then I put the whole thing back into CONTEXT_INFO.

The CallStackPop is a little more complicated, but still very small.

DECLARE @BIN VARBINARY(128)
SELECT @BIN = ISNULL(CONTEXT_INFO(), CAST('' AS VARBINARY(1)))
SELECT @BIN = SUBSTRING(@BIN, 5, 128)
 
SET CONTEXT_INFO @BIN

In this function, I just remove the first 4 bytes from CONTEXT_INFO.

CallStackView is simply a while loop to get a list of all 4 byte object ids that have been added to the value.

CREATE FUNCTION dbo.CallStackView()
RETURNS @result TABLE (
    SchemaId INT,
    SchemaName VARCHAR(256),
    ProcedureId INT,
    ProcedureName VARCHAR(256)
)
AS
BEGIN
    DECLARE @BIN VARBINARY(128)
    SELECT @BIN = ISNULL(CONTEXT_INFO(), CAST('' AS VARBINARY(1)))
 
    DECLARE @PROCID INT
 
    WHILE (LEN(@BIN) > 0 AND CONVERT(INT, SUBSTRING(@BIN, 1, 4)) > 0) BEGIN
        SET @PROCID = CONVERT(INT, SUBSTRING(@BIN, 1, 4))
        SET @BIN = SUBSTRING(@BIN, 5, 128)
 
        INSERT @result (
            SchemaId,
            SchemaName,
            ProcedureId,
            ProcedureName
        )
        SELECT
            s.schema_id,
            s.name,
            o.object_id,
            o.name
        FROM sys.objects o
        INNER JOIN sys.schemas s ON s.schema_id = o.schema_id
        WHERE o.object_id = @PROCID
 
    END
 
  RETURN
END
 
GO

This works well, and as I stated above, unless you have a nesting level greater than 32, you’ll be ok.

There is one thing this method does not do.  It does not show the originating statement sent by the client.  I needed to create another stored procedure for that.  I called it GetOriginatingStatement.  The code for it looks like this:

CREATE TABLE #inp_buff (
    EventType NVARCHAR(30),
    Parameters INT,
    EventInfo NVARCHAR(255)
)
 
INSERT INTO #inp_buff
EXEC('DBCC INPUTBUFFER(@@SPID) WITH NO_INFOMSGS')
 
SELECT
    @RETVAL = EventInfo
FROM #inp_buff
 
DROP TABLE #inp_buff

This procedure will return (VIA the @RETVAL output parameter) the text of the outermost statement (the last statement sent by the client).  You can use this in conjunction with the call stack functions above to get a complete picture of the stack.

 

Download Source Code: CallStack.zip

 

This software is subject to the Microsoft Public License (Ms-PL)


Feb 02

Over the years, I have encountered a few web developers who ignore client side (JavaScript) errors in their applications.  Some have even asked me for ways to suppress client-side errors all together.  With the growing amount of client-side code in web applications, this is a big concern.

It may seem to some that JavaScript errors are inconsequential, but this is not the case.  They are errors just like any other.  Whenever an error occurs, your application is no longer doing what you designed it to do.

The problem might have to do with the separation of client and server.  Developers may feel that nothing can be done about client-side errors because they aren’t notified of them as they are when a server-side error occurs.

This is why I have decided to release a server control designed to allow .Net developers to log JavaScript errors on the server the same way they can log server-side errors.  The control has a very small footprint.  It’s simple to use.  And it takes up no visible space on the page.

The control uses window.onerror to capture JavaScript errors and send the error information to the server using the iCallbackEventHandler interface (note: As of this writing, window.onerror doesn’t work in Google Chrome).  Whenever a JavaScript error occurs, the control raises an event on the server that can be handled in the page.

To use the control is very easy.  Simply add this to your markup:

<cc1:JSErrorNotifier ID="JSErrorNotifier1" runat="server"
    OnJavaScriptError="JSErrorNotifier_JavaScriptError">
</cc1:JSErrorNotifier>

and this to your code-file:

protected void JSErrorNotifier_JavaScriptError(object sender, WebControls.JavaScriptErrorEventArgs e)
{
    e.DisplayMessage = "A JavaScript error has been received by the page";
}

 

 

Download Source: JSErrorNotifier.zip

This software is subject to the Microsoft Public License (Ms-PL)


Jan 20

This blog is dedicated to helping you learn more about web development using Microsoft Technologies.
 
I will provide you with help, tips, advice, and techniques on how to create well designed and easy to maintain ASP.Net web applications.  Technologies will include C#, JavaScript, SQL Server, and more.
 
I'm not sure yet how often I'll be posting, but I will try to add tips as often as possible.


.