DataMember Missing

Posts   
 
    
Sam avatar
Sam
User
Posts: 95
Joined: 30-Jun-2004
# Posted on: 10-Jan-2005 18:01:00   

I am trying to crate an asp.net tree view of Categories and Subcategories. I was using the design time databinding in vs 2003. The code basically looks like this (binding to Infragistics WebTree):


objCategoryCollection.GetMulti(null);
objCategoryCollection.Sort((int)CategoryFieldIndex.Description, ListSortDirection.Ascending);
treeCategories.Levels[0].RelationName = "CategorySubCategory";
treeCategories.Levels[0].LevelKeyField = "Categoryid";
treeCategories.Levels[0].ColumnName = "Description";
treeCategories.Levels[0].LevelImage = "Images/ig_treeOfficeFolder.gif";
treeCategories.Levels[1].LevelKeyField = "Subcategoryid";
treeCategories.Levels[1].ColumnName = "Description";
treeCategories.Levels[1].LevelImage = "Images/scrollRight.gif";
//the problem
treeCategories.DataMember = "Category";
//
treeCategories.DataSource = objCategoryCollection;
treeCategories.DataBind();

When it is run I get a "Object reference not set to an instance of an object" error. So I tried binding to a datagrid and going into the property builder and saw that there was no datamembers to choose? I get the same behavior if I create objCategoryCollection in code without using the design time object? This code did work at one time. I have made a few changes so I cannot be sure but it seems to have broken after I updated llblgen and re generated the dal. If I comment out the line:


treeCategories.DataMember = "Category";

The tree binds but I get four items (the number of categories) in the root of the tree that says "CategoryEntity" (no subcategories as I believe it cannot see the "CategorySubCategory" relationship). Again this was working correctly when I was able to specify the DataMember property. Any ideas?

PS Could be a problem with my code and not llblgen pehaps I need to define a relation in the GetMulti() method? I could have had that in there when it was working? But adding the fallowing doesnt fix it:


IRelationCollection relations = new RelationCollection();
relations.Add(DAL.EntityClasses.CategoryEntity.Relations.SubCategoryEntityUsingCategoryid);
objCategoryCollection.GetMulti(null, relations);

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 10-Jan-2005 21:31:52   

I experienced the same thing with the infragistics webgrid: I couldn't select datamembers. I had a vanilla .net webgrid on the same page and I could select datamembers with that one. I'm completely unaware what causes this problem, as it works in the normal .net grid.

When you ran into the exception, what was the stacktrace exactly?

Frans Bouma | Lead developer LLBLGen Pro
Sam avatar
Sam
User
Posts: 95
Joined: 30-Jun-2004
# Posted on: 10-Jan-2005 21:54:02   

Strange. Seems like binding collection objects to Infragistics controls doesn't word that great. You can bind to a grid but don't get any of the design time stuff so it is basically pointless. Also, It is possible that when I had it working before binding to a dataset that I created instead of the collection object which may have never worked. It has been a while since I worked on this thing. Here is the stack trace. Thanks for taking a look at this.

[NullReferenceException: Object reference not set to an instance of an object.] Infragistics.WebUI.UltraWebNavigator.DataBinding.BindList(IEnumerable datasource, String columnName, LeafCollection leafs) Infragistics.WebUI.UltraWebNavigator.DataBinding.DataBind(IEnumerable dataSource, String dataMember, LeafCollection leafs) Infragistics.WebUI.UltraWebNavigator.UltraWebTree.DataBind() ExpenseReimbursment.GUI.Controls.CategoriesTree.LoadCategoryTree() in c:\inetpub\wwwroot\expensereimbursment.gui\controls\categoriestree.ascx.cs:114 ExpenseReimbursment.GUI.Controls.CategoriesTree.Page_Load(Object sender, EventArgs e) in c:\inetpub\wwwroot\expensereimbursment.gui\controls\categoriestree.ascx.cs:41 System.Web.UI.Control.OnLoad(EventArgs e) System.Web.UI.Control.LoadRecursive() System.Web.UI.Control.LoadRecursive() System.Web.UI.Control.LoadRecursive() System.Web.UI.Control.LoadRecursive() System.Web.UI.Control.LoadRecursive() System.Web.UI.Control.LoadRecursive() System.Web.UI.Control.LoadRecursive() System.Web.UI.Control.LoadRecursive() System.Web.UI.Control.LoadRecursive() System.Web.UI.Control.LoadRecursive() System.Web.UI.Control.LoadRecursive() System.Web.UI.Control.LoadRecursive() System.Web.UI.Page.ProcessRequestMain()

confused I have tried removing the Infragistics tree and adding a vanilla DataGrid. Still no DataMembers. Is it possible that this is because I am dealing with a UserControl rather than a Page?

Update: Tried to add vannila grid to Page (not user control). Still no DateMember.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 11-Jan-2005 10:20:21   

Hmmm. I too don't see datamember values listed, just datasource. (It's been a while since I tested it in asp.net). I do know something was different with the winforms databinding logic so this must be it then. Infragistics indeed doesn't work. That never worked, I couldn't get it to work. There is no documentation on this, and trial-error didn't bring me very far here. I really don't know why there are no datamembers as I supply the propertydescriptors to vs.net like I do in winforms(there is no difference).

Frans Bouma | Lead developer LLBLGen Pro
Sam avatar
Sam
User
Posts: 95
Joined: 30-Jun-2004
# Posted on: 11-Jan-2005 16:21:15   

The thing is that even if I explicitly define a datamember it does not work. DataGrid.DataMember = "SomeTable" always gives me the same result no matter if I change the DataMember to a different value? How should I proceed?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 11-Jan-2005 17:12:37   

I'm afraid you're out of luck at the moment disappointed I'm sorry. I don't know what else to produce for visual studio to make it show datamember values which actually are used. Indeed, you can specify a name, but the designer of the grid will not pick up the properties of the member.

I did test if it requested the property descriptors of the collection in a member and it didn't. So vs.net simply didn't request the information needed to produce columns, so I can't give it to vs.net... I've decompiled dataview, all designers of it etc. but I couldn't find any piece of evidence I missed something. There is also no documentation on this. I mailed microsoft about this 7 months ago and they replied 3 times that they were busy with it, but I still haven't heard anything. I ran into various problems with this, like cyclic references weren't handled correctly by vs.net (it didn't check if a property descriptor received was already seen) but they didn't respond on that either so I don't know what else to do. disappointed

As a workaround, you could drag a collection onto the form which is of the same type as the collection of the member, then design the grid with that (infragistics won't work, forget that) and at runtime bind to the member, not the collection you used to design the columns.

Frans Bouma | Lead developer LLBLGen Pro
Sam avatar
Sam
User
Posts: 95
Joined: 30-Jun-2004
# Posted on: 11-Jan-2005 19:28:25   

Sound good. Not really a problem I can take or leave the design time features. What I really want to do with this is be able the hierarchical information to a control/controls without creating my own dataset. For example if I have a CategoryCollection that has a SubCategory's how do I bind the CategoryCollection to one control and the corresponding SubCategoyies to another (or the same) control? Here is the example you have in your help file:


// [C#]
// create the collection of customer entities
ShipperEntity shipper = new ShipperEntity(1);
CustomerCollection customers = new CustomerCollection();
ISortExpression sorter = new SortExpression(SortClauseFactory.Create(CustomerFieldIndex.City, SortOperator.Ascending));
customers.GetMultiManyToManyUsingShippers(shipper, 0 , sorter);
// bind it to the master datagrid.
_master.DataSource = customers;
// bind it to the detail as well and make sure orders are shown
_detail.DataSource = customers;
_detail.DataMember = "Orders";

This is basically exactly what I want to do but instead of a one to many relationship I have a many to many relationship. I want to display all categories and their subcategories so basically I have written the fallowing code:


objCategoryCollection.GetMulti(null);
objCategoryCollection.Sort((int)CategoryFieldIndex.Description, ListSortDirection.Ascending);
treeCategories.DataSource = objCategoryCollection;
treeCategories.Levels[0].RelationName = "CategorySubCategory";
treeCategories.Levels[0].LevelKeyField = "Categoryid";
treeCategories.Levels[0].ColumnName = "Description";
treeCategories.Levels[0].LevelImage = "Images/ig_treeOfficeFolder.gif";
treeCategories.Levels[1].LevelKeyField = "Subcategoryid";
treeCategories.Levels[1].ColumnName = "Description";
treeCategories.Levels[1].LevelImage = "Images/scrollRight.gif";
treeCategories.DataMember = "Category";
treeCategories.DataSource = objCategoryCollection;
treeCategories.DataBind();

When I run this a get the error I mentioned earlier. The problem line is treeCategories.DataMember = "Category". Not a designtime issue but a runtime issue. Is there a problem with my code that it is not fetching some of the information I am expecting when I call the GetMulti method? In you above example you do _detail.DataMember = "Orders" where Orders is the name of the table? Would the table name I want to bind to not be "Category"? Also what should I set the RelationName property to? Normally if this was a dataset I add and specify the relation name to the dataset and set it up that way. I guess I need to know when binding to a collection object what do I put in for the DataMember and ReleationName strings?

Mainly I am confused about the DataMember property I just tried binding to a vanilla asp.net grid with the fallowing code


objCategoryCollection.GetMulti(null);
objCategoryCollection.Sort((int)CategoryFieldIndex.Description, ListSortDirection.Ascending);
DataGrid1.DataMember = "SubCategory";
DataGrid1.DataBind();

The result is the same as if I never specified the DataMember property I get the categories binded to the grid instead of the subcategories. Again, is there some thing wrong with the way that I am fetching the data?

Thanks in advance!

I also wanted to let you know how pleased that I have been with the product and the support. It is hard for me that one man has time to write all that code, figure out all those problems and still have time to answer my stupid questions. Thanks!!!

Devildog74
User
Posts: 719
Joined: 04-Feb-2004
# Posted on: 12-Jan-2005 11:07:28   

Here is my two cents worth on infragistics web design time data binding.

Dont use it.

Create an object that is serializable that you can put into the Node.Tag object. Use this object to know where you are.

I have attached code that I use to map from LLBLGen entity collections to Infragistics Tree views. You can use this code with the northwind db.

I have found that the easiest way to use an infragistics web tree is to use a recursive function to pass an entity collection and build a serializable object that can be associate with each tree node.

I hoe you find the code useful.

** Filling the tree**


        private ArrayList employeeTags;

        private void Page_Load(object sender, System.EventArgs e)
        {

            if (! Page.IsPostBack)
            {
                InitializeControl();
            }
        }


        /// <summary>
        /// This method coordinates building the tree from scratch on 
        /// demand.
        /// </summary>
        private void InitializeControl()
        {
            // clear the previous session
            CleanSession();

            // clear the tree
            UltraWebTree1.Nodes.Clear();
    
            // fetch data from the DAL
            GetData();
    
            // set employee count
            lblCount.Text = "Total Employees = " + employeeTags.Count.ToString();
    
            // build the root node
            BuildRootNode();
        }

        /// <summary>
        /// Creates the very first node in the tree
        /// </summary>
        private void BuildRootNode()
        {
            Node rootNode = new Node();
            rootNode.Text = "Northwind Employees";
            rootNode.ShowExpand=true;

            // create a temp tag object for the root node since its not in the DB
            EmployeeTag tempTag = new EmployeeTag();
            tempTag.EmployeeName = "Northwind Employees";
            tempTag.EmployeeId = 0;
            tempTag.ReportsTo = 0;

            rootNode.Tag = tempTag;

            // build the tree
            BuildTree(rootNode);

            UltraWebTree1.Nodes.Add(rootNode);
        }


        /// <summary>
        /// Fetch the data from the DAL, create an ArrayList of 
        /// Tag objects for use with the tree
        /// </summary>
        private void GetData()
        {
            employeeTags = new ArrayList();

            EntityCollection employees = EmployeeController.EmployeesCollection();

            foreach(EmployeesEntity employee in employees)
            {
                employeeTags.Add( new EmployeeTag(employee));
            }

            Session.Add("EmployeeTags", employeeTags);
        }

        /// <summary>
        /// Clean any old session values
        /// </summary>
        private void CleanSession()
        {
            Session.Remove("EmployeeTags");
            Session.Remove("RemovedTag");
        }

        /// <summary>
        /// For the current node, parse the ArrayList of Tag objects.
        /// 
        /// Add all related nodes on demand to the node demanding load.
        /// </summary>
        /// <param name="node">The node in which we are loading child nodes for</param>
        private void BuildTree(Node node)
        {
            EmployeeTag currentTag = node.Tag as EmployeeTag;
            employeeTags = Session["EmployeeTags"] as ArrayList;

            if (currentTag != null && employeeTags != null)
            {
                for (int i = 0; i < employeeTags.Count ; i++)
                {
                    EmployeeTag tempTag = (EmployeeTag)employeeTags[i];
                    if (currentTag.EmployeeId == tempTag.ReportsTo)
                    {
                        Node childNode = new Node();
                        childNode.ShowExpand=true;
                        childNode.Tag = tempTag;
                        childNode.Text = tempTag.EmployeeName;
                        node.Nodes.Add(childNode);
                    }
                }

                if (node.Nodes.Count > 0)
                {
                    node.ShowExpand=true;
                }
                else
                {
                    node.ShowExpand=false;
                }
            }
        }


** Using the trees postback / server side events**

        /// <summary>
        /// Handles the node removed event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UltraWebTree1_NodeRemoved(object sender, Infragistics.WebUI.UltraWebNavigator.WebTreeNodeEventArgs e)
        {
            if (e.Node.Tag != null)
            {
                // place the tag from the removed node into the session
                Session.Add("RemovedTag", e.Node.Tag);
                lblRemovedNode.Text = ((EmployeeTag)e.Node.Tag).EmployeeName;
            }
        
        }

        /// <summary>
        /// Handles the demand load event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UltraWebTree1_DemandLoad(object sender, Infragistics.WebUI.UltraWebNavigator.WebTreeNodeEventArgs e)
        {
            // add any child nodes to the current node
            BuildTree(e.Node);
        }


        /// <summary>
        /// Handles the node clicked event for the tree.
        /// 
        /// Here we report values on the currently selected node
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UltraWebTree1_NodeClicked(object sender, Infragistics.WebUI.UltraWebNavigator.WebTreeNodeEventArgs e)
        {
            if (e.Node != null  && e.Node.Tag != null)
            {
                EmployeeTag tag = e.Node.Tag as EmployeeTag;
                lblEmpId.Text  = tag.EmployeeId.ToString();
                lblEmpName.Text = tag.EmployeeName.ToString();
                lblReportsTo.Text = tag.ReportsTo.ToString();
            }
        }
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 12-Jan-2005 11:34:25   

Sam wrote:

Sound good. Not really a problem I can take or leave the design time features. What I really want to do with this is be able the hierarchical information to a control/controls without creating my own dataset. For example if I have a CategoryCollection that has a SubCategory's how do I bind the CategoryCollection to one control and the corresponding SubCategoyies to another (or the same) control? Here is the example you have in your help file:


// [C#]
// create the collection of customer entities
ShipperEntity shipper = new ShipperEntity(1);
CustomerCollection customers = new CustomerCollection();
ISortExpression sorter = new SortExpression(SortClauseFactory.Create(CustomerFieldIndex.City, SortOperator.Ascending));
customers.GetMultiManyToManyUsingShippers(shipper, 0 , sorter);
// bind it to the master datagrid.
_master.DataSource = customers;
// bind it to the detail as well and make sure orders are shown
_detail.DataSource = customers;

_detail.DataMember = "Orders";

This is basically exactly what I want to do but instead of a one to many relationship I have a many to many relationship. I want to display all categories and their subcategories so basically I have written the fallowing code:


objCategoryCollection.GetMulti(null);
objCategoryCollection.Sort((int)CategoryFieldIndex.Description, ListSortDirection.Ascending);
treeCategories.DataSource = objCategoryCollection;
treeCategories.Levels[0].RelationName = "CategorySubCategory";
treeCategories.Levels[0].LevelKeyField = "Categoryid";
treeCategories.Levels[0].ColumnName = "Description";
treeCategories.Levels[0].LevelImage = "Images/ig_treeOfficeFolder.gif";
treeCategories.Levels[1].LevelKeyField = "Subcategoryid";
treeCategories.Levels[1].ColumnName = "Description";
treeCategories.Levels[1].LevelImage = "Images/scrollRight.gif";
treeCategories.DataMember = "Category";
treeCategories.DataSource = objCategoryCollection;
treeCategories.DataBind();

When I run this a get the error I mentioned earlier. The problem line is treeCategories.DataMember = "Category". Not a designtime issue but a runtime issue. Is there a problem with my code that it is not fetching some of the information I am expecting when I call the GetMulti method? In you above example you do _detail.DataMember = "Orders" where Orders is the name of the table?

No, "Orders" is the property in each 'Customer' entity in the collection bound. That's how databinding of hierarchies works (2-level deep only). You bind a collection of customers, and you specify that you want to see the datamember "Orders" in the grid, which means that the grid will display for the current customer in the other grid (where you bind the same customer collection but you don't specify any datamember so the grid will show the objects in the collection bound) the entities in its Orders collection in the second grid.

I'm not sure if this even works in ASP.NET as it requires a live CurrencyManager on the form, which keeps track of related objects bound to various controls. I'm not sure if this is available in asp.net as the forms are read-only objects which dissapear once they're rendered and sent to the client.

So a master-detal grid setup in asp.net with customers bound to grid 1 and customers[currentRowIndex].Orders bound to grid 2.

Would the table name I want to bind to not be "Category"? Also what should I set the RelationName property to? Normally if this was a dataset I add and specify the relation name to the dataset and set it up that way. I guess I need to know when binding to a collection object what do I put in for the DataMember and ReleationName strings?

Well, you bind a category collection to the grid, so datamember should be the name of the field mapped onto the Category - SubCategory relation, for example SubCategories (don't know how you called that field). I have the feeling "Category" isn't a property of teh entities in the bound category collection.

Mainly I am confused about the DataMember property I just tried binding to a vanilla asp.net grid with the fallowing code


objCategoryCollection.GetMulti(null);
objCategoryCollection.Sort((int)CategoryFieldIndex.Description, ListSortDirection.Ascending);
DataGrid1.DataMember = "SubCategory";
DataGrid1.DataBind();

The result is the same as if I never specified the DataMember property I get the categories binded to the grid instead of the subcategories. Again, is there some thing wrong with the way that I am fetching the data?

No, I think you expect functionality to be present in asp.net which isn't there: there is no live currency manager which checks which collection to bind as there is no live interaction between user and form: if the user scrolls down to the second row in the parent grid, the child grid has to be updated via a postback, which simply results in:

// bind childgrid: int parentIndex = parentGrid.GetCurrentRowIndex(); childGrid.DataSource = _parents[parentIndex].ChildCollection; childGrid.DataBind();

I also wanted to let you know how pleased that I have been with the product and the support. It is hard for me that one man has time to write all that code, figure out all those problems and still have time to answer my stupid questions. Thanks!!!

smile simple_smile Your questions aren't stupid, and it's my pleasure to help you simple_smile Sometimes keeping up with all the work is challenging but I wouldn't trade for any other job in the world simple_smile

Devildog: thank you for your massive answer, as always simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Sam avatar
Sam
User
Posts: 95
Joined: 30-Jun-2004
# Posted on: 12-Jan-2005 16:14:31   

Thanks Frans and DevilDog. DevilDog: the code is exactly what I was looking for. I will try it out. Two questions: Where is the definition for the employee EmployeeTag object? I am guessing this is the serializable object that you are talking about and I would like to see how to build it. Also, I see that you have a master detail both of the same entity type (employee). Am I right in thinking that I will have to have two enitities, collections, tag objects (one for each entity) and then relate them together in the BuildTree Method? Is this right?


private void GetData()
{
    categoryTags = new ArrayList();
    subCategoryTags = new ArrayList();
    objCategoryCollection.GetMulti(null);
    objSubCategoryCollection.GetMulti(null);
    foreach(DAL.EntityClasses.CategoryEntity category in objCategoryCollection)
    {
        categoryTags.Add( new categoryTag(category));
    }
    foreach(DAL.EntityClasses.SubCategoryEntity subCategory in objSubCategoryCollection)
    {
        subCategoryTags.Add( new subCategoryTag(subCategory));
    }
    Session.Add("CategoryTags", categoryTags);
    Session.Add("SubCategoryTags", subCategoryTags);
}

Does this look right for the two serializable objects?


[Serializable]
private class CategoryTag
{
/// <summary>
/// Creates a new <see cref="CategoryTag"/> instance.
/// </summary>
public CategoryTag(){}
/// <summary>
/// Creates a new <see cref="CategoryTag"/> instance.
/// </summary>
/// <param name="category">Category.</param>
public CategoryTag(CategoryEntity category)
{
    _Categoryid = category.Categoryid;
    _Description = category.Description;
}
private int _Categoryid;
public int Categoryid
{
    get
    {
        return _Categoryid;
    }
    set
    {
        _Categoryid = value;
    }
}
private string _Description;
public string Description
{
    get
    {
        return _Description;
    }
    set
    {
        _Description = value;
    }
}
}
[Serializable]
private class SubCategoryTag
{
/// <summary>
/// Creates a new <see cref="SubCategoryTag"/> instance.
/// </summary>
public SubCategoryTag(){}
/// <summary>
/// Creates a new <see cref="SubCategoryTag"/> instance.
/// </summary>
/// <param name="subCategory">Sub category.</param>
public SubCategoryTag(SubCategoryEntity subCategory)
{
    _SubCategoryid = subCategory.Subcategoryid;
    _Categoryid = subCategory.Categoryid;
    _Description = subCategory.Description;
}
private int _SubCategoryid;
public int SubCategoryid
{
    get
    {
        return _SubCategoryid;
    }
    set
    {
        _SubCategoryid = value;
    }
}
private int _Categoryid;
public int Categoryid
{
    get
    {
        return _Categoryid;
    }
    set
    {
        _Categoryid = value;
    }
}
private string _Description;
public string Description
{
    get
    {
        return _Description;
    }
    set
    {
        _Description = value;
    }
}
}


Thanks again to the both of you.

Sam avatar
Sam
User
Posts: 95
Joined: 30-Jun-2004
# Posted on: 12-Jan-2005 19:10:29   

Nevermind...Figured it out. Thanks again! Above code works!

Devildog74
User
Posts: 719
Joined: 04-Feb-2004
# Posted on: 12-Jan-2005 21:00:49   

NP, I just got your email and sent you the entire source for the sample.