Welcome Guest Search | Active Topics | Sign In | Register

How to use ObjectDataSource with EO Menu? Options
Saed
Posted: Wednesday, October 15, 2008 1:37:15 PM
Rank: Advanced Member
Groups: Member

Joined: 10/15/2008
Posts: 45
Hi there,
I'm trying to use the exceptional EO Menu in a Web Project. So far I think I'm OK with grasping its functionality after spending considerable time.

Now I created an untyped DataSet (Menu.xsd) to retrieve menu data from Access database table and placed it into App_Code folder as VS 2005 suggested. Tried referencing it with GridView and worked OK. However, I can't seem to figure out how to bind EO Menu to said DataSet and dynamically populate entire table rows to in order to generate Menu entries. I'm using same Demo table field names but with different table name (called structure) and values. Below is the code I used:

globalMenu.ascx:
Code: Visual Basic.NET
<%@ Control Language="VB" AutoEventWireup="false"
CodeFile="globalMenu.ascx.vb" Inherits="mainMenu" %>
<%@ Register TagPrefix="eo" Namespace="EO.Web" Assembly="EO.Web" %>

<asp:ObjectDataSource 
    ID="menuData" 
    runat="server" 
    TypeName="MenuTableAdapters.structureTableAdapter"  
    SelectMethod="GetData" 
    DeleteMethod="Delete"
    InsertMethod="Insert" OldValuesParameterFormatString="original_{0}"
    UpdateMethod="Update">
    
    <DeleteParameters>
        <asp:Parameter Name="Original_FolderID" Type="Int32" />
    </DeleteParameters>
    
    <UpdateParameters>
        <asp:Parameter Name="ParentFolderID" Type="Int32" />
        <asp:Parameter Name="FolderName" Type="String" />
        <asp:Parameter Name="URL" Type="String" />
        <asp:Parameter Name="Original_FolderID" Type="Int32" />
    </UpdateParameters>
    
    <InsertParameters>
        <asp:Parameter Name="ParentFolderID" Type="Int32" />
        <asp:Parameter Name="FolderName" Type="String" />
        <asp:Parameter Name="URL" Type="String" />
    </InsertParameters>
    
</asp:ObjectDataSource>


<asp:GridView 
    ID="GridView1" 
    runat="server"
    DataSourceID="menuData" 
    DataKeyNames="FolderID"
    AutoGenerateColumns="true"
    ForeColor="#333333"
    GridLines="None"
    EmptyDataText="No data available.">
</asp:GridView>
 
  
<eo:Menu 
    ID="globalMenu" 
    runat="server"
    DataSourceID="menuData" 
    ControlSkinID="None" 
    CssFile="~/Styles/Menu.css">
    
    <TopGroup 
        Style-CssClass="L1_Menu" 
        Orientation="Horizontal">
    </TopGroup>

    <LookItems>
        <eo:MenuItem 
            ItemID="_TopLevelItem" 
            NormalStyle-CssClass="L1_Normal"
            DisabledStyle-CssClass="L1_Disabled"
            SelectedStyle-CssClass="L1_Selected"
            HoverStyle-CssClass="L1_Hover">
            <SubMenu 
                Style-CssClass="L2_Menu"
                CollapseEffect-Type="GlideTopToBottom" 
                ExpandEffect-Type="GlideTopToBottom"
                ItemSpacing="0" 
                LeftIconCellWidth="25" 
                SideImage="Office2003SideBar2"> 
            </SubMenu>
        </eo:MenuItem>
        
        <eo:MenuItem 
            ItemID="_Separator" 
            IsSeparator="True" 
            NormalStyle-CssClass="Separator">
        </eo:MenuItem>
        
        <eo:MenuItem 
            ItemID="_Default" 
            NormalStyle-CssClass="L2_Normal"
            DisabledStyle-CssClass="L2_Disabled"
            SelectedStyle-CssClass="L2_Selected"
            HoverStyle-CssClass="L2_Hover"
            Text-Padding-Right="30">
        </eo:MenuItem>
    </LookItems>

</eo:Menu>


globalMenu.ascx.vb:
Code: Visual Basic.NET
Partial Class mainMenu
            Inherits System.Web.UI.UserControl
        End Class


I thoroughly explored related Demos as well as Documentation, but still stuck. Tried implementing <Bindings> as suggested by Documentation but without much luck.

Code: Visual Basic.NET
<eo:Menu 
    ID="globalMenu" 
    runat="server"
    DataSourceID="menuData" 
    DataFields="FolderName"
           .
           .
           .
   
    </TopGroup>

        <Bindings> 
            <eo:DataBinding 
                Property="Value" 
                DataField="FolderName"> 
            </eo:DataBinding> 
        </Bindings>

    <LookItems>
           .
           .
           .

    </LookItems>

</eo:Menu>


Most probably I must do some scripting inside globalMenu.ascx.vb, but what exactly? The scripting of related Demo keeps creating new DataSet after adding table entires one by one during each run. Nevertheless, I tried to customise the Demo script but didn't work either. With GridView I didn't need any extra scripting! Could someone assist please? I'm a php developer but entirely new to this environment you know:-(

Many thanks in advance.


Saed Hamdan
"Man may be destroyed but not defeated" -Hemmingway
eo_support
Posted: Wednesday, October 15, 2008 2:11:52 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,217
Hi,

Well, first of all if you can handle php then you can handle everything. :)

In order for populate our menu from object data, you do not need to use an ObjectDataSource. The data flow with ObjectDataSource is:

DataSet.GetData -> ObjectDataSource -> Control

With our menu the flow should be:

DataSet -> Menu

You would implement this flow by:

Code: Visual Basic.NET
Menu1.DataSource = New MenuTableAdapters.structureTableAdapter

'some additional code which are explained later
.....

Menu1.DataBind()


Note this is not done yet. The most important difference between our Menu and Grid as far as data concern is that Grid is quite happy with two dimensional data, where our Menu requires hierarchical data. So you must some how tell the menu how your data is hierarchaly organized. ObjectDataSource does not provide this information. This is the main reason that Menu can not directly populate from an ObjectDataSource.

As you have already noticed, there are various ways to provide hierarchical information to the menu. Using which way largely depends on your scenario or even personal preference. You can take a look of all supported methods at here:

http://www.essentialobjects.com/ViewDoc.aspx?t=MenuCommon%2fDataBinding%2fDataBinding_overview.html

In your case, since you already have the automatically generated DataSet object, this one might be the closest:

http://www.essentialobjects.com/ViewDoc.aspx?t=MenuCommon%2fDataBinding%2fpopulate_dataset.html

When using DataSet object, the DataRelation objects within the DataSet object is what provides hierarchal information to the menu. So if you can just add that to your DataSet (with code), then menu should be happy. I would imagine the code to be something like this:

Code: Visual Basic.NET
'Create a new DataSet
Dim ds as DataSet as New MenuTableAdapters.structureTableAdapter

'Add data relations. You will need to get the parentColumn
'and childColumn from the DataSet first. You can do this
'by getting the Table object first, then get the DataColumn
'object from the Table
Dim r as DataRelations = ds.Relations.Add(parentColumn, childColumn)

'Populate the menu
Menu1.DataSource = ds
Menu1.DataBind()


If you only use one table, using an IDataReader might be easier for you because all you need to do is to open a OleDbConnection, creates an OleCommand object and execute a select on that object. That will give you an IDataReader object which you can hand to the menu directly. In this case the hierarchal information is set through the Menu's DataField property.

Hope this helps. Please feel free to let us know if you have any more questions.

Thanks
Saed
Posted: Wednesday, October 15, 2008 10:06:51 PM
Rank: Advanced Member
Groups: Member

Joined: 10/15/2008
Posts: 45
WOW! That was fast... No wonder EO is ranked high for its support; not to undervalue products of course.

Quote:
Well, first of all if you can handle php then you can handle everything.

Apreciate the encouragement. Let's hope so, although I still find it a bit complicated working with .NET framework.

Interesting what you mentioned comparing Grid with Menu. I can see the picture now. I reckoned I need to use generated Adapter but couldn't figure how.

As I said, I consulted all relevant Documentation, including the ones you included, but I was seeking after code rather than steps. However, using DataBinding as mentioned in Documentation didn't work with me unfortunately.

Looking at the code you kindly provided I can see you are using as twice ('Dim ... as ... as New ...') to create a new DataSet which I thought not applicable.

I'll give it a try and see... As for IDataReader, I'll check it as well since I didn't think about before.

Once again, many thanks for the excellent support.

Regards,

Saed Hamdan
"Man may be destroyed but not defeated" -Hemmingway
eo_support
Posted: Thursday, October 16, 2008 2:01:22 AM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,217
Hi,

You are right. Two "as" are definitely wrong. Below are detailed step by step instructions (with code) that you may find useful.

The following steps assume you have a table named TestTable in your database with the following data:

ID ParentID Desc
1, NULL, Root Node
2, 1, Child Node 1
3, 1, Child Node 2

Note the relationship between "ID" and "ParentID". This is where the hierarchical relation is established. Also note that ParentID for the root node must be NULL (It can not be a non-existing ID such as 0). Also keep in mind that this is just one way to specify such information. If your database structure is different, you will need to see what other approaches might fit you better ---- that's when you will want to understand all those different approaches.

Here are the steps:
1. Using Visual Studio to create a DataSet. Choose "Use SQL Statement", then use "SELECT ID, ParentID, Desc FROM TestTable". This would create two classes for you: DataSet1 and DataSet1TableAdapters.TestTableTableAdapter (similar to your MenuTableAdapters.structureTableAdapter);
2. At the design view, drag a Relation to the DataSet. Choose "TestTable" as both the parent and child table, key column as "ID", foreign column as "ParentID";
3. Check "Nested Relation" at the bottom of the relation dialog;
4. Create a blank page and drag a menu into the page;
5. Bring up Menu Builder to initialize the menu from the "MSDN" template;
6. Switch to "Menu" tab and editing the Menu's Bindings property;
7. Add a new Binding with its DataField set to "Desc" and Property set to "Text";
8. Copy the following code to your Page_Load:

Code: Visual Basic.NET
Dim ds As DataSet1 = New DataSet1()
Dim adapter As DataSet1TableAdapters.TestTableTableAdapter = _
    New DataSet1TableAdapters.TestTableTableAdapter()
adapter.Fill(ds.TestTable)
Menu1.DataSource = ds
Menu1.DataBind()


Run the page and you should see the menu with a single top level node ("Root") and two child nodes ("Child 1" and Child 2").

Thanks
Saed
Posted: Friday, October 17, 2008 3:07:29 AM
Rank: Advanced Member
Groups: Member

Joined: 10/15/2008
Posts: 45
Hi again,
Sorry to say but still can't get through:-(

You see, I understand the logic my friend; it is the implementation that holds my horses.

Quote:
1. Using Visual Studio to create a DataSet ...

I believe you are referring to Dataset Designer to do that as well as the next 2 steps. The problem is I'm working on a Web Application not a Windows Application, thus, only capable to create untyped DataSets via (Add New Item...). No Data menu to utilise as well. MSDN documentation always refers me to handling typed DataSets via Data menu under Windows Applications. Dragging created DataSets from App_Code folder into my aspx page will only result in creating a hyperlink to said DataSet file; no code is generated.

During my search I read that handling untyped DataSets is only applicable programmatically. Don't know how true that is.

Therefore, and to save us both time and hassle, appreciate if you could demonstrate entire code to incorporate, including the imports, i.e. starting from "Imports System" down to "Menu1.DataBind()".

Many thanks in advance,

Saed Hamdan
"Man may be destroyed but not defeated" -Hemmingway
eo_support
Posted: Friday, October 17, 2008 5:36:00 AM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,217
It doesn't have anything to do with Windows Application or Web application. You would simply go to "File -> New File -> DataSet" to create a dataset and then follow the onscreen instructions. On the first screen you will need to specify where your access data file is (Choose a Connection), on a few screen next you will need to choose "Use SQL statement" (Choose a Command Type), you would then enter your select statement.

If you continue to have a problem, we can set up an online meeting to show you how to use it. Just let us know what time is good for you. We are in GMT-5.

Thanks

Saed
Posted: Friday, October 17, 2008 10:02:34 AM
Rank: Advanced Member
Groups: Member

Joined: 10/15/2008
Posts: 45
Quote:
You would simply go to "File -> New File -> DataSet" to create a dataset and then follow the onscreen instructions.

Apparently you missed my point my friend. Creating a new DataSet as you mentioned or via adding a New Item will always result in generating an untyped DataSet that only has an XML code (.xsd), which VS places into App_Code folder. I have no problem in defining a connection or the SQL statement (or even adding a Relation) during creation. In fact, my DataSets created this way are all fine.

The problem is how to reference such DataSets in my aspx files, in particular with EO Menu? As I said before, I created an ObjectDataSource and then an AccessDataSource, then referenced each via DataSourceID with native GridView of VS and it worked fine. However, attempting to implement the same with EO Menu didn't work for the reason you kindly justified before.

To summarise:
Let's say I have a previously created .xsd DataSet named myDS.xsd with an ID as myDS1 that retrieves its data from an Access data tabe named myTable. Other information (Connection, Columns, Relation, etc) are declared inside DataSet XML file.

What code do I need to include into my aspx.vb in order to use the above DataSet to populate an EO Menu?

I hope the problem is clear now.

Thanks again. BTW, we are GMT +4.

Saed Hamdan
"Man may be destroyed but not defeated" -Hemmingway
eo_support
Posted: Friday, October 17, 2008 11:12:53 AM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,217
Hi,

We do understand your situation. If you would just follow the steps we provided, you will see it just works.

What you have missed is, when you use File -> New File -> DataSet to create a DataSet, Visual Studio not only creates the DataSet XML file for you, it also automatically generates all the .vb source code that access the DataSet. This includes a number of classes, the most importants are DataSet1 and DataSet1TableAdapters.TestTableTableAdapter.

The reason that you may not be aware of this is by default, Visual Studio doesn't show you these two generated source files. However, if you just switch to class view, you will see these two classes. Double click them and you will see the automatically generated source code. (Now you see why Visual Studio wants you to put the .xsd file in App_Code directory?)

Since now you have these two wrapper classes, you would just use them directly in your code (we have already provided all the necessary code in our previous post).

Hope this clears up.

Thanks
Saed
Posted: Friday, October 17, 2008 3:30:01 PM
Rank: Advanced Member
Groups: Member

Joined: 10/15/2008
Posts: 45
Quote:
if you just switch to class view ...

This was the trick which revealed entire picture! Guess I still have too much to learn using VS Brick wall

At last the menu is functional. Just to close the code chapter, here is the code I used, in case someone else might be interested, based on the summarised situation above:

Code: Visual Basic.NET
' Create a new DataSet based on myDS untyped DataSet
        Dim thisDataSet As myDS = New myDS()

        ' Create the DataAdapter
        Dim thisAdapter As myDSTableAdapters.myTableTableAdapter = _
        New myDSTableAdapters.myTableTableAdapter()

        ' Fill the DataSet
        thisAdapter.Fill(thisDataSet.myTable)

        ' Populate the menu.
        globalMenu.DataSource = thisDataSet
        globalMenu.DataBind()

Interestingly, no need to define or reference the Relation since its is already declared inside myDS. Can't say how grateful I am for your patience and consideration folks.

Now after the menu is up and running, two things I have noticed:

1) Fixing Menu Width via CSS vs Declaration:
According to my table, I have 7 values that function as Parents (i.e. Level1 menu entries). I had menu width fixed in relevant CSS file which was fine before. Now with dynamic population, this doesn't work any more as the menu continues populating L1 entries beyond 7 with empty entries. Setting menu width in the declaration however solved the issue. Any justification to that?

2) No More Separators:
Obviously, with dynamic population the Separator is no more. Is there a way you suggest to identify where to insert the Separator (e.g. entering its ID among L2 values in the table)?

Does anyone told you how wonderful are you? I'll be back though:-)

Regards,

Saed Hamdan
"Man may be destroyed but not defeated" -Hemmingway
eo_support
Posted: Friday, October 17, 2008 4:49:26 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,217
Saed wrote:
1) Fixing Menu Width via CSS vs Declaration:
According to my table, I have 7 values that function as Parents (i.e. Level1 menu entries). I had menu width fixed in relevant CSS file which was fine before. Now with dynamic population, this doesn't work any more as the menu continues populating L1 entries beyond 7 with empty entries. Setting menu width in the declaration however solved the issue. Any justification to that?


If you want your menu to have 7 top level items and your menu turns up 8, then something is wrong either with your data or your data relation. In the sample data mentioned in our previous post, we have 1 top level item in the table and the menu show up with exactly 1 top level item. So that will be what you fix. Not CSS. CSS width will not fix that problem for you.

In this regard, I would suggest you to create a test table in your database and follow our previous post exactly step by step to see how it works. Once you see that working, it should be easy for you to find out what's wrong in your data/code.


Saed wrote:

2) No More Separators:
Obviously, with dynamic population the Separator is no more. Is there a way you suggest to identify where to insert the Separator (e.g. entering its ID among L2 values in the table)?


First you will need to have dummy records in your database (this is necessary because the menu strictly follows one menu item per data record. Separators are also menu items). You will then handle the menu's ItemDataBound event. At runtime, the menu goes through your datasource and:

1. Create a menu item for each record;
2. Calls your ItemDataBound event handler;

Inside your ItemDataBound event handler you will need to check the current data record, and if you see it is your dummy records, you then set the newly created menu item's IsSeparator to true. It will be something like this:

Code: Visual Basic.NET
Protected Sub Menu1_ItemDataBound( _
    ByVal sender As Object, ByVal e As EO.Web.NavigationItemEventArgs) _
    Handles Menu1.ItemDataBound

    'Get the current data row
    Dim record As DataRow = CType(Menu1.DataItem, DataRow)

    'Check whether you need to turn this new menu item into a separator
    'based on the record content
    If CType(record.Item("some_field"), Integer) = some_value Then
        e.MenuItem.IsSeparator = True
    End If

End Sub


So basically ItemDataBound gives you a chance to "fix up" whatever automatically created.


Saed wrote:

Does anyone told you how wonderful are you? I'll be back though:-)


We know that. :) As return, we ask you to purchase our product and also tells all your friends about the great quality of our product and our support. :)

Thanks!


Saed
Posted: Saturday, October 18, 2008 1:02:49 PM
Rank: Advanced Member
Groups: Member

Joined: 10/15/2008
Posts: 45
Hello there,
I doubt it is menu data or data relation as it also occurred with your example. Anyway, I'll investigate later on when have time and let you know.

For the Separator, I thought I might need something similar to what you suggested. Tried your code but didn't work as it threw an exception: can't convert DBNull to Integer or something like that. So, I modified checking condition as below and it worked.

Code: Visual Basic.NET
'Check whether you need to turn this new menu item
        'into a separator based on the record content
        Dim divider As String = "====="
        If CType(record.Item("MenuEntry"), String) = divider Then
            e.MenuItem.IsSeparator = True
        End If

I think the reason could be related to the way I constructed my table and handled dummy records. I added a column to reference L2 entries aiming to define their sequence which I then used to sort L2 entries by. This way, when I add a dummy Separator to the end of my table, I just assign it a reference number to indicate its sequence in its entries group. When populating the menu, entries sorted and the Separator appears in the right place. You may check the table view to see what I mean.

As for the purchase, we already had. Not only that, we passed our view to some of our key customers who also purchased it (should we call for our commission?). Quality is always valued my friend.

Regards,

Saed Hamdan
"Man may be destroyed but not defeated" -Hemmingway
eo_support
Posted: Saturday, October 18, 2008 3:51:12 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,217
Saed wrote:
I doubt it is menu data or data relation as it also occurred with your example. Anyway, I'll investigate later on when have time and let you know.

When you have time to investigate, try to follow our previous post step by step. We have verified that it produces the correct result.


Saed wrote:
For the Separator, I thought I might need something similar to what you suggested....

Excellent work! Please keep in mind that code/steps we posted is generally only intended to help you to understand how our product works. It should never be interpreted as "ready to use" so that you can copy and paste because everybody situation can be different. So while our sample code works with our sample data, it is quite possible that it does not work with your data. So we expect our user to be able to adapt the sample code to fit their own situation --- and you did it! So great work here!


Saed wrote:
As for the purchase, we already had. Not only that, we passed our view to some of our key customers who also purchased it (should we call for our commission?). Quality is always valued my friend.

Much appreciate that! If you have any more customers that is interested in our product, let us know and might be able to work out some incentives. :)

Thanks!



You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.