Why does my repeater keep crashing on Eval(NULL) values? - asp.net

<asp:Repeater ID="rptLessons" runat="server">
<ItemTemplate>
<tr>
<td><%#Eval("fullname")%></td>
<td><%#isCompleted(Eval("totallessons"), Eval("completedlessons"), Eval("totalNumAvail"), Eval("totalNumCorrect"))%></td>
<td><%#FormatPercent(Eval("totalnumcorrect") / Eval("totalNumAvail"))%> <%-- (<%#Eval("totalnumcorrect")%> / <%#Eval("totalNumAvail")%>) --%></td>
<td><%#FormatPercent(Eval("completedlessons") / Eval("totallessons"))%> <%-- (<%#Eval("completedlessons")%> / <%#Eval("totallessons")%>) --%></td>
<td><%#Eval("lastaccessed")%></td>
</tr>
</ItemTemplate>
</asp:Repeater>
I can't figure it out but as soon as it hits some NULL data it refuses to move on to drawing the next elements.

You need to give a stack trace to be sure.
But I can see several issues:
DIV#0 errors inside FormatPercent
NULL errors.
Example Solution
(System.Convert.ToInt32 should convert DBNull/NULL to 0)
Or alter isCompleted to accept Object paramters and do your NULL / DBNull checking inside the function.

On slightly different approach that might be helpful would be to do your computations in your code behind rather than inline in the markup. Just easier to check for nulls etc. I almost always go down this path with anything other than a simple Eval() in my markup.
<td>
<%#GetCorrectPercent()%>
</td>
protected string GetCorrectPercent()
{
if(Eval("totalnumcorrect") == null || Eval("totalNumAvail") == null)
return "n/a";
return ((int)Eval("totalnumcorrect") / (int)Eval("totalNumAvail")).ToString();
}
Not sure all the formatting is correct here but this should get you going in a different direction. Eval() will work within the called methods so long as the caller is current performing a DataBind().

If I had to guess, I would say that your isCompleted function doesn't handle values of Nothing. This is a guess because the function hasn't been listed in your example.

I tend more towards the explicit. Forgive any minor mistakes in the code, I'm not able to test this.
If in your markup you swap out those evals for literals then in the code behind:
If you have a collection of MyClass.
In your page's init event
this.rptLessons.OnItemDataBound += rptLessons_DataBound...
In the load or where ever you choose
this.rptLessons.DataSource = CollectionOfMyClass;
this.rptLessons.DataBind();
Then in that itemDataBoundEvent
MyClass myClass = (MyClass)ri.DataItem;
Literal litFullname = FindControl(ri, "litFullName");
litFullName.Text = myClass.Fullname;
This way you can cater for nulls etc in a more controlled way than using eval.

Related

How do you handle NULL values in DataBound controls?

I am trying to limit the characters displayed in ASP.NET Label controls that have their Text values databound to a SQL Database. If the field in the database is not NULL, I do not have any trouble. If they are NULL, I get the exception "Object reference not set to an instance of an object."
My code is as follows:
<asp:Label ID="LabelDescriptionLabel" runat="server" Text='<%# IIf(Not IsDBNull(Item.LabelDescription), IIf(Item.LabelDescription.ToString.Length > 30, Left(Item.LabelDescription, 30) & "...", Item.LabelDescription), Item.LabelDescription)%>' />
I have also tried the following, without any luck:
<asp:Label ID="LabelDescriptionLabel" runat="server" Text='<%# IIf(Not IsNothing(Item.LabelDescription), IIf(Item.LabelDescription.ToString.Length > 30, Left(Item.LabelDescription, 30) & "...", Item.LabelDescription), Item.LabelDescription)%>' />
I am using ASP.NET with VB.NET. I have tried using the CSS "text-overflow: ellipsis" method as well, but I had some table formatting issues that I have not resolved.
Is there a way to handle these NULL values and prevent run-time exceptions?
Thanks!
One problem you are having is that the old IIF is a function which evaluates all the parts before it decides what to return.
IIF(Not IsDBNull(Item.LabelDescription), TruePart, FalsePart)
It does not matter if the expression is true or not, the TruePart and
FalsePart statements will always be executed. Since they include references to something which may be DBNull/Nothing/null, you get an error.
Something else though is that Nothing is not the same as DBNull, and if Item is an Object which can be Nothing/null, then testing the description of Nothing/null will be problematic.
The If Operator has been available since abut 2008. The structure is the same but it only evaluates the test condition and the True or False expression which applies:
If( Condition, TruePart, FalsePart )
The "short circuiting" prevents the expression/Part which does not apply from being evaluated. As long as the rest of the code is valid, you should be able to just clip one of the Is from the IIF functions.
Since you want to the object Nothing I would use the Nothing keyword rather than the VB IsNothing function:
If(Item IsNot Nothing),
If(Item.LabelDescription.ToString.Length > 30, x, y), y)
The If operator is not the same as the If/Then/Else statement block.

Custom Repeater with hiractial Databinding

