I know, I know; but...

G

Guest

I could never get a solution to my delimma, so I'm trying in a last
(hopefully not futile) attempt to get past this damned issue!!! I would
REALLY appreciate someone helping my dumb-ass out here.

Alright, I have an asp.net program I started writing. I created a class
called tank, with some subclasses (round and square). I want to instantiate
this tank-type object at runtime, and assign it member values when the user
selects something. I keep getting an error about my instances referring to a
null object because the instances don't exist until the user makes a
selection at runtime. I tried this several ways, creating a class file and
just putting them directly under the agitatorSz namespace, but still seems to
be some kind of scope issue.

Here's my class code and my application code:

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace agitatorSz
{
/// <summary>
/// Summary description for WebForm1.
/// </summary>
public class main_frm : System.Web.UI.Page
{
public System.Web.UI.WebControls.Button tankType_btn;
protected System.Web.UI.WebControls.Button dimensions_btn;
protected System.Web.UI.WebControls.TextBox impellers_txt;
protected System.Web.UI.WebControls.TextBox agitators_txt;
protected System.Web.UI.WebControls.Label volume_txt;
protected System.Web.UI.WebControls.Label turnover_txt;
protected System.Web.UI.WebControls.Label gpm_txt;
protected System.Web.UI.WebControls.DataGrid product_grd;
protected System.Web.UI.WebControls.TextBox length_txt;
protected System.Web.UI.WebControls.TextBox width_txt;
protected System.Web.UI.WebControls.TextBox diameter_txt;
protected System.Web.UI.WebControls.TextBox depth_txt;
protected System.Web.UI.WebControls.Button model_btn;
protected System.Web.UI.WebControls.DropDownList model_ddl;
protected System.Web.UI.WebControls.Button powerSrc_btn;
protected System.Web.UI.WebControls.DropDownList power_ddl;
protected System.Web.UI.WebControls.TextBox maxMudLvl_txt;
protected System.Web.UI.WebControls.DropDownList tankType_ddl;

private void Page_Load(object sender, System.EventArgs e)
{
// Put user code to initialize the page here
}

#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tankType_btn.Click += new
System.EventHandler(this.tankType_btn_Click);
this.model_btn.Click += new System.EventHandler(this.model_btn_Click);
this.Load += new System.EventHandler(this.Page_Load);

}
#endregion

private void tankType_btn_Click(object sender, System.EventArgs e)
{
tankTypeVar=tankType_ddl.SelectedItem.Value;

switch(tankType_ddl.SelectedItem.Value)
{
case "1":
{
roundTank rt = new roundTank();
rt.orientation="test1";
//enable controls relevant to class, disable others
diameter_txt.Enabled=true;
length_txt.Enabled=false;
width_txt.Enabled=false;
break;
}
case "2":
{
squareTank st = new squareTank();
st.orientation="test2";
//enable controls relevant to class
length_txt.Enabled=true;
width_txt.Enabled=true;
diameter_txt.Enabled=false;
break;
}
}
}

private void model_btn_Click(object sender, System.EventArgs e)
{
if(rt!=null)
{
depth_txt.Text=rt.orientation;
}
if(st!=null)
{
depth_txt.Text=st.orientation;
}
}
}
}

------------
class code
------------

using System;

namespace agitatorSz
{
/// <summary>
/// Summary description for tank.
/// </summary>
public class tank
{
public tank()
{
}
public float powerSource;
public string orientation; //horizontal or vertical (values are: H or V)
public float depth;
public float maxMudLvl;
public int agitatorQty;
public int impellerQty;
public float mudVolume;
public float turnoverRatio;
public float GPM;
}

public class roundTank:tank
{
public roundTank()
{
}
public float diameter;
}

public class squareTank:tank
{
public squareTank()
{
}
public float length;
public float width;
}
}
 
M

Marina

Without looking through your code too much (very long piece there), my guess
would be that the object is getting instantiated on one request to the page.
And on a different request to the page (i.e. next click or something), you
are looking for it to be there.

Well, web applications dont' work that way (nothing to do with asp.net in
particular). Each request to a page will create a new instance of that page
and run through its code. That object is then destroyed. It doesn't stay
around somehow, and knows to match it up on the next request from the user.

So you have to recreate this object every time the page loads and you expect
for it to be there.
 
D

David Veeneman

I may be missing something, but have you thought about deferring
instantiation until the user makes a selection? If I understand correctly,
you know you will need a Tank object, but you won't know which type of Tank
until the user selects. So instantiate at that point; it should eliminate
the null object errors.

Alternatively, if there are behaviors you absolutely, positively have to
initialize before the Tank object gets instantiated, you might be able to
use something similar to a Strategy pattern. Encapsulate the early behaviors
in a separate TankHelper class, then have the Tank delegate to the
TankHelper for the specified behaviors. Personally, I'd be reluctant to do
this unless I really had to-- I'm far more comfortable deferring
instantiation.
 
