I want to display a tree structure like this:
-
Mission1
- Activity1
- Project1
- Activity2
- Activity1
-
Mission2
- Activity3
- Project2
- Activity3
But instead I’m getting a list that repeats the parent elements for every activity or project that is different like this:
-
Mission1
- Activity1
- Project1
- Activity1
-
Mission1
- Activity2
I want to group the Activities and Projects under one parent (‘Mission’) element.
Here’s my LINQ query:
var q = from mission in _context.tMissions
join activity in _context.tActivities on mission.id equals activity.missionId
join project in _context.tDefaultEventTypes on activity.id equals project.activityId
where !project.isRemoved && project.defaultCategoryId == 4
orderby mission.name
select CreateFrom(project);
return q.ToList();
I then just bind that to a list and display it on the page. Here’s my aspx:
<asp:DataList ID="dlTierTypes" runat="server">
<ItemTemplate>
<li><%# Eval("Activity.Mission.Name") %></li>
<ul>
<li><%# Eval("Activity.Name") %></li>
<ul>
<li><%# Eval("Name") %></li>
</ul>
</ItemTemplate>
</asp:DataList>
And the code behind just binds it:
public void SetTierTree(IEnumerable<DefaultEventType> tierList)
{
dlTierTypes.DataSource = tierList;
dlTierTypes.DataBind();
}
Not sure how to tackle this exactly.
EDIT – Here’s my new function in the repository.
public ICollection<DefaultEventType> GetTierTree()
{
var q = from mission in _context.tMissions
join activity in _context.tActivities on mission.id equals activity.missionId
join project in _context.tDefaultEventTypes on activity.id equals project.activityId
where !project.isRemoved && project.defaultCategoryId == 4
orderby mission.name
select new DefaultEventType(project.tierLevel.TryParseEnum<GanttType>(GanttType.Unknown), DefaultCategoryRepository.CreateFrom(project.tDefaultCategory))
{
//t = new DefaultEventType(project.tierLevel.TryParseEnum<GanttType>(GanttType.Unknown), DefaultCategoryRepository.CreateFrom(project.tDefaultCategory)),
AllowNumericSuffix = project.allowNumericSuffix,
AttachMilestoneMoniker = project.attachMilestoneMoniker,
Description = project.description,
Id = project.id,
IsReadOnly = project.isReadOnly,
IsSticky = project.isSticky,
Name = project.name,
Sid = project.sid,
Style = project.style.TryParseEnum<GanttElementStyle>(GanttElementStyle.Unknown),
TimeStamp = project.createdDT,
UpdatedTimeStamp = project.updatedDT,
Activity = new Activity { Id = activity.id, Name = activity.name, Mission = new Mission { Id = mission.id, Name = mission.name } }
};
var q2 = q.GroupBy(row => row.Activity.Mission.Id)
.Select(group => group.GroupBy(row => row.Activity.Id));
return q2.Cast<DefaultEventType>().ToList();
}
But I receive this error when displaying the page:
‘Could not convert from type ‘System.Collections.Generic.List1[System.Linq.IGrouping2[System.Int32,DefaultEventType]]’ to type ‘DefaultEventType’.’
First you need to adjust the query. The use of
GroupBycan greatly help in this example.Just take exactly what you have, then group the results by mission. The, for each group, group the missions by activity (this needs to be nested group bys, not sequential group bys).
I’m not sure if your provider can do that mapping on the DB end or not; if not throw an
AsEnumerablein that query where needed.Next, you’ll need to change how you render the data to reflect this change.
Rather than a single
DataListyou’ll need 3 nested data lists. You’ll bind the whole query to the outer level which displays the mission, then subscribe to the data bound event to set the inner-group as the data source of that instance of the inner data list. Then, in that inner data list, display the activity and bind the innermost group level to the innermostDataList, which can bind the project name normally.Also, you may consider using a
TreeViewto render the data structure instead of aDataList, since aTreeViewis designed to render trees. I’m not sure if you needed to reject it for some reason or just didn’t consider it, but it would probably be easier to work with. If you do go that route the query will still be modified as I suggested, you just won’t need any nested ASP controls.