Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations bkrike on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

DataGrid, DataList, or something else?

Status
Not open for further replies.

xtremeLogic

Programmer
Dec 19, 2003
53
CA
Hi,

I have a page that keeps track of shipping information. I have everything laid out in a small table for one destination. Inside the table I have some server controls (dropdowns labels, linkbuttons, etc.) relevent to one destination. I would like to add multiple shipping destinations (ie additional tables) based on user selection. Similarly, I would also like to have the option of deleting a shipping destination. So in the end, if a user has specified 3 different shipping destinations complete with all selections, I would like to insert each destination into the db in succession. I already have the stored procedure ready to input one destination. I'm guesssing I have to run a loop (length # of desinations) and repeatedly call the stored procedure.

I was thinking of using a DataGrid or DataList but I'm not too clear as to what would work as I do not have anything databound in my table. Any help on this would be greatly appreciated. Thanks.
 
Sounds to me like the perfect application for a custom IEnumerable collection object bound to a datalist. IEnumerable classes can be bound to any databound control.

DataLists give you (IMHO) the greatest degree of control over your output while still maintaining the incredible ease of databound controls. (Repeater has the most control, but it's more difficult to use).

So, basically, you would code up a class that would encapsulate information about shipping destinations, and then create a collection class (that implements IEnumerable) that would allow you to bind them directly to a datalist.

Destination & Destinations, for example.

Then, you could declare your ItemTemplates to be whatever you wanted (a table in your case which would have all the dropdowns, etc...).

Here's a great article on writing custom collections. It's actually quite simple.


Good luck!
:)
paul

penny.gif
penny.gif

The answer to getting answered -- faq855-2992
 
Thanks for the help link9!


I'm not sure as to the whole process of creating a custom collection and binding it to a dataList. Also, how would I be able to add and delete rows from the dataList using this structure? I would really appreciate it if someone could show me a snippet of code or something to help me see it more clearly.

Also, how can I do nested sublists using the ienumerable structure (ie. in a shipping destination I would like to store multile order details). Would I create another custom class and set that as a property of my main class?

Any help would be greatly appreciated.
 
That's exactly right about the second collection class that becomes a property of the other class, which would then be accessible OnDataBound for each and every item.

To add or delete rows from the datagrid, you simply add .Delete() and .Add() methods to your class.

It's really conceptually no different than binding to a datatable or anything else... you would need to set your .DataKeyField property of your datagrid to the unique id for your items (Which would be a property of the Destination class), and then when someone clicked delete, you would simply retrieve that value and call .Delete on the object. Rebind the datagrid, and that's it.

If you check out the article I gave you a link to, it will be very clear how to create the collection. Once you have the collection, binding it is easy. You simply set the .DataSource property of the datagrid to the collection class, so if you had a collection class called destinations, which took a SqlConnection as an argument to its constructor (so it would have a way of populating itself), then some code might look like this:

Destinations myDestinations = new Destinations(myCon);
myDataGrid.DataSource = myDestinations;
myDataGrid.DataBind();

As long as Destinations implements IEnumerable, the above code will work just fine.

Setting up your columns is also equally simple. Let's say that the Detination class has two properties, Name & Address. Some code might look like this:

<asp:TemplateColumn HeaderText="Name">
<ItemTemplate>
<asp:Label id=lblName Runat=Server Text='<%# ((Destination)Container.DataItem).Name %>' />
</ItemTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="Address">
<ItemTemplate>
<asp:Label id=lblName Runat=Server Text='<%# ((Destination)Container.DataItem).Address %>' />
</ItemTemplate>
</asp:TemplateColumn>

You'll need to just start plugging away at it. It's a very nice way to handle things, and I'm sure you'll like it because you take the DataTable out of the picture, and hook your databound controls directly to your business objects, creating a tighter integration while adding a finer grain of control at the same time.

If you have specific questions about the method, post back to this thread, and we can work through them.

:)
-paul

penny.gif
penny.gif

The answer to getting answered -- faq855-2992
 
I have created my custom IEnumerable collection thanks to the article you gave the link to.

I can bind the collection to my datgrid but I am still having trouble with the add and delete methods. I'm not sure still exactly how to implement them. For the add method, Everytime I select the add button, I get an error: Object reference not set to an instance of an object. I think, this is because, the controls are created in the datagrid and you can't reference them right off the bat. I'm thinking for the delete method I would need to know which row to delete in my custom class. But how do I create a unique ID for each row in my collection? For the time being I am manually incrementing the ID. I have posted my code. I appreciate any help. Here is some code of my custom class:
Code:
Public Class SetInfo
    Private _SetID As Integer
    Private _Qty As Integer
    Private _Name As String = ""
    Private _NumberOriginals As Integer
    Private _ProcessMedia As Integer
    Private _Scaling As Integer
    Private _Binding As Integer