G

Guest

Thank you, that's good information. Obviously, this asp.net stuff is new to
me. Do you have any ideas as to how I can keep the object static or retain
its values as the user makes selections?
 
M

Marina

You have to recreate it every time, or stored it in session. If your object
is serializable, you might be able to store it in viewstate.

I suggest before you go too much further that you familiarize yourself with
the model of web programming. I think it is important to understand that,
before trying to do too much. The concepts are just very different from that
of windows programming.
 
J

Jason Schultz

You can store the tank is a Session variable. That way, you can store
the object, and pass it around from page to page.
It would look something like this: Session["tank"] = rt; or
Session["tank"] = st;

Also, for your derived tank types (roundTank and squareTank). I would
do something like this:

public class Tank
{
// Make all variables protected, and create accessors
}

public class RoundTank : Tank
{
}

public class SquareTank : Tank
{
}

Then, when you are ready to instanciate the tank do the following:
Initialize a Tank instance to null before you actually create it.
Tank t = null;
switch(ddl_tank)
{
case(1)
t = roundTank();
break;
case(2)
t = squareTank();
break;
}
Then you can set the session after that
Session["tank"] = t;
 
G

Guest

That's great input. Thanks a lot. In fact, that seems to be exactly what I'm
looking for. Does this "session variable" have to be a variable, or can it be
a class instance?
 
S

Steve Walker

In message said:
You can store the tank is a Session variable. That way, you can store
the object, and pass it around from page to page.
It would look something like this: Session["tank"] = rt; or
Session["tank"] = st;

I tend to wrap those up, too:

private Tank CurrentTank
{
get
{
Tank t = Session["tank"] as Tank;
if(t == null)
{
t = new Tank(); //or whatever
Session["tank"] = t;
}
return t;
}
set
{
Session["tank"] = value;
}
}

So that I can write

Tank rt = this.CurrentTank;

etc, without worrying about the session key, or casting to Tank, or (if
appropriate) tank being null.

Also, because I make a lot of use of custom controls which access common
data, I would tend to do:

public interface ITankPage
{
Tank CurrentTank{get;set;}
}

public class EditTank : System.Web.UI.Page,ITankPage
{
[....]
}

so that, in a control, I can write;

ITankPage page = this.Page as ITankPage;
if(page!=null)
{
this.Text = page.CurrentTank.Name;
}
 
J

Jason Schultz

You can pass either primative types (int, float, etc) or objects
(string, or any class that you instanciate).
 
S

Steve Walker

Steve Walker said:
In message said:
You can store the tank is a Session variable. That way, you can store
the object, and pass it around from page to page.
It would look something like this: Session["tank"] = rt; or
Session["tank"] = st;

I tend to wrap those up, too:

private Tank CurrentTank

^^^^^^^^
Insert appropriate accessibility scope, obviously.
 
M

Marina

I think the CurrentTank property could be a static property in some non Page
class. That way you could always say:

SomeClass.CurrentTank

And it would always get you the current user's tank, since it is getting it
from session.

Is it really necessary to go through the whole interface thing? It also
means, you have to re-create the CurrentTank property for every page that
you might need it on.

Steve Walker said:
In message said:
You can store the tank is a Session variable. That way, you can store
the object, and pass it around from page to page.
It would look something like this: Session["tank"] = rt; or
Session["tank"] = st;

I tend to wrap those up, too:

private Tank CurrentTank
{
get
{
Tank t = Session["tank"] as Tank;
if(t == null)
{
t = new Tank(); //or whatever
Session["tank"] = t;
}
return t;
}
set
{
Session["tank"] = value;
}
}

So that I can write

Tank rt = this.CurrentTank;

etc, without worrying about the session key, or casting to Tank, or (if
appropriate) tank being null.

Also, because I make a lot of use of custom controls which access common
data, I would tend to do:

public interface ITankPage
{
Tank CurrentTank{get;set;}
}

public class EditTank : System.Web.UI.Page,ITankPage
{
[....]
}

so that, in a control, I can write;

ITankPage page = this.Page as ITankPage;
if(page!=null)
{
this.Text = page.CurrentTank.Name;
}
 
S

Steve Walker

Jason said:
You can pass either primative types (int, float, etc) or objects
(string, or any class that you instanciate).

With a small proviso; if you're storing state out of process (SQLServer
or StateServer) your objects must be serializable. In process is the
default.
 
J

Jason

Correction. You can pass any object that inherits from ISerializable.
So that is every primitive data type, as well as DataSet, DataTable,
HashTable, Image.

You can do the same with your Tank class, just do the following:

public class Tank : ISerializable
{
}

public class RoundTank : Tank
{
}

public class SquareTank : Tank
{
}
Then you should be able to assign your tank object to a session.
 

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

Top