27

I have a repeater that displays data from my Projects table. There are projectId, name and description. I use Substring(1, 240) on description. But sometimes the string is shorter than 240, so I get ArgumentOutOfRangeException. Can you tell me how to display the whole text if I get the exception. This is my code.

    <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<asp:Panel ID="pnlDisplayProjects" runat="server" Visible="true">
    <center><h2><b>Проекти</b></h2></center>
        <asp:Repeater ID="rptrProjects" runat="server">
            <HeaderTemplate>
                <table border="1" cellpadding="2" cellspacing="2" align="center" width="80%" style="background-color:#F7F6F3;">
            </HeaderTemplate>
            <ItemTemplate>
                <tr>
                    <td align="left" style="width:40px">
                        <asp:Label ID="LblProjectId" runat="server" Text='<%# Eval("ProjectID") %>' />
                    </td>
                    <td align="center">
                        <asp:Label ID="LblName" runat="server" Text='<%# Eval("Name") %>' />
                    </td>
                </tr>
                <tr>
                    <td colspan="2">
                        <asp:Label ID="LblDescription" runat="server" Text='<%# Eval("Description").ToString().Substring(1, 240) + "..." %>'/>
                        <asp:HyperLink ID="HlMore" runat="server" NavigateUrl='<%#"~/Project/ViewProject.aspx?projectId=" + Eval("ProjectID") %>' Text="More" />
                    </td>
                </tr>
            </ItemTemplate>
            <FooterTemplate>
                </table>
            </FooterTemplate>
        </asp:Repeater>
</asp:Panel>

 protected override void OnPreRender(EventArgs e)
    {
        var table = Projects.GetTableWithProjects();

        if (table.Rows.Count > 0)
        {
            rptrProjects.DataSource = table;
            rptrProjects.DataBind();
        }
        else
        {
            pnlDisplayProjects.Visible = false;
            Master.PrintMessage("There are no projects.");
        }
    }

12 Answers 12

52

I would suggest you write a separate extension method if you're using .NET 3.5. Something like this:

public static string SafeSubstring(this string text, int start, int length)
{
    return text.Length <= start ? ""
        : text.Length - start <= length ? text.Substring(start)
        : text.Substring(start, length);
}

Basically that's a version of Substring which will never throw an exception unless start or length is negative (in which case I don't know what it could sensibly return).

You'd call it like this:

Eval("Description").ToString().SafeSubstring(1, 240) + "..."

The downside of this is that it will include the ellipsis (...) even if it's not actually truncated the string at all...

Sign up to request clarification or add additional context in comments.

5 Comments

@Phil: Care to give an example which fails? Just "I suspect this doesn't work" isn't particularly helpful. (I've just fixed a couple of problems.)
Shouldn't you be checking if text is null. Otherwise this 'Safe' method will throw!
@superlogical: Well it depends on how you want it to behave. It's "safe" in terms of the index. I'd probably check whether text is null, but only to throw ArgumentNullException instead :)
@JonSkeet, just what I was looking for. Cheers.
it's a classic extension!
46
string dec = "description";
string result = dec.Substring( 0, dec.Length > 240 ? 240 : dec.Length )

3 Comments

Just a sanity check - SubString should be Substring, right?
The wasted processing when dec.Length <= 240 is negligible when validating UI input, but could be potentially costly in a loop (particularly in space).
Or just dec.Length <= 240 ? dec : dec.Substring(240)
21
description.Substring(0, Math.Min(description.Length, 240));

2 Comments

Yeah Math.Min for the win. Preferable over a ternary.
Worth adding the null conditional operator ?.Substring(0, Math.Min(description.Length, 240));
5

Based on Jon Skeet's answer, I think it should be checking for null or else it's not exactly a safe method :)

public static string SafeSubstring(this string text, int start, int length)
{
    if (text == null) return null;      

    return text.Length <= start ? ""
        : text.Length - start <= length ? text.Substring(start)
        : text.Substring(start, length);
}

1 Comment

.net library methods generally don't check for nulls, they just blow up with an exception if passed one. It's the callers responsibility to check and should behave similarly for consistency.
3

An extension method:

public static string SafeSubstring(this string text, int start, int length)
{
   if (start >= text.Length)
      return "";            
   if (start + length > text.Length)
      length = text.Length - start;         
   return text.Substring(start, length);
}

Comments

2

You can use LINQ as below:

string someString = "abcde"; 
string subStr = string.Join("", someString.Take(240));

1 Comment

Note, this method is going to be more inefficient due to string to chararray conversion (ie copy) then Take() then reconvert to string. Substring internally skips the middle step.
1

It's a bit of a hack, but the simplest method would be to change:

Eval("Description").ToString().Substring(1,240)

to

Eval("Description").ToString().PadRight(240).Substring(1, 240)

I'm not sure about the performance considerations on this, though, if therre are a lot of items.

Comments

0

If you REQUIRE 240 characters in that place for some reason, you can cast that field coming out of the database as a char(241) in which case it will always be 241 characters, with padding to the right if the content of the field was shorter than 241 characters. Then you can still strip off the first character with the Substring(1, 240) as you are right now.

Although, I must wonder if you're not wanting to do a Substring(0, 240) which wouldn't throw an exception if the string was shorter than 240 characters, and would start at the beginning of the string, rather than the 2nd character.

Comments

0

Another option is to write a a method that you call so your eval turns into:

<%# GetString(Container.DataItem) %>

So in the method you could check to make sure that the item is NOT null because you may bomb out calling .ToString on a NULL item. Then you can evaluate the container item and pass in the length to the substring call.

Comments

0
Text='<%# Eval("Description").ToString().Substring(1, Math.Min(240, Eval("Description").ToString().Length - 1)) + "..." %>'

Comments

0

Let's try to keep this simple...

We only need to truncate to a given max length, so how about we call it what it is:

description.TruncateTo(240);

The extension method that enables the above (ellipsis is appended by default if truncated):

public static class StringExtensions
{
    public static string TruncateTo(this string val, int maxLength, bool ellipsis = true)
    {
        if (val == null || val.Length <= maxLength)
        {
            return val;
        }

        ellipsis = ellipsis && maxLength >= 3;
        return ellipsis ? val.Substring(0, maxLength - 3) + "..." : val.Substring(0, maxLength);
    }
}

Comments

0

Using .Net 5 or higher.

I recommend using:

yourText.Substring(0, Math.Min(yourText.Length, 100));

This logic will use the smallest value between the comparisons, as it returns the smallest of two numbers. As per MS documentation.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.