asp.netvb.netdevexpressaspxgridview

How to handle nulls with Eval


I can have a record with all filled in fields and then without an SO_ID or SO_Num. I want my eval to be able to handle these and just return a '-' in the grid column when this happens while still returning all other data for that row. I've tried other solutions online and couldn't find one that works.

<dx:GridViewDataColumn FieldName="SO_Num" VisibleIndex="19" runat="server" Caption="Sales Order Number">
<DataItemTemplate>
 <a id="clickElement" href="../sales/order/view.aspx?ID=<%# Eval("SO_ID").ToString()%>"><%#Eval("SO_Num").ToString()%></a>
 </DataItemTemplate>
</dx:GridViewDataColumn> 

Solution

  • You can use a in-line "iif(), and thus this:

                    <asp:TemplateField HeaderText="City">
                        <ItemTemplate>
                            <asp:TextBox ID="txtCity" runat="server" 
                                Text = '<%# IIf(IsDBNull(Eval("City")), "-", Eval("City")) %>'
                                ></asp:TextBox>
                        </ItemTemplate>
                    </asp:TemplateField>
    

    So, in above, if say Eval("City") is null, then we display a "-", else we display Eval("City")

    Edit: display of values and some button to click and navgate are DIFFERENT!!

    As I pointed out, if you need a Eval() in the GridView, and want to convert a null say into a "-", then do that (but, I fail to see why I would want to display some "-" in the GV. Why do that?? Seems rather strange to me?

    However, if you have a button on the GV row, and you want to click on that button to jump or navigate to some other page? Fail to see how such a button click and navigate has ANY REALATIONSHIP to what we display? Why are the two concepts connected? I fail to see any sensible logic here?

    If you want to drop in a pane jane button, or even a link button (no difference here), then wire up a click event for that given button you drop in.

    so, say in our GV, we drop in a button to view a given row of data. Say this GV with hotels, and a button.

            <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
                CssClass="table table-hover" Width="50%"
                DataKeyNames="ID" >
                <Columns>
                    <asp:BoundField DataField="FirstName" HeaderText="FirstName"  />
                    <asp:BoundField DataField="LastName" HeaderText="LastName" />
                    <asp:TemplateField HeaderText="Hotel Name">
                        <ItemTemplate>
                            <asp:Label ID="txtHotel" runat="server"
                                Text='<%# Eval("HotelName") %>' >
                            </asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
                    <asp:BoundField DataField="Description" HeaderText="Description" />
    
                    <asp:TemplateField HeaderText="View">
                        <ItemTemplate>
                        <asp:Button ID="cmdView" runat="server"  Text="View" CssClass="btn "
                          />
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
                <PagerStyle CssClass="pagenavi" />
            </asp:GridView>
    

    so, we just dropped in a plane jane button for operations on that one row.

    So, our code to load is this:

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
        If Not IsPostBack Then
            LoadGrid()
        End If
    
    End Sub
    
    Sub LoadGrid()
    
        Using conn As New SqlConnection(My.Settings.TEST4)
            Dim strSQL As String =
                "SELECT * from tblHotelsA ORDER BY HotelName"
            Using cmdSQL As New SqlCommand(strSQL, conn)
                conn.Open()
                Dim rst As New DataTable
                rst.Load(cmdSQL.ExecuteReader)
                GridView1.DataSource = rst
                GridView1.DataBind()
            End Using
        End Using
    
    End Sub
    

    And our results are now this:

    enter image description here

    Ok, so now lets wire up that plane jane button click.

    As a normal rule, you can double click on a button to build the click event, or bring up the property sheet, choose events tab/section, and then add the click event. However, since the button is in the GV, then we have to add the click event this way (in the markup).

    Type in OnClick=, and when you hit the "=" sign, intel-sense will popup a dialog to create the event.

    You get this:

    enter image description here

    So, we select create new event. Don't seem like anything occurred, but flipping to code behind, we have a click event stub, and our code thus can be this:

    Protected Sub cmdView_Click(sender As Object, e As EventArgs)
    
        Dim btn As Button = sender
        Dim gRow As GridViewRow = btn.Parent.Parent
        Dim intPKID As Integer = GridView1.DataKeys(gRow.RowIndex).Item("ID")
    
        Debug.Print("Row click index = " & gRow.RowIndex)
        Debug.Print("Row click database PK id = " & intPKID)
    
        ' now do whatever you want with this row information.
    
        ' to get values from non templated columns, use cells()
        ' to get values from tempated columns, use findcontrol
    
        'eg:
    
        ' get last name (Boundfield)
    
        Debug.Print("Last name = " & gRow.Cells(1).Text)
    
        ' get hotel name - template - "label"
    
        Dim lblHotel As Label = gRow.FindControl("txtHotel")
    
        Debug.Print("Hotel name (label) = " & lblHotel.Text)
    
    
    End Sub
    

    output:

    enter image description here

    So, as noted, I fail to see why ANY issue occurs here in regards to some data in a column of the GV being null?

    In your case, just navigate based on the button click to anything you want, based on any value you want.

    say like this:

    <asp:TemplateField HeaderText="View"> <asp:Button ID="cmdView" runat="server" Text="View" CssClass="btn " OnClick= "cmdView_Click" CommandArgument = '<%# Eval("SO_ID") %>' /> </asp:TemplateField>

    And then in code behind:

    Protected Sub cmdView_Click(sender As Object, e As EventArgs)
    
        Dim btn As Button = sender
        Dim gRow As GridViewRow = btn.Parent.Parent
        Dim intPKID As Integer = GridView1.DataKeys(gRow.RowIndex).Item("ID")
    
        Debug.Print("Row click index = " & gRow.RowIndex)
        Debug.Print("Row click database PK id = " & intPKID)
    
        Dim intSOID = btn.CommandArgument
    
        Dim strURLJumpTo = "../sales/order/view.aspx?ID=" & intSOID
    
        Response.Redirect(strURLJumpTo)
    

    So, you are free to cook up any URL navagation you want.

    NOTE VERY close how I used the data keys feature of the GV. That allowed me to have, use, get, play with the database PK row id, but NEVER do I have to expose or show or have or mess with the database PK id in the actual GV markup.

    This is not only nice, but also is a HUGE deal from a security point of view, since then the user, the browser (client side) thus NEVER has to see, or know or can munge or play with the database PK row id - it is 100% server side managed.

    And in fact if you SO_ID or whatever values are not in the GV, or the user does not care? Then I would NOT pass the values in the URL as so called "query parms" of the URL, but would in fact pass the values in session() like say this:

        Session("OrderID") = intPKID
    
    
    
        Response.Redirect("../sales/order/view.aspx")
    

    Then in the page load event of the target page, I do this:

    and NEVER EVER forget to check/use the ispostback in your page load event.

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
        If Not IsPostBack Then
    
            ViewState("OrderID") = Session("OrderID")
    

    Now, in that code behind, and that page, you use this to get/use/have the orederid

     dim intOrderID = ViewState("OrderID")
    

    So, on page load (first page, ispostback = false), you transfer the session() value to the ViewState. And you do this since the user might have more then one copy of the browser running - and thsu using session() to pass the value is ok, but session() is global to the ONE user, where as ViewState is per page. So, that's why we transfer to ViewState on page load, since FROM THAT POINT onwards in that page, and code behind, we use ViewState. If we used session() in the code behind, then it is global, and if more then one copy of the browser is running or even multiple tables, they will all have the same session() value.

    So, say you click on a house to view or buy?

    Well, then they might open another tab - display same GV, and click on a different row. If we use session, you now display two pages - but both have the one and same row PK id value - and if you click buy house, you get the wrong house if your code behind uses session(), but with ViewState, the issue does not exist.

    And thus, you can even dump your ugle "id" and parameters out of the URL - they look much nicer, but are also much more secure, and the user thus does not have to see, or know about things such as database row PK junk and stuff.