This is a follow-up post to Iterating a Complex DataList Hierarchy With Lambda Expressions, which showed one way to avoid code duplication when iterating a complex control hierarchy. This shows how to achieve roughly the same thing by taking advantage of the yield keyword.
Again, here’s the DataList we need to traverse (see the original post for more details on what the demo does).
<asp:DataList ID="dlFamilies" runat="server" DataKeyField="FamilyID">
<ItemTemplate>
<%# Eval("FamilyName") %>
<asp:DataList ID="dlPeople" runat="server" DataSource='<%# Eval("FamilyMembers") %>'
DataKeyField="PersonID">
<ItemTemplate>
<asp:CheckBox
CssClass="person-checkbox"
ID="cbPerson"
runat="server"
Text='<%# Eval("Name") + " (" + Eval("Gender") + ")" %>'
AutoPostBack="true"
OnCheckedChanged="cbPerson_CheckedChanged"
/>
</ItemTemplate>
</asp:DataList>
</ItemTemplate>
</asp:DataList>
This boiler plate code might be copied multiple times to perform whatever action you need on the data.
// Loop through DataList that shows families.
foreach (DataListItem familyItem in dlFamilies.Items)
{
// Find family object based on FamilyID
int currentFamilyID = (int)dlFamilies.DataKeys[familyItem.ItemIndex];
Family family = _families.Where(f => f.FamilyID == currentFamilyID).FirstOrDefault();
// Grab the inner DataList that shows people in a family.
DataList dlPeople = (DataList)familyItem.FindControl("dlPeople");
foreach (DataListItem personItem in dlPeople.Items)
{
// Find person object based on PersonID
int currentPersonID = (int)dlPeople.DataKeys[personItem.ItemIndex];
Person person = family.FamilyMembers.Where(p => p.PersonID == currentPersonID).FirstOrDefault();
// DO SOMETHING
}
}
Instead of duplicating this code, we create a custom iterator that returns a PersonRow object for every person in the DataList. It includes a strongly typed reference to the Person, Family, and a boolean telling you whether or not the checkbox is checked.
private IEnumerable<PersonRow> GetPeople()
{
// Loop through DataList that shows families.
foreach (DataListItem familyItem in dlFamilies.Items)
{
// Find family object based on FamilyID
int currentFamilyID = (int)dlFamilies.DataKeys[familyItem.ItemIndex];
Family family = _families.Where(f => f.FamilyID == currentFamilyID).FirstOrDefault();
// Grab the inner DataList that shows people in a family.
DataList dlPeople = (DataList)familyItem.FindControl("dlPeople");
foreach (DataListItem personItem in dlPeople.Items)
{
// Find person object based on PersonID
int currentPersonID = (int)dlPeople.DataKeys[personItem.ItemIndex];
Person person = family.FamilyMembers.Where(p => p.PersonID == currentPersonID).FirstOrDefault();
CheckBox cb = (CheckBox)personItem.FindControl("cbPerson");
PersonRow row = new PersonRow { Family = family, Person = person, Selected = cb.Checked };
yield return row;
}
}
}
One advantage of using yield instead of a lambda expression is that the code is more readable. You just loop through a collection. A disadvantage is that you need to create a custom class to hold the result of your iterator.
public class PersonRow
{
public Person Person { get; set; }
public Family Family { get; set; }
public bool Selected { get; set; }
}
Here’s code that uses the new iterator:
// Called when a checkbox is checked.
protected void cbPerson_CheckedChanged(object sender, EventArgs e)
{
int maleCount = GetPeople()
.Where(r => r.Person.Gender == Gender.Male && r.Selected)
.Count();
int femaleCount = GetPeople()
.Where(r => r.Person.Gender == Gender.Female && r.Selected)
.Count();
btnGetPeople.Enabled =
maleCount == REQUIRED_MALE_COUNT &&
femaleCount == REQUIRED_FEMALE_COUNT;
lblState.Text = string.Format("{0} male(s) selected and {1} females selected",
maleCount,
femaleCount);
}
// Caled when the submit button is pressed
protected void btnGetPeople_Click(object sender, EventArgs e)
{
lblSelectedPeople.Text = string.Empty;
foreach (PersonRow row in GetPeople().Where(p => p.Selected).ToList())
{
lblSelectedPeople.Text += row.Person.ToString() + "<br />";
}
}
I’ve included an updated sample available for download.
0 comments:
Post a Comment