Public Sub New(ByVal setID As Integer, ByVal setQty As Integer, ByVal setName As String, ByVal numOriginals As Integer, ByVal processMedia As Integer, ByVal scaling As Integer, ByVal binding As Integer)
        _SetID = setID
        _Qty = setQty
        _Name = setName
        _NumberOriginals = numOriginals
        _ProcessMedia = processMedia
        _Scaling = scaling
        _Binding = binding
    End Sub
.
.
End Class
And here is the code for my other class:
Code:
Public Class TransmittalSetDetails
    Implements IEnumerable

    Private t_setDetails As New ArrayList

    Public Sub New()
    End Sub

    Public ReadOnly Property Count() As Int32
        Get
            Return t_setDetails.Count
        End Get
    End Property

    Public Sub AddSet(ByVal transmittalSet As SetInfo)
        t_setDetails.Add(transmittalSet)
    End Sub

    ' Impelementation of IEnumerable
    Public Function GetEnumerator() As IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
        Return New SetEnumerator(t_setDetails)
    End Function

    '********************************************************************************************************
    ' Inner class
    Private Class SetEnumerator
        Implements IEnumerator

        Private t_sets As ArrayList
        Private t_position As Int32 = -1

        Public Sub New(ByVal transmittalSetDetails As ArrayList)
            t_sets = transmittalSetDetails
        End Sub

        ' Implementation of IEnumerator
        Public ReadOnly Property Current() As Object _
            Implements System.Collections.IEnumerator.Current

            Get
                Return t_sets(t_position)
            End Get

        End Property

        ' Implementation of IEnumerator
        Public Function MoveNext() As Boolean _
            Implements System.Collections.IEnumerator.MoveNext

            t_position += 1

            If t_position >= t_sets.Count Then
                Return False
            Else
                Return True
            End If

        End Function

        ' Implementation of IEnumerator
        Public Sub Reset() _
            Implements System.Collections.IEnumerator.Reset

            t_position = -1

        End Sub

    End Class
End Class
And the main class:
Code:
Public Class TransmittalInfo
    Private _orderDetailsID As Integer
    Private _name As String = ""
    Private _destinationID As Integer
    Private _deliveryMethodID As Integer
    Private _setDetails As New TransmittalSetDetails

    Public Sub New()
    End Sub

    Public Property TransmittalSetDetails() As TransmittalSetDetails
        Get
            Return _setDetails
        End Get
        Set(ByVal Value As TransmittalSetDetails)
            _setDetails = Value
        End Set
    End Property

    Public Sub AddSet(ByVal transmittalSet As SetInfo)
        _setDetails.AddSet(transmittalSet)
    End Sub
End Class
And Finally, my codebehind for the shipping page (contains the datagrid calls):
Code:
    Protected WithEvents setDetailsGrid As New System.Web.UI.WebControls.DataGrid
    ' Create an instance of our custom class
    Protected myTransmittalInfo As New TransmittalInfo

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
   If Not Page.IsPostBack Then
       ' Add Initial Set with default values to the transmittal
       myTransmittalInfo.AddSet(New SetInfo(1, 1, "Current Set " & Now(), CStr(Session("ItemsInCart")), 0, 3, 0))
       bindSetGrid()
   End If
End Sub

    Private Sub bindSetGrid()
        ' Bind the custom collection to a grid
        setDetailsGrid.DataSource = myTransmittalInfo.TransmittalSetDetails
        setDetailsGrid.DataBind()
    End Sub
    Private Sub setDetailsGrid_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles setDetailsGrid.ItemCommand
        If "AddSet" = CStr(e.CommandName) Then
            myTransmittalInfo.AddSet(New SetInfo(2, CInt(txtNumSets.Text.Trim()), "Current Set " & Now(), CStr(Session("ItemsInCart")), 0, 3, 0))
        ElseIf "RemoveSet" = CStr(e.CommandName) Then
            'No Implementation yet...
        End If
        bindSetGrid()
    End Sub
 
What object has the NULL reference when that error is thrown?

I suspect that it's the txtNumSets, but I need to know that before delving deeper into how to avoid the exception.

As for deleting, you can set the .DataKeyField property of your setDetailsGrid to "_SetID", but you'll need a public accessor on your SetInfo class so that the DataGrid can use it. You may already have one that isn't posted.

Code:
~~~~~
        setDetailsGrid.DataSource = myTransmittalInfo.TransmittalSetDetails
        setDetailsGrid.DataKeyField = "_SetID"
        setDetailsGrid.DataBind()
~~~~~

Then, OnDelete, just ask for setDetailsGrid.DataKeys(e.Item.ItemIndex) to retrieve that _SetID for the item that was clicked.

Code:
        ElseIf "RemoveSet" = CStr(e.CommandName) Then
            dim deleteID As Int = Convert.ToInt32(setDetailsGrid.DataKeys(e.Item.ItemIndex).ToString())
            setInfo.Delete(deleteID)
        End If