Im using a Custom NestedRepeater Control for ASP.NET which can be found on code project
The source is in c# which i have converted to vb and plugged into my solution, so far so good. The problem, im having is databinding to the repeater, my code behind looks like this...
'' get all pages
Dim navPages As DataSet = Navigation.getMenuStructure()
navPages.Relations.Add(navPages.Tables(0).Columns("ID"), navPages.Tables(0).Columns("ParentID"))
NestedRepeaterNavigation.RelationName = RelationName
NestedRepeaterNavigation.DataSource = navPages
NestedRepeaterNavigation.RowFilterTop = "ParentID is null"
NestedRepeaterNavigation.DataBind()
Then in the item template of my custom repeater im trying the following...
<ItemTemplate>
<img src="/pix.gif" height="10" width="<%#(Container.Depth * 10)%>">
<%# (Container.DataItem as DataRow)["DESCRIPTION"]%>
<%# (Container.NbChildren != 0 ? "<small><i>(" + Container.NbChildren.ToString() +")</i></small>" "") %><small><i></i></small>
</ItemTemplate>
The databinding falls over; firstly that 'as DataRow' says it was expecting an ')'. And secondly that '!=' identifier expected.
Is this due to the translation from c#, should the databinding be different?
Though I've not programmed in VB.net for long (about 3 years) but I know that AS is not applicable in VB.net you need ctype to cast Container.DataItem like
CType(Container.DataItem, DataRow).
you can also try DirectCast(Container.DataItem, DataRow) but I don't think this will work.
Also for inequality comparison you can use
Not Container.DataItem = 0
but not !=

How to use ASP.NET Resources in this way?

I am doing some labeling. I would like to add a resource key (s, sec) to one of my combo boxes with item[3sec, 5sec, 10sec, 30sec ...] and use it like:
Text="3<%$ Resources: myResource, s%>" to get comboBoxItem 3sec,
Text="5<%$ Resources: myResource, s%>" to get comboBoxItem 5sec ...
But I find that the server will treat this as plain text.
Do I need to define each item in a separate resource key pair?
Consider defining your resource file entry value as follows:
{0}sec
And then
String.Format(Resources.myResource.s, "3")
You can try creating your own custom expression builder. They are really powerful if you want to achieve what you have asked in your question.
Or, you can always do it using server-side code.
You could do it like this (with a DataBind() call somewhere):
Text='<%# "3" + Resources.myResource.s %>'
Edit: You can also do this from code behind. Something like this:
int[] times = new int[]{ 3, 5, 10, 30 };
foreach (int time in times)
{
string text = time.ToString() + Resources.myResource.s;
cbo.Items.Add(new ListItem(text, time.ToString()));
}
Edit 2: As per Muhammad's observation the first example doesn't work for this. I've used it for other controls, and I didn't see that for the current case it's not correct. Given this I would fill the control from code behind.

Filling multiple literals with the same value

I have multiple literals on a page. For example,
<asp:Literal id="Label1" runat="server" />
I have around 10 of these on one page and want to fill them all with the same value. Is there a good way to do that without referencing the id for each control?
Create a public property that holds the value you want to display and in the aspx use
<%= propertyName %>
everywhere you want to display the value.
If you're using resource files, you can set the meta:resourcekey attribute to the same value for all 10 of them.
The only way I've seen it done is make the id's a loopable name (e.g. Label1 - Label10) and loop through referencing the id. But we didn't try too hard for a different solution!
Are the Literals located in a single Container (for instance, a Panel) or are they littered about the page? If they are in a single container which does not contain too many other non-relevant controls, it would be as simple as looping through the Container's Controls collection.
Yes, I'm well aware that a Page is a Container as well, but it is almost always very inefficient to loop through all the controls in a Page. I wouldn't recommend it at all.
How are your labels named. If they have a common naming convention like Label1 - Label10 then you could do
for(int i = 1; i <= 10; i++)
{
Literal l = Page.FindControl(string.Format("Label{0}", i)) as Literal
if(l != null)
l.Text = "Whatever";
}
If they're not named similarly, you could just stick the label names in an array and loop through that instead of having 10 explicit .Text = statements. e.g.
string [] litNames = new string [] { "litFirst", "otherLit", "someOtherVar" }
foreach(string s in litNames)
{
Literal l = Page.FindControl(s) as Literal
if(l != null)
l.Text = "Whatever";
}

How can I tell if E4X expression has a match or not?

I am trying to access an XMLList item and convert it to am XML object.
I am using this expression:
masonicXML.item.(#style_number == styleNum)
For example if there is a match everything works fine but if there is not a match then I get an error when I try cast it as XML saying that it has to be well formed. So I need to make sure that the expression gets a match before I cast it as XML. I tried setting it to an XMLList variable and checking if it as a text() propertie like this:
var defaultItem:XMLList = DataModel.instance.masonicXML.item.(#style_number == styleNum);
if(defaultItem.text())
{
DataModel.instance.selectedItem = XML(defaultItem);
}
But it still give me an error if theres no match. It works fine if there is a match.
THANKS!
In my experience, the simplest way to check for results is to grab the 0th element of the list and see if it's null.
Here is your code sample with a few tweaks. Notice that I've changed the type of defaultItem from XMLList to XML, and I'm assigning it to the 0th element of the list.
var defaultItem:XML =
DataModel.instance.masonicXML.item.(#style_number == styleNum)[0];
if( defaultItem != null )
{
DataModel.instance.selectedItem = defaultItem;
}
OK I got it to work with this:
if(String(defaultItem.#style_number).length)
Matt's null check is a good solution. (Unless there is the possibility of having null items within an XMLList.. probably not, but I haven't verified this.)
You can also check for the length of the XMLList without casting it to a String:
if (defaultItem.#style_number.length() > 0)
The difference to String and Array is that with an XMLList, length() is a method instead of a property.

Resources