So, I've seen many discussions that are in this area, but it seems like they are mostly discussing Windows Forms, or they don't get around to answering this specific scenario enough to point me in the right direction.
Exactly what I need to do (Generic Example):
HTML (fragment.aspx)
<div id="html_fragment_1" runat="server">Contents</div>
<div id="html_fragment_2" runat="server">Contents</div>
...
Code Behind (fragment.aspx.cs)
Fragments fragment = new Fragments();
fragment.return_fragment( AN INSTANCE THAT REFERS TO html_fragment_N );
Class (Fragments.cs)
public void return_fragment ( AN INSTANCE THAT REFERS TO html_fragment_N ) {
string html = INSTANCE.html_fragment_N;
// DO SOMETHING WITH html
HttpContext.Current.Response.Write(html);
}
The reason I need to do this is that every aspx form on my site needs to be manipulated in the same way by Fragments.return_fragment(), where the content for several DIVs need to be read from the Form and arranged into an XElement to be returned.
Instead of doing the manipulation in the CodeBehind for every page, I'd rather have each Form use Fragments.return_fragment() so that it saves effort implementing a new Form page, and the code can be changed easily without having to change it in each Form.
Looks like you want your app's Global.asax file:
http://msdn.microsoft.com/en-us/library/1xaas8a2(v=vs.71).aspx
Look at your app's page events here as a place to specify things that should happen on every page request.
Related
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)
control (eg label) changes value when page loads.
before page load, label value is "x"
proceed to load page naturally x is lost at this point, so what is a simple way to store the value from the previous page
load?
It sounds like what you want is a history of previous states of the page, or at least just one previous value. I can suggest a way to do this, but with a caveat: Don't overuse it. You can persist data between posts in ViewState but that data gets written to the page itself. That way when the users posts the form, they're also posting that data back. (More at the end.)
Here's a simple example. First, define some class that contains all of the additional state you want to store:
[Serializable]
public class PageState
{
public string MyLabelPreviousText {get;set;}
}
Then in your code behind:
public partial class MainPage : System.Web.UI.Page
{
private PageState _pageState;
protected void Page_Load(object source, EventArgs e)
{
_pageState = ViewState["pageState"] as PageState ?? new PageState();
_pageState.MyLabelPreviousText = MyLabel.Text;
}
protected void Page_PreRender(object sender, EventArgs e)
{
ViewState["pageState"] = _pageState;
}
When you load the page, you're checking to see if you've already saved your class (in this case called PageState) to the ViewState. If you haven't, you create a new one.
In the PreRender event, after you're done updating the page, you're updating that class with the text of your label and then saving the whole thing to ViewState again. That way the next time the page loads you can retrieve it again.
I'm not sure at which point you want to save the previous text of your label. In this example it's saving the text during the Load event. That way if the text is changed at any point from then on, the value you have saved is the original text of the label. The details may vary depending on what you're trying to do, but this pattern lets you save that sort of custom data without using Session and piling up data like this in memory.
I mentioned not overusing it. If you save some labels, controls, and other data, ViewState probably won't get too large. ASP.NET is already putting the state of every server control there anyway. But if you go really crazy with it then ViewState can get huge. All of that data gets written to the page, and all of it gets posted back to the server with each postback.
It's probably not a huge concern, but be mindful of it. Use Chrome dev tools, Fiddler, or even just inspect your page source to see if that data is getting so large that it might impact performance.
For anyone who isn't familiar, ViewState looks like this in the HTML source:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwULLTEyMzgxNzgyNDIPZBYCZg9kFgICAQ9kFggCAQ9kFgJmD2QWAgIBD2QWAmYPZBYMA
... etc.
As you can see, it's actually a hidden form input. ASP.NET is placing all of the details of the page's controls in the form so that when you post the form, it can piece all of that together and make sure that the page looks the same after postbacks. That's the reason why when we set up a lot of controls the first time we check if(!IsPostBack()). If it is a postback then we don't need to populate the dropdowns, etc. all over again. They are restored from that ViewState data.
This approach is functionally the same as adding an additional hidden input as suggested in one comment. The difference is that you're using an existing hidden input (viewstate) and if you need to save multiple values you're just making them properties of a class instead of adding more and more hidden inputs.
I'm not sure if this is what you mean without any code samples but I know from memory that in ASP.NET you can access the previous page property like so:
Page lastPage = currentPage.PreviousPage
Which returns the entire Page object. Assuming your label is defined like so:
<asp:label id="myLabel" runat="server" />
Then you can access the text property with:
Label myLabel = lastPage.FindControl("myLabel") as Label;
lastPageVal = myLabel.Text
So ensure that lastPageVal is a static variable, then it will also persist throughout pages.
I have a content page connected to a master page. I can access an element on the master page and modify it directly from the content page .cs file by calling a method on the site master. (this is probably the most standard bug people have in this type of area)
My problem is that I wanted to extend this functionality to update the site master page from an AJAX request as well. The ajax file calls a different page which in turns starts an instance of the logic layer which I use for all the calculations and connections. What I am trying to do is access the sitemaster directly from the logic layer (only a .cs file).
My current code is this:
SiteMaster sm = new SiteMaster();
sm.MyMethod("param1", "param2");
This successfully accesses the method called "MyMethod" in the site master but inside this method I have this code:
mySpan.InnerText = "this is a test";
which doesn't work because I get the "Object refernce not set to an instance of an object...." error. This is because mySpan is NULL. If I call it using this.mySpan.InnerText though, if I hover over "this" then I can see the ID "mySpan".
Does anyone know how I can get around this problem? Every search I have made is regarding people who want to access the elements from the content page which already works for me.
I believe you've got a misunderstanding here. If I understand correctly you've got a page with a MasterPage. On that aspx page you're doing an ajax call (perhaps to a WebService) which does something like:
[WebMethod]
public void UpdateText(string message)
{
var master = new SiteMaster();
master.mySpan.Text = message;
}
There are a couple of things wrong here.
When you use this approach is an aspx page you're updating that Page's master. For example:
public void OnSomeRandomButtonClick(object sender, EventArgs e)
{
((SiteMaster)this.Page.Master).mySpan.Text = "Some Text";
}
What you're doing here is updating the span on the master page before it's being sent to your browser. The other subtly is that you're not creating a new SiteMaster, you're using the Page's existing Master and casting it to a SiteMaster.
There are a couple of reasons you can't do this with ajax:
A webservice doesn't have a MasterPage
By the time you send an ajax request your Master page has already been created and sent to the browser.
So your question becomes how do we update a span in the Master without posting back to the server?
Lets look at the html which is actually on your box, it will look something like this:
<html>
<head>
<title>My Awesome Page</title>
</head>
<body>
<h1>This is my Awesome Website</h1>
<span id="mySpan">I'm sure you'll like it</span>
<div>
<p>Page Content</p>
<div>
</body>
</html>
Lets assume that everything here is generated by the master and only the <p>Page Content</p> is your aspx page (There will also be loads of ASP.NET junk added, we'll ignore that for the time being).
What you want to do is update the text in mySpan without posting back to the server. You can do this via the javascript - don't get ajax involved at all!
I'm going to assume you're using jQuery (mostly because I'm more familiar with it that plain old JS). You've got the ID of your span ("mySpan") so the rest is easy:
$('#mySpan').html('This is the updated message');
You can put this in either a click or a page load.
No. You can not simply construct an ASP.NET page and use its state.
ASP.NET pages (and controls and Master pages) are being constructed and initialized from inside the ASP.NET engine based on the Markup provided for them. There is for example no initialization for mySpan inside the codeBehind of your master page, that will be constructed when the code generated based on the Markup is invoked based on a user request.
So you define this in your class:
protected HtmlGenericControl mySpan;
But the ASP.NET engine will compile this markup
<span id="mySpan" style="color:green"></span>
to this code:
this.mySpan = new HtmlGenericControl();
this.mySpan.Style.Add("color", "green);
and that is why you can use this object inside your code.
So if you want to use a property of your Master page from your Business layer, you have so many choices. On of the fastest one to implement is to make your Logic class singleton inside the Session scope, store the value you want to use inside the master page into that singleton object and then read that value from the master Page. This is an example of what you should do, of course it is rough.
class Logic
{
public static Logic Instance
{
get
{
if (HttpContext.Current.Session["LogicInstance"] == null)
HttpContext.Current.Session["LogicInstance"] = new Logic();
return (Logic) HttpContext.Current.Session["LogicInstance"];
}
}
public string TextForSpan {get;}
// The rest of your implementation
}
Instead of the code to assign the inner text, write:
Logic.Instance.TextForSpan = "This is my text";
And inside your master page:
this.mySpan.InnerText = Logic.Instance.TextForSpan;
For an existing site, I have to pass values in hidden fields from a form that's loaded by different pages (eg. City1.aspx, City2.aspx, City3.aspx, etc.), but they are loaded inside an iframe. I also have to dynamically change the value of at least one of those hidden field (let's call it "source") based on the city page loading it. I am familiar with PHP and JavaScript/JQuery, but I have no idea how to do this in C#.
I've found tutorials on retrieving the file name (sans extension) via JavaScript. I think I can still get the city even if the form is in an iframe, but I'd like to keep to the site's conventions and use C# if possible.
Code snippets or links to possible solutions would be much appreciated.
if you want modify the value of your input in c# associated to your aspx (Code behind), you must to add attributes runat=server to your input.
use this code in your aspx
<input id="test" type="hidden" runat="server"/>
and in your c#
test.Value = 123; //your value is 123 for example
Disclaimer, I don't know JQuery, so there could be easier ways to do this. I also haven't tested any code...
If you know the exact ID then you can do something like this from the parent page (in a javascript block):
var frame = document.getElementById('myIFrame');
var ctrl = frame.document.getElementById('myControl');
ctrl.value = "New Value";
If you don't know the exact ID's of the controls in the CityX.aspx pages, then you will either need a way for those ID's to be discovered, or you will need to go through all controls within the iframe looking for the correct one. (I say this because if the controls in the iframe pages are held in any sort of ASP.NET structure they will not be called txtMyCtrl (for instance) but possibly something like ct00_txtMyCtrl.)
If you don't know the EXACT control name (because of the ASP.NET structure I mentioned before), you could do something like:
var frame = document.getElementById('myIFrame');
var ctrls = frame.document.getElementByTagName("INPUT");
for(var i=0;i<ctrls.length;i++){
if(ctrls[i].getAttribute("type")=="hidden" && ctrls[i].id.indexOf("_myControl") != -1){
ctrls[i].value = "New Value";
break;
}
}
Or if you have the ability to update the CityX.aspx pages, then you could have the following in the CityX.aspx page:
function getCtrls(){
return [document.getElementById("<%=hiddenCtrl.ClientID%>"),
document.getElementById("<%=anotherHiddenCtrl.ClientID%>")];
}
... and then in your parent page, something like:
var frame = document.getElementById('myIFrame');
var ctrls = frame.document.getCtrls();
for(var i=0;i<ctrls.length;i++){
ctrls[i].value = "New Value";
}
They're just ideas on a general theme
I have created a web page that I use as a small dashboard to hold issue or no issue. It works great. The page uses an .aspx and .aspx.cs. I would like to be able to reuse the information on this page on other pages. My site already uses master pages and I have not been able to find an easy way to include this information.
How can I use an include from a page that has coding in the code behind easily?
Typically you use Web User Controls for this.
Web User Controls allow you to package up other controls into one that you can drop onto multiple pages. They are great for common UI items such as address entries, dashboards, etc. Basically anything that needs to be the same across multiple pages.
At the risk of seeming very obvious - do you mean usercontrols. These will allow you to reuse chunks of functionality across your site.
I guess this question falls into two categories: User Controls, and Code Reuse. Not sure which one you are after.
User Controls
If you are talking about the controls on your page you will want to create a common user control.
Code Reuse
You need to create a common class (whether it is static or not depends on how you intend to use it) and define functions within that class.
For instance, lets say you have a page that you want to print "Hello World!" on any aspx/.cs page.
You could do this
public static class MyClass
{
public string PrintHelloWorld()
{
return "Hello World!";
}
}
Then you call it from any of your pages like so:
MyClass.PrintHelloWorld();
Right click on the project > Add New Item...
Select User Control (.ascx)
Put your markup & code behind there.
Then you add that control in any other page (includding other controls [although I wouldn't recommend that])
It sounds like you may want to create an ascx User Control.
http://msdn.microsoft.com/en-us/library/ie/2x6sx01c.aspx