The above code assumes that you've declared a .Delete() method on that setInfo class that takes a setID as an argument and knows how to delete it from the data store.

=======================

Another separate question: Why do you have three classes? It seems to me that you would only need two, but maybe I'm missing something.

One class holds information about the shipping destinations, has the requisite .Add(), .Delete(), & .Modify() methods on it which operate on their respective destinations, and then the collection class, which would simply serve as the container for one or many shipping destinations. It would also have .Add() and .Remove() methods, but they would only serve to add or remove objects to/from the collection, rather than data to the datastore (which would be the job of the setInfo class).

Could you explain your logic behind the current layout? I think I could help more if I understood what you were getting at.

Let me know.

-paul

penny.gif
penny.gif

The answer to getting answered -- faq855-2992
 
Hi,

txtNumSets is the object that triggers the null reference error. I tried hardcoding some numbers as default values for the controls but everytime I change them and enter new values and press the add button, I lose the selections.

As for the delete method, I see what you mean by referencing the _SetID. My question is that when I create a SetInFo object, how do I set the _SetID property to be unique. Also, when I am binding to the datagrid, where is the _setID being stored for a specific row. Finally, how do I actually implement the delete method in my collections class. Conceptually, I know that all you need is the _setID to reference the right row, but I'm still not sure how to perform the actualy delete operation.

As for the three classes, let me explain this better with a screen shot of my page.

On my page, I can have multiple transmittals (the blue box on the screen). The user can add or delete a transmittal (but you have to have at least 1). Now within a transmittal, there are various properties (location, delivery method, set details). Also, within a transmittal, a user can create multiple set details(grey box; must have at least 1 row).

After your original suggestion. I decided to make the setDetails Table a custom collection bound to a datagrid. Now, I'm thinking to make my transmittal table another custom collection bound to a datalist (as it has more formatting flexibility). I haven't implemented this custom collection yet, but i think it should be the exact same thing as my inner collection just different properties.

I know these are a lot of questions, but I was wondering if you could show me some snippets or how to solve this issue of mine. I really appreciate your help.

Thanks
 
I can't view the .jpg you referenced. It's in a protected directory.

About the txtNumSets being NULL, is it a page object, or is it a child control of the datagrid??? Maybe it would be clearer if you put that picture somewhere that I can see it.

My question is that when I create a SetInFo object, how do I set the _SetID property to be unique

Are the locations not stored in a database somewhere w/ unique id's already assigned? That would be my first thought... that they probably should be, and then you just use the id from the database to uniquely identify the locations. That's the most solid solution.

Also, when I am binding to the datagrid, where is the _setID being stored for a specific row

It's stored in the .DataKeys collection of the datagrid. Please reference the code I posted above regarding that.

Finally, how do I actually implement the delete method in my collections class

The delete method should be implemented in the SetInfo class to actually remove the location. Where is your data stored? I'm confused, because to delete something from a database, you simply code up a DELETE statement and execute it. Maybe we're just not on the same page here.

You can implement a .Remove() method in your collection class to remove the object from the collection by either using the .Remove() method of the ArrayList (which is the underlying collection), or you can code up your own if that won't work.

ArrayList.Remove() takes an object as an argument and iterates the collection until it finds the reference you passed, and then removes it.

so:

public sub Remove(object itemToRemove)
t_setDetails.Remove(itemToRemove)
end sub

would be how to do it in the collection class, but I'm afraid that there are just too many variables for me to be able to do the .Delete() on your SetInfo class. I don't know what your underlying data structure is.

-paul

penny.gif
penny.gif

The answer to getting answered -- faq855-2992
 
Thanks for the response. Sorry, I didn't realize that the jpg was protected. It seems that I can't send the link out. I'll give you my login details for my account.
user: xtremeLogic
pass: tektips

Once you login, go into myphotos and select screenshots and view the full screen version. Hopefully, once you see my shot you'll realize what i'm trying to accomplish.

txtNumSets is a childcontrol of the datagrid. Its value is saved to the _qty property of the SetInfo class.

Now I'm a bit confused as to how the class will interact with the database. I thought that my two custom collections would keep track of transmittal information and set information respectively and then when the user goes to the next page, I'll insert the values into the two tables: Transmittals & Sets. You were saying that I should grab the ID value and assign it to the _setID property in my SetInfo class.

I'm not sure what you mean by the underlying structure of my SetInfo class. Based on the article link you sent, my innermost class, SetInfo is like the student class, and I don't see any underlying structure. Similarly, my TransmittalSetDetails is like their Students class as it implements IENumerable. It uses an ArrayList of SetInfo objects. Then my TransmittalInfo class is like their ClassRoom class except that I would like to have multiple transmittal objects. So I will probably have to redo the same chain of classes for TransmittalInfo.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top