Calling method on Master page from WebMethod - c#

Or any viable workaround.
So, imagine a Master page that implements IFooMaster with a method ShowFancyMessagePopupTheBusinessCantLiveWithout(string message);
I am in a page that inherits this master page. Upon a checkbox being unchecekd, I want to show a message to the user that if they save it, they can't re-check the checkbox without some admin action.
I've been given feedback that I can't just use an alert('message'); in javascript because they want the consistent look of these messages.
Next, I tried to make an ajax call via PageMethods (as that's what everything else in this codebase uses) to show a message. My problem lies in this method being static.
[WebMethod]
public static void ShowSuperImportantMessage()
{
if(!checkboxICareAbout.Checked)
((IFooMaster)Master).ShowFancyMessagePopupTheBusinessCantLiveWithout("If you uncheck that thing, you can't recheck it.");
}
Since ShowSuperImportantMessage() is static, I can't access Master from within.
The method on the master page looks more or less like this:
public void ShowFancyMessagePopupTheBusinessCantLiveWithout(string message)
{
lblGenericMessage.Text = message;
btnGenericMessageOK.Focus();
upGenericMessage.Update();
mpeGenericMessage.Show();
}
mpeGenericMessage is an ajaxtoolkit:ModalPopupExtender.
upGenericMessage is an update panel.
The other 2 are obvious.
Any ideas? Can I do some jQuery kung-fu to show that stuff? I tried, but the solution complained that the controls I tried to refer to by ClientID didn't resolve since they were on the Master page.
quick edit: Before anyone tells me the architecture is a problem, or I shouldn't have put such a thing on a master page, or w/e...
I know the situation is not ideal, but I this is inherited code, and I can't drop it all and rewrite half of their web stack.

Try something like this (untested):
((IFooMaster) ((Page)HttpContext.Current.Handler).Master)
It appears this doesn't work - Master isn't hooked up when the PageMethod is called (makes sense).
So, instead, create an empty page using the same master page. Have that page accept either a POST or GET with whatever parameters you need to pass to your master-page method. Have the Page_Load extract the parameters and call the method. It should then use Response.Write to return a result (and remember to change the Content-Type). Have your client-side code call the page and get the result.

Did you try something like window.top before the ClientID?
Per comments
You don't need to hardcode ClientID. Since your js is in page, try something along the following lines....
window.top.document.getElementById( "<%= yourelement.ClientID %>" ).Whatever();

Sorry to take so long to respond/answer.
I'm not proud of this at all, mind you, but the eventual solution was to hardcode the client IDs into the jQuery that pulled up the modal dialog on the master page.
Like I said, I'm not proud of this dirty, dirty fix. However, the consolation is that, since it's on the master page, there isn't really any naming container above it. As such, it's much less likely to run into problems with the clientID changing.

Related

asp:HiddenField being cached server-side somewhere?

I have two ASPX pages; they use the same DLL and class, so the first line of each file looks like:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="CustomPage.aspx.cs" Inherits="CustomPageCode.CustomPage" %>
(maybe this is bad form to have two *.aspx pages sharing the same codebehind, but I don't want to have two separate classes with identical code)
I'm 'configuring' each page through a hidden field --
Page1.aspx has the line:
<asp:HiddenField ID="DepartmentName" value="DepartmentOne" runat="server" />
and Page2.aspx has the line:
<asp:HiddenField ID="DepartmentName" value="DepartmentTwo" runat="server" />
My CodeBehind reads DepartmentName.Value to do a bunch of codebehind things, like SQL queries, based on the value of the HiddenField specific to each department, and also Javascript reads that value to do department-specific things as well. I'm doing it this way to simplify configuring each page -- the way the page is configured is right there in the ASPX page and the same value is visible to both ASPX and Javascript.
However, if either page does a POST event -- now DepartmentName.Value ONLY returns the value from the page that did the POST for any page with the same codebehind. Page1.ASPX, even though the asp:HiddenField value in the source is still clearly "DepartmentOne", if Page2.ASPX did the POST, DepartmentName.Value will be "DepartmentTwo" regardless of which page is opened.
The funky thing is: if I open the same page in Chrome, Page One will still have Page Two's DepartmentName.Value, even if the POST event never occurred in Chrome; clearing the IE cache doesn't fix it either. This is definitely something happening on the server side, getting cached somewhere. An IIS reset resolves it.
Google has told me that ASP.NET caches a bunch of things from a POST event but doesn't exactly say how it's handled, or how to enable/disable it, or which of the many cache locations it is located in, and many examples look like I'd have to specifically tell it to start caching things in a persistent way. The closest thing I've found is ModelState.Clear(); in a !IsPostBack at the beginning of the Page_Load, but that doesn't resolve it, I'm not using MVC in my code as far as I know.
So, my question is, how do I force that the GET uses the hidden value in the source code, and not some cached value from an old POST event?
It's probably ViewState, but I'd have to see more of your code for this to be more than a wild guess. I do see this:
I don't want to have two separate classes with identical code)
Yep, that's a good thing. But it sounds like maybe you have too much code in the page class itself that should be moved to a separate utility class, where two separate pages can now share that same utility code. Alternatively, you want a single Department.aspx page that takes a URL argument, like this: /Department.aspx?deptid=Department1 or /Department.aspx?deptID=Department2
Then key off of the url argument, rather than a hidden field. If you don't like the ugly URL, you can use routing to get prettier URLs like this: /Departments/Department1 or /Departmennts/Department2
I discovered my problem:
After wrestling with ViewState, it turns out my problem wasn't hidden fields being cached, it was what was being done with the hidden fields.
At the beginning of my class, I have a variable:
public static Dictionary<string, string> ThisDictionary = new Dictionary<string, string>();
that my code uses ThisDictionary.Add() to add values from ASPX hidden fields to -- but I never declared ThisDictionary as 'new' in my actual function, so every time I added an element to the Dictionary of hidden fields, it was persistent across multiple pages using the same class.
So, when I load my values from what I think is the hidden field, the codebehind is reading the hidden field correctly, but when it takes action in C#, it is using the data in the Dictionary with a bunch of other pages' data in it, hence the appearance that hidden field values are being cached somewhere.
By adding a statement to declare it as a new Dictionary<string,string>() at the beginning of my Page_Load function, it now wipes the dictionary clean with each page load and now it's behaving how I would expect, containing only values from the hidden fields on the particular page.
(I acknowledge what I should probably do is have a separate class with these variables in it, rather than lumping it all into the main ASPX class that gets called when the page loads. Something for the next version)

Dynamic data for JQuery in ASP.NET pages

Problem
I have some pages that need dynamic data from website to generate output to users.
A simple solution is an aspx(php, ...) page to generate data and create another html page serving as GUI retrieving data from first page and showing it to users. in this method I can call my GUI page for example form1.aspx and my data page form1.json.aspx.
although I personally like this method, it is not suitable when creating components for it.
Another method that currently I'm using is using same GUI page call itself with a querystring to retrieve data. this page should check for that query string and if it exists, only generate data and remove everything else from page. As an example for this method if I call my page form1.aspx, to retrieve data, I need to call it like form1.aspx?JSON
Here is an example of what I'm doing:
protected void Page_Load(object sender, EventArgs e) {
if (Request.QueryString.ToString().IndexOf("JSON") == 0){
this.Controls.Clear();
Response.Clear();
// send pure data to client
} else {
// render page as GUI
}
}
However this method becomes too messy if I add master page and/or inherit my page from some template page. Master pages can only removed in Page_PreInit and that adds another extra method.
Security controls cause another problem, if user leaves page open for long time until session expires any attempt to retrieve data will fail cause security module will redirect the request to login page.
Next problem is I cannot consolidate my component in package because it needs modification in page (removing master page, clearing page components ...).
What I'm looking for:
1- I'm looking for a solution that I can call my page and get pure data (JSON or XML format) and doing so run a server side method that generates data, so I don't have to worry about what another designer puts in their master page or template.
2- I think it is possible to use axd extension to do this but I don't have a clue about it and couldn't find a helping document either.
3- Is there any better way of doing this. any suggestion or solution to improve this much appreciated.
Page methods. Check this article: http://weblogs.asp.net/craigshoemaker/archive/2008/09/29/using-jquery-to-call-asp-net-ajax-page-methods.aspx or http://encosia.com/using-jquery-to-directly-call-aspnet-ajax-page-methods/
WCF JSON service: http://www.codeproject.com/Articles/327420/WCF-REST-Service-with-JSON
Other ways of doing is using an HTTP Handler. Implement IHttpHandler interface and register your implementation in your Web.config file. Later call it using jQuery ($.get / $.post):
http://msdn.microsoft.com/en-us/library/46c5ddfy.aspx
EDIT
As OP pointed out, in order to access session state in a page method you should use WebMethodAttribute this way:
[WebMethod(EnableSession = true)]
I think you can use webservice instead of aspx page to return a JSON or XML string and then the caller page (any aspx page) will response after process is success.
So with this webservice, any third party page will have access to your server side method.
To create a webservice pls Check this link: Create and use Asp.net web service basic
Regards

Updating ASP.NET server-rendered controls using ajax

This question got me thinking about how one might go about doing the relatively undoable: seamlessly integrate server-generated HTML from ASP.NET with client-side control via javascript. Sure, you can always use javascript/jquery/libraries to create the same display client-side. But much of the time, it's easier to do all the rendering on the server instead of just passing data to a client-side control which must handle the user interface and rendering. Or maybe you've already got a lot of less-interactive server code that you'd really rather not completely re-do using javascript libraries to just add some better interactivity.
I had a theory which seems to work in a basic proof of concept. Suppose you want to completely re-render the HTML of a server-generated control based on a client-side event, without posting back. So using jquery, I have a page:
default.aspx:
<a id="link1" href="#">Click Here</a>
<div id="container">
<asp:PlaceHolder id="MyPlaceholder" runat="server" />
</div>
<script type="text/javascript">
$(document).ready(function() {
$('#link1').click(function() {
$.ajax({
url: "default.aspx",
type: "GET",
dataType: "html",
async: true,
data: { "ajax": "1" },
success: function(obj) {
// replace the HTML
$('#container').html(obj);
}
});
});
});
The event causes it to query itself with ajax. The codebehind that does the trickery is like this:
TestUserControl ctl;
string ajax;
protected void Page_Load(object sender, EventArgs e)
{
ctl = (TestUserControl)Page.LoadControl("TestUserControl.ascx");
Myplaceholder.Controls.Add(ctl);
ctl.OnRender += new TestuserControl.RenderHandler(ctl_Render);
}
protected void Page_PreRender()
{
ajax = Request.QueryString["ajax"] == null ? "" : Request.QueryString["ajax"];
}
void ctl_Render()
{
if (ajax == "1")
{
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
using (HtmlTextWriter writer = new HtmlTextWriter(sw))
{
ctl.DoRender(writer);
}
Response.Write(sb.ToString());
Response.End();
}
}
In TestUserControl, i expose base.render to get the output:
public void DoRender(HtmlTextWriter writer)
{
base.Render(writer);
}
Basically, if the page is called without the "ajax" querystring, it just acts like itself. But when that querystring is used, it intercepts the output stream from the content I am concerned with (a usercontrol called TestUserControl.ascx) and renders just that. This is returned to the client, which updates the HTML. All the IDs will be recreated exactly as before since I am not trying to render just that control in isolation, but in context of its own page. Theoretically, every bit of magic created by ASP.NET should be reproduced, retrieved and updated by the ajax query.
Apart from the obvious lack of efficiency, it seems to work swimmingly in this little test. I can completely rerender the control using the server generated HTML without a postback and I've written zero javascript. This example doesn't actually change anything, but it would be simple to pass more parameters to change the output.
I was wondering if anyone had tried anything like this in practice? What potential problems might I not be thinking of?
If server performance is not an issue, it seems like it might be quite easy way to get a heck of a lot of functionality with all the benefits of ASP.NET server controls. But I can't seem to find any discussion of using this technique in practice so I am wondering what I might be missing.
Well, for starters, you're sending a GET request to your page, so the control you want to update won't receive up-to-date form data. More importantly, ViewState will be lost and you probably don't want that, unless your user control is very simple.
You could work around the problem by using a POST request, but there are other potential issues, e.g. can you guarantee that the client scripts embedded in your user control either run again or don't when you update it, consistently, on all browsers?
Fortunately, others have already solved those problems. I'd suggest you place your user control inside an UpdatePanel and force a refresh from the client side:
__doPostBack("yourUpdatePanelClientID", "");
If the alternative is using an UpdatePanel, it's definitely worthwhile to consider partial rendering. Even if you're only updating a small portion of a page with the UpdatePanel, these updates must send the entire ViewState back to the server, the server must run the entire page through its life cycle, render it, and then extract the partial area. You take a significant performance hit for its convenience.
In your case, you're still incurring a page life cycle hit since you're rendering that partial within an ASPX pae. It's not as bad as the UpdatePanel, but unnecessary.
I've found that splitting the partial rendering out to a web service or HttpHandler handler works well. It's much faster than most other methods for rendering partials in WebForms, but still allows the flexibility of using User Controls for templating.
The drawback is that controls inside the User Control cannot handle PostBacks. You can definitely re-render it and/or pass parameters in to control its rendering, but you can't use this to render a GridView and expect its paging links to work, for example.
I wish there was more discussion about the topic of moving ASP.NET web forms into the modern era. I've had to figure out most everything on my own. I personally strive for the simplest solutions, and avoid all solutions that require layering on even more Microsoft. Nothing against MS, I just want to keep it simple and use ordinary web standards.
So far, I've been able to AJAX down everything I've tried, using jQuery load. There are no pat, simple answers, but if you are persistent, you can probably solve any client side problem resulting from old school ASP.NET practices.
I think the number one most important thing to do is stop using ViewState. Completely. This will force you out of many terrible practices that would cause you problems on the client. This is actually easier to do than you might think. And at that point, AJAXing stuff down will usually just work. Even DataGrids. Do your own paging, whether client- or server-side, it's not that hard, and then you can reuse your solution everywhere.
The problems will come from third party stuff that you have no control over. In those cases you can make use of IE developer tools (F12) to see what ASP.NET was originally sending down. Worst case, you'll have to scrape out some JavaScript and run it yourself. In practice I rarely have to do anything that terrible. I suppose if you are using a lot of heavy weight controls this would be impractical, but in that case you've already bought the farm.

Run javascript function after Server-Side validation is complete

Ok, I've got a lightbox with a small form (2 fields) in it, inside an UpdatePanel, and I want to close this lightbox (must be done via javascript) when the 'Save' button is pressed.
However, there is a need to have a server-side CustomValidator on the page, and I only want to close the lightbox if this returns as valid.
Does anyone know a way to trigger javascript (or jQuery) code from a server-side validator?
You can add a little snippet of code using the ScriptManager to execute after the response comes back to the UpdatePanel.
if (Page.IsValid){
ScriptManager.RegisterStartupScript(
customValidator1,
typeof(MyPageClass),
"closeBox",
"myLightBoxVariableOnThePage.close()",
true);
}
When that server side validator runs, it will send a whole new page to the browser. Anything that was shown in the browser before was destroyed, including any state kept in your javascript. If new page bears a strong resemblance to the old page, you should consider this a happy coincidence.
Therefore, the thing to do here is rather than executing a javascript function, have your CustomValidator make the correct changes to the page on success so that it's rendered to the browser correctly in the first place.

Popups with complex functionality using jQuery

I am using jQuery to simulate a popup, where the user will select a series of filters, which I hope to use to rebind a ListView in the original window.
The "popup" is opened via an ajax request and the content is actually a diferent aspx file (the rendered output is injected into a div that acts as the popup).
I have another ListView in this popup, and it has pagination.
My problem is that since the popup is in reality html content inside a div in the same page, when I try to paginate, the whole page postbacks and is replaced with the aspx that has the filters.
How can I fix this?
I tried using an update panel to contain the ListView but it didn't work.
$("div.yourthingie").hide();
Will hide the part you want to show :) Instead of generating the popup on the fly, leave a small part already made, and hide it in the begining, when you need to show, unhide and add the information you need to.
Hope it helps
Either get rid of the HTML "crust" and just produce the <div> with its contents, or use an IFRAME.
First, let's think through what is happening. When you submit the original page, you are taking a "normal" Request/Response trip to get the code. On the page is a JQuery AJAX bit that fires off what is essentially a modal dialog. The desired effect is the user plays with the new page until they have figured out their filters and submits back. The problem is this "modal page" loses information when someone paginates.
The solution to this is fairly simple, in theory. You have to store the "filters" in the popped up page so they can be resent, along with pagination information. OR you have to cache the result set while the user paginates.
What I would do to solve this is create a static page that has the "filters" in place and work out the AJAX kinks separate from having the page post back to a parent page. Once you have all of the AJAX bits working properly, I would then link it into the popup routine and make sure the pagination is still non-problematic. THe final problem is creating a JavaScript routine that sends back to the parent page and allows the parent page to send its JQuery bits back to the server.
I am not sure about the HTML DIV part of the equation and I think you can solve the problem without this solution. In fact, I believe you can make the "modal popup" page without invoking AJAX, if it is possible to either a) submit the filters to apply via the querystring or b) fake a form submit to the second page. The query string is an easier option, but it exposes some info. Faking a form submit is not that difficult, overall, but could be problematic with a popup.
I am just firing off some ideas, but I hope it spurs something for you.

Categories