A bit stuck with properties in a user control

A

Alan Silver

Hello,

I'm trying to write a simple user control as an exercise (and 'cos it
might be useful). I am trying to do a simple date picker, which consists
of three drop down lists, one for the day, one for the month and one for
the year. I know this isn't earth-shattering stuff, but I'm still
learning, so please be patient!!

I'm having trouble with the properties of the control. I have created
the control OK and it displays fine. I added a method that sets the
range for the year drop down, and in the Form_Load event, I check to see
if it's been called, and if not, I use a default year range. All fine so
far.

Now, I wanted to add a property for the day, so that this could be set
or got. I did the following ...

public int Day {
get {
return System.Convert.ToInt32(drpDay.SelectedItem.Text);
}
set {
if (Day<=0 || Day>31) {
drpDay.SelectedIndex = 0;
} else {
drpDay.SelectedIndex = Day - 1;
}
}
}

where drpDay is the name of the drop down for the day.

In the page I was using to test this, I did the following ...

void Page_Load(Object sender, EventArgs e) {
if (!IsPostBack) {
dtpDate.SetYearRange(1990, 2000);
dtpDate.Day = 3;
}
}

which gave a compile error on the line dtpDate.Day = 3;, saying "Object
reference not set to an instance of an object". I'm not sure what the
problem is as drpDate definitely exists, and the Day property exists and
is public.

Anyone able to explain what I did wrong? TIA. The full code is shown
below in case it is needed.

Thanks,

Alan

DatePicker.ascx
============
<script runat="server" Language="C#">

void Page_Load(Object sender, EventArgs e) {
if (!IsPostBack) {
int i;
for (i=1; i<=31; i++) {
drpDay.Items.Add(i.ToString());
}
if (drpYear.Items.Count == 0) {
SetYearRange(DateTime.Now.Year, DateTime.Now.Year+10);
}
}
}

public void SetYearRange(int StartYear, int EndYear) {
int i;
for (i=StartYear; i<=EndYear; i++) {
drpYear.Items.Add(i.ToString());
}
}

public int Day {
get {
return System.Convert.ToInt32(drpDay.SelectedItem.Text);
}
set {
if (Day<=0 || Day>31) {
drpDay.SelectedIndex = 0;
} else {
drpDay.SelectedIndex = Day - 1;
}
}
}

</script>

<asp:DropDownList ID="drpDay" Runat="Server" />&nbsp;<asp:DropDownList
ID="drpMonth" Runat="Server"><asp:ListItem Text="January"
/><asp:ListItem Text="February" /><asp:ListItem Text="March"
/><asp:ListItem Text="April" /><asp:ListItem Text="May" /><asp:ListItem
Text="June" /><asp:ListItem Text="July" /><asp:ListItem Text="August"
/><asp:ListItem Text="September" /><asp:ListItem Text="October"
/><asp:ListItem Text="November" /><asp:ListItem Text="December"
/></asp:DropDownList>&nbsp;<asp:DropDownList ID="drpYear" Runat="Server"
/>





TestDatePicker.aspx
===============
<%@ Page Language="C#" Debug="true" %>
<%@ Register TagPrefix="Pixata" TagName="DatePicker"
Src="DatePicker.ascx" %>

<script runat="server">
void Page_Load(Object sender, EventArgs e) {
if (!IsPostBack) {
dtpDate.SetYearRange(1990, 2000);
dtpDate.Day = 3;
}
}
</script>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Test of my date picker custom control</title>
</head>

<body>
<form runat="server">
<Pixata:DatePicker ID="dtpDate" Runat="Server" />
</form>
</body>
</html>
 
K

Karl Seguin

Alan,
I know you said that it was, but I'm almost sure drpDay doesn't exist.. :)
make sure it's declared as a protected or public field and ther isn't a
typo...also, this seems like it could/will cause problems:

drpDay.SelectedIndex = Day - 1;


Day will recursively call itself...don't you mean value - 1 ?

Karl
 
A

Alan Silver

Hello,

Just to answer my own question (in case it's of use to anyone), I found
out that the properties were being set before the Page_Load event had
fired. As I was populating the drop down for the day in that event, when
the property was being set, the drop down was empty. Presumably the
error I saw was because the collection of list items had not been set to
anything.

Sorted now.
 
A

Alan Silver

Alan,
I know you said that it was, but I'm almost sure drpDay doesn't exist.. :)
make sure it's declared as a protected or public field and ther isn't a
typo

Yup, it exists. If you look at the code I posted, it is the first of the
three drop down lists in the user control.
...also, this seems like it could/will cause problems:

drpDay.SelectedIndex = Day - 1;

Day will recursively call itself...don't you mean value - 1 ?

Yup, I spotted that one after posting. That sorted out one of the
problems ;-)

I'm still trying to work out the best way to sort out this problem of
properties being set before the Page_Load event fires. I was wondering
about having three private variables, one for the selected year, one for
the start year and one for the end year. All would have initial values
of zero. In each of the three property codes, I would check to see if
all three of these are non-zero, and if they are, then set up the drop
down with the range of years and set the selected year. In the Page_Load
event, I could then check to see if the start and end years were set,
but not the selected year. If so, I would set up the drop down, but not
select a value.

I haven't tried this yet, but it seems reasonably straightforward, if a
little twisted for such a simple requirement. Is there a better way to
do it?

Thanks for the reply
 
K

Karl Seguin

Alan:
As you've learnt, you typically don't access controls within properties,
because they aren't necessarily available yet. Having private fields which
are exposed via public properties, and then using the values in those fields
during Page_Load is really the only/best way to go.

There's really two ways of dealing with your situation. You can assign
default values to the start and end year,which simplifies your code...in
your page_load you make use of these variables and if they've been set to
something other than the default, you really couldn't care any less. Or, if
you must have the values set (ie, you don't want to use defaults), you
typically check if they were set and if not throw an exception or atleast
hide the control.. Which you use depends on your exact needs...one isn't
better than another really..depends on how you want/need it to behave. I
find it a little tricky dealing with default values with nulls...because is
0 the default value or was it entered? In .net 2.0 we get nullable types
where you can assign null to a integer...thus making it easier to figure out
if it's a default value or if it's an actual user-entered one..

Karl
 
A

Alan Silver

As you've learnt, you typically don't access controls within properties,
because they aren't necessarily available yet. Having private fields which
are exposed via public properties, and then using the values in those fields
during Page_Load is really the only/best way to go.

Yup, spotted that one!!
There's really two ways of dealing with your situation. You can assign
default values to the start and end year,which simplifies your code...in
your page_load you make use of these variables and if they've been set to
something other than the default, you really couldn't care any less.
<snip>

That is a very sensible approach, and far better than the convoluted
method I was contemplating. It's so simple, I could kick myself for not
thinking of it[1]. I already have code in there to set default values,
so that's not a problem. I assumed that user controls would probably
always want these as you can never be sure if the user gave values for
every property. Apart from certain vital ones (like ID), I was assuming
defaults for everything.

Thanks for the reply, I think this should be a lot easier now.

Alan

P.S. If you have any ideas about binding to a check box control, please
could you look at the thread "How do I do data binding with a checkbox?"
which I posted yesterday, but unfortunately hasn't sparked anyone's
imagination yet ;-) I'm still stuck on that one.

[1] In the interests of honesty, I should point out that I am not in the
habit of kicking myself, even when it is highly deserved. I used the
expression as an example of the rich and idiomatic language we have ;-)
 
A

Alan Silver

There's really two ways of dealing with your situation. You can assign
default values to the start and end year,which simplifies your code...in
your page_load you make use of these variables and if they've been set
to something other than the default, you really couldn't care any less.

OK, I just thought of something whilst trying this out.

As I see it, properties are likely to be set in two places, either when
the control is specified, eg ...

<Me:MyControl ID="Fred" Year="2005" />

or at some point in the code.

My problem is that I'm not sure if your approach would handle both.
Suppose the public property for the year just set the private member
variable, and the Page_Load event for the control actually set the drop
down, then this would be fine for when the control first loads, but
wouldn't do anything if the property were changed later.

How do I handle this? I thought of having a private Boolean member
variable, m_Loaded, that is initially false, but set to true in the
Page_Load event to indicate that the control has loaded. The public
property would then do something like ...

m_Year = value; // save the value in the private member variable
if (m_Loaded) {
dropYear.SelectedItem = m_Year; // set the drop down
}

I think this would work, but it seems a little inelegant. I'm sure there
is a better way of doing it. Any thoughts? TIA

P.S. I know the code shown above won't actually set a drop down
correctly as it assumes that "value" contains a number that relates to
the index of the drop down's Item collection. I just used that code for
simplicity. I currently go through each item in the drop down looking to
see if the text matches. Again, this works, but seems inelegant. If you
know of a better way to set a drop down, given the Text value of one of
the items, please let me know. Thanks.
 
K

Karl Seguin

Alan:
You are correct, but I think you're worrying for nothing. It's true that if
you change the value after the control has run through it's logic than the
change won't automatically be reflected. But this typically won't
happen...you'll normally set the values in the Page_Load event of the page
and thus everything will time up nicely.

I appreciate that you are trying to learn and it's useful to look at all
scenarios, but do keep in mind that the simplest method will work most of
the time. An alternative solution coul be to:

(A) place the user control's ogic in the PreRender event...if anything
happens after this ur screwed anyways 'cuz it's already been rendered
(B) Use a function in the user control to handle the logic and call the
function from the page..so instead of passing variable you explicitely have
control over when it's done. RenderDates(startYear, EndYear, SelectedYear).
So long as you cal the function before/during PreRender all should be fine.

Karl
 
A

Alan Silver

Alan:
You are correct, but I think you're worrying for nothing. It's true that if
you change the value after the control has run through it's logic than the
change won't automatically be reflected. But this typically won't
happen...you'll normally set the values in the Page_Load event of the page
and thus everything will time up nicely.

Good point, I hadn't thought of that.

Thanks for the reply
 

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,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top