0

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.

James
  • 2,013
  • 3
  • 18
  • 31
  • @Liath yes, mySpan is null, thanks for pointing that out, I added it to the original question. – James Jul 17 '14 at 07:46
  • Personally I use this.Page.Master as SiteMaster; – Liath Jul 17 '14 at 07:47
  • can you also add how you're getting mySpan? Presumably sm.mySpan? – Liath Jul 17 '14 at 07:48
  • @Liath What do you mean exactly? Inside the method "MyMethod" I simply have the line I posted in the question (which is inside my Site.Master.cs file). mySpan is available according to VS2012 but it is always null when I run the code. – James Jul 17 '14 at 07:50

2 Answers2

2

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.

Liath
  • 9,913
  • 9
  • 51
  • 81
  • I really enjoyed your point of view, a totally different approach from mine and it's client side :) – Alireza Jul 17 '14 at 08:10
  • 2
    I think you may have just hit the nail on the head from the aspect of "before it's being sent to your browser". Due to the nature of the ajax call (on page load) I forgot about the fact that the page is already loaded (in my case, it's loaded, not the other way round) so obviously I can't make any changes to the master page. I won't bother going into the details of what I was actually trying to do, but the only way to resolve what I want to do is return a variable from the AJAX call and update the page via jQuery as you stated. Your comments helped push me in the right direction, thanks! – James Jul 17 '14 at 08:15
  • 1
    @James glad we could help – Liath Jul 17 '14 at 08:16
2

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;
Alireza
  • 4,976
  • 1
  • 23
  • 36
  • 1
    Great answer - I'm still not 100% what the OP is trying to do but I think between the two of us we've covered most of the options! – Liath Jul 17 '14 at 08:08
  • Agreed! A vast range of options – Alireza Jul 17 '14 at 08:12
  • @Alireza this is a very interesting option. I'm thinking of a simple solution which is the following: When the user logs in I create the session for "mySpanString". This value can be updated whenever I call the current update of the mySpan (which works from the content page) but I can also force the update in the logic layer using HttpContext.. and then, when returning all AJAX calls, I simply check the value of the mySpanString session and return it with all current AJAX calls, then use jQuery to update. Not exactly what I originally imagined, but could work... what do you think? – James Jul 17 '14 at 08:25
  • 1
    It will work. The important thing to keep in mind is the first and second paragraphs of my answer. The rest are implementation details and depend on your design. In this special case, @Liath's answer was better than mine because that's what you want to do on an AJAX request, and that approach need not a `Session` variable. However you can mix the two methods: his to update the span immediately after the request and mine to keep it updated "after" the page is refreshed from the server. – Alireza Jul 17 '14 at 08:33
  • @Alireza The problem is that Liath's answer offers no solution for me. I have three different types of cases which can require me to update the span. On page load (server side), on page load (ajax call) and after page load (n ajax calls). This means the only method to allow all three cases to work is to use a Session. – James Jul 17 '14 at 08:43
  • Then you should mix them. Use my method in the server-side and keep the updated value in the session scope. This will solve your page load issue. Then you can use that value inside your AJAX calls for client-side updating – Alireza Jul 17 '14 at 08:53