Guidelines for overloading Render method

S

sk

Hi,
I'm trying to override the Render method of my Page class. Are there any
standard guidelines for this?

Thanks.
Shardul
 
G

gaidar

I don't know what about the guidlines but what I know is what overriding
Render method is a little bit difficult thing, because you should render
hidden fields, scripts, viewstate and and so forth, and so on.

This code I've found in google and it seems to me that this way ASP.NET Page
do it originally:

internal void OnFormRender(HtmlTextWriter writer, string formUniqueID)
{
if (_fOnFormRenderCalled)
{
throw new
HttpException(HttpRuntime.FormatResourceString("Multiple_forms_not_allowed"));
}
_fOnFormRenderCalled = true;
_inOnFormRender = true;
RenderHiddenFields(writer);
if (_viewStateToPersist != null)
{
if (_formatter == null)
{
CreateLosFormatter();
}
writer.WriteLine();
writer.Write("<input type=\"hidden\" name=\"");
writer.Write("__VIEWSTATE");
writer.Write("\" value=\"");
_formatter.Serialize(writer, _viewStateToPersist);
writer.WriteLine("\" />");
}
else
{
writer.WriteLine();
writer.Write("<input type=\"hidden\" name=\"");
writer.Write("__VIEWSTATE");
writer.Write("\" value=\"\" />");
}
if (_fRequirePostBackScript)
{
RenderPostBackScript(writer, formUniqueID);
}
RenderScriptBlock(writer, _registeredClientScriptBlocks);
}

internal void OnFormPostRender(HtmlTextWriter writer, string
formUniqueID)
{
if (_registeredArrayDeclares != null)
{
writer.WriteLine();
writer.WriteLine("<script language=\"javascript\">\r\n<!--");
writer.Indent = writer.Indent + 1;
IDictionaryEnumerator iDictionaryEnumerator =
_registeredArrayDeclares.GetEnumerator();
while (iDictionaryEnumerator.MoveNext())
{
writer.Write("var ");
writer.Write(iDictionaryEnumerator.Key);
writer.Write(" = new Array(");
IEnumerator iEnumerator =
((ArrayList)iDictionaryEnumerator.Value).GetEnumerator();
bool flag = true;
while (iEnumerator.MoveNext())
{
if (flag)
{
flag = false;
}
else
{
writer.Write(", ");
}
writer.Write(iEnumerator.Current);
}
writer.WriteLine(");");
}
writer.Indent = writer.Indent + 1;
writer.WriteLine("// -->\r\n</script>");
writer.WriteLine();
}
RenderHiddenFields(writer);
if (_fRequirePostBackScript && !_fPostBackScriptRendered)
{
RenderPostBackScript(writer, formUniqueID);
}
RenderScriptBlock(writer, _registeredClientStartupScripts);
_inOnFormRender = false;
}

private void RenderHiddenFields(HtmlTextWriter writer)
{
if (_registeredHiddenFields != null)
{
IDictionaryEnumerator iDictionaryEnumerator =
_registeredHiddenFields.GetEnumerator();
while (iDictionaryEnumerator.MoveNext())
{
writer.WriteLine();
writer.Write("<input type=\"hidden\" name=\"");
writer.Write((String)iDictionaryEnumerator.Key);
writer.Write("\" value=\"");
HttpUtility.HtmlEncode((String)iDictionaryEnumerator.Value,
writer);
writer.Write("\" />");
}
_registeredHiddenFields = null;
}
}

private void RenderScriptBlock(HtmlTextWriter writer, IDictionary
scriptBlocks)
{
if (scriptBlocks != null)
{
writer.Indent = writer.Indent + 1;
IDictionaryEnumerator iDictionaryEnumerator =
scriptBlocks.GetEnumerator();
while (iDictionaryEnumerator.MoveNext())
{
writer.WriteLine((String)iDictionaryEnumerator.Value);
writer.WriteLine();
}
writer.Indent = writer.Indent - 1;
}
}

Best regards,
Gaidar
 
S

Shardul Kulkarni

Hi Scott,
My company markets a helpdesk application that is web based and for
performing database administration tasks we have a custom application (.exe)
that is client/server based. This app is written using pure SDK (no
MFC/ATL/OWL). This code is pretty old (nearly a decade) and it supports only
English characters. It comprises of 3 main dlls which are pure C dlls (no
COM) with exported C++ classes. For the next release of our main app, we
intend to support multiple languages and so the management decided to
convert this legacy app also to web based. We have written a .NET wrapper
(written in C# and managed C++) which eventually calls into the low-level
dll's and the UI is in ASP.NET. The UI calls into the wrapper which in turn
calls into the legacy dlls. And for the most parts this works ok. But in the
old app, whenever you performed any operation a small window would open and
display status messages. For example when you performed an operation to
check the database for correctness, it would show messages like 'Checking
views...', 'Checking tables'... and so on. The calls to display these
messages are placed in the low-level dlls.

Now my job is to display these progress messages through the ASP.NET UI.
This is quite a challenge and we have figured out how to get the progress
message calls to end-up at the ASP level using delegates at the wrapper
level and function pointers at the dll level and this also works just fine.
So when the dll makes a call to show a progress message, it calls a method
at the low-level which calls the function pointer which points to the
delegate in the UI. So when my UI method is called, I just have to send it
the client (which is a browser). And this is anything but trivial.

I looked at a very good article on MSDN by Dino Esposito (Build your ASP.NET
pages on a richer Bedrock) but his solution just tells you when the
operation completes. Its not interactive. I have to mimic the exact behavior
of the old app, which is to open a small window and display the messages
within that window and then to close the window once the operation
completes.

For testing purposes I created a test page (Test2.aspx) that does NOT have a
code-behind class. And took the following steps:
1. Created a marker interface IRequireProgressMessage.
2. Put a <%@ Implements interface="IRequireProgressMessage" %> directive at
the top of my page Test2.aspx.
3. I wrote a server-side script inline within the page inside the <body> tag
and just before the <form> tag.
4. Within the block I put a check for postback and if it succeeds then I
emitted the following client-side script:
<script >
var oWin = window.showModelessDialog("blank.html",'','');
var oDoc = oWin.document;
</script>
5. After emitting this I flush the buffer just to be sure. At this point we
haven't yet called into the wrapper and the client has opened a modeless
window for displaying the progress messages. blank.html contains a single
<div> element with the id 'msgDiv'.
6. After this I setup the infrastructure to receive calls to SetMessage from
the dlls. This involves implementing an interface with one method
SetMessage(string strMessage). The implementation of SetMessage emits the
following client side script:
"<script>oDoc.all.msgDiv.innerHTML= '" +strMessage + "' </script>"
after checking whether the current handler
(System.Web.HttpContext.Current.Handler) is of the type
IRequireProgressMessage.
7. Call a method of the wrapper object. For this I created a test() method
on the wrapper which wraps a test() method on one of the lower level
objects. This method sends 2 messages with a delay of 2 seconds.

This works fine and the two messages are displayed in the modeless dialog.
However, this works only when you have the script within the .aspx page. But
the UI guys have already finished most of their work and they have used
code-behind throughout. The calls into the wrapper are placed from within
the Page_Load method. Now the problem with Page_Load is that if I emit my
script there and even if I flush the buffer, it won't get sent to the
browser. I don't know what goes on behind, but I think it just gets buffered
into another intermediate buffer and is emitted when Render gets called. So
I thought of overriding the Render method, move all of the processing from
Page_Load into the overridden method and then call into the wrapper after
rendering the <body> tag. For this I derived a new class from
System.Web.UI.Page, overrode the Render method. In this method I loop the
Controls collection and keep a check whether we rendered a LiteralControl
with the text "<body" in it. If it does it calls a virtual method
PostBodyRender. This method will be overridden by the code-behind class and
from within this do the processing that Page_Load used to do. It will first
emit the script that gets the modeless dialog to display if the page is
posted back and then do the rest of the processing, which involves setting
up the messaging infrastructure and calling into the wrapper method.

And this too works fine, but (and a big bad BUT) it gets munged if there is
some in-line script within the page!!! All aspx pages set the <title> from
server-side code. When this happens there's only one item in the Controls
collection which is the HtmlForm object representing the single <form>
element in the page. I just can't figure out when is the rest of the html
rendered and what happens to the <body> element. I figured, I must be doing
something wrong in Render and felt that there must be some standard way of
overriding the Render method which led me to post this here.

Whew! This turned out to be quite a big post! I tried to be verbose just to
be sure you have all the information. Let me know if you need anything else.

Thanks in advance.
Shardul.
 
S

Scott Allen

Hi Shardul:

Quite an explanation here! It's always a pain to provide immediate
feedback to a web app - I feel your pain.

After reading your description my concern would be that whatever
solution you come up with would be fragile - in the sense that an
unknowing developer could change functionality in the ASP.NET page and
break the feedback mechanism.

Did you consider a polling mechanism? The message from the lower level
could be saved on the server, the client window could refresh or
postback every 5 seconds and check the status. Messages would still be
pretty much real time - but the solution I feel would be much easier
to maintain.
 
S

Shardul Kulkarni

Hi Scott,

A polling solution was among the first things that I thought of. But theres
a problem. If the polling interval is 5 soconds and say there are 5 messages
that are generated, then it will take a whole 25 seconds before all those
messages are displayed to the user, and the operation most likely will
complete before 25 seconds have elapsed since it was invoked. So if you
reduce the polling interval, then you will be hitting the server a lot more
and it may impair the performance of the server.

-Shardul
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top