void ControlTreeDataLoader.LoadData()
        {
            if (hideIfEmpty && itemGroups.All(itemGroup => !itemGroup.Items.Any()))
            {
                Visible = false;
                return;
            }

            EwfTable.SetUpTableAndCaption(this, style, classes, caption, subCaption);

            var itemSetupLists = new[] { headItems }.Concat(itemGroups.Select(i => i.Items)).Select(i => i.Select(j => j.Setup.FieldOrItemSetup));
            var allItemSetups     = itemSetupLists.SelectMany(i => i).ToList();
            var columnWidthFactor = EwfTable.GetColumnWidthFactor(allItemSetups);

            foreach (var itemSetups in itemSetupLists.Where(i => i.Any()))
            {
                Controls.Add(new WebControl(HtmlTextWriterTag.Colgroup).AddControlsReturnThis(itemSetups.Select(i => EwfTable.GetColControl(i, columnWidthFactor))));
            }

            var fields = EwfTable.GetFields(specifiedFields, headItems, itemGroups.SelectMany(i => i.Items));
            var cellPlaceholderListsForItems = TableOps.BuildCellPlaceholderListsForItems(headItems.Concat(itemGroups.SelectMany(i => i.Items)).ToList(),
                                                                                          fields.Length);

            // Pivot the cell placeholders from column primary into row primary format.
            var cellPlaceholderListsForRows =
                Enumerable.Range(0, fields.Length).Select(
                    field => Enumerable.Range(0, allItemSetups.Count).Select(item => cellPlaceholderListsForItems[item][field]).ToList()).ToList();

            var headRows = TableOps.BuildRows(cellPlaceholderListsForRows.Take(firstDataFieldIndex).ToList(),
                                              fields.Select(i => i.FieldOrItemSetup).ToList().AsReadOnly(),
                                              null,
                                              allItemSetups.AsReadOnly(),
                                              allItemSetups.Count,
                                              true);
            var bodyRows = TableOps.BuildRows(cellPlaceholderListsForRows.Skip(firstDataFieldIndex).ToList(),
                                              fields.Select(i => i.FieldOrItemSetup).ToList().AsReadOnly(),
                                              false,
                                              allItemSetups.AsReadOnly(),
                                              headItems.Count,
                                              true);

            // We can't easily put the head fields in thead because we don't have a way of verifying that cells don't cross between head and data fields.
            Controls.Add(new WebControl(HtmlTextWriterTag.Tbody).AddControlsReturnThis(headRows.Concat(bodyRows)));

            EwfTable.AssertAtLeastOneCellPerField(fields, cellPlaceholderListsForItems);
        }
        /// <summary>
        /// Call this during LoadData.
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="vl"></param>
        /// <param name="availableRoles">Pass a restricted list of <see cref="Role"/>s the user may select. Otherwise, Roles available
        /// in the System Provider are used.</param>
        /// <param name="validationPredicate">If the function returns true, validation continues.</param>
        public void LoadData(int?userId, ValidationList vl, List <Role> availableRoles = null, Func <bool> validationPredicate = null)
        {
            availableRoles = (availableRoles != null ? availableRoles.OrderBy(r => r.Name) : UserManagementStatics.SystemProvider.GetRoles()).ToList();

            user = userId.HasValue ? UserManagementStatics.GetUser(userId.Value, true) : null;
            if (includePasswordControls() && user != null)
            {
                facUser = FormsAuthStatics.GetUser(user.UserId, true);
            }

            Func <bool> validationShouldRun = () => validationPredicate == null || validationPredicate();

            var b = FormItemBlock.CreateFormItemTable(heading: "Security Information");

            b.AddFormItems(
                FormItem.Create(
                    "Email address",
                    new EwfTextBox(user != null ? user.Email : ""),
                    validationGetter: control => new Validation(
                        (pbv, validator) => {
                if (validationShouldRun())
                {
                    Email = validator.GetEmailAddress(new ValidationErrorHandler("email address"), control.GetPostBackValue(pbv), false);
                }
            },
                        vl)));

            if (includePasswordControls())
            {
                var group = new RadioButtonGroup(false);

                var keepPassword = FormItem.Create(
                    "",
                    group.CreateInlineRadioButton(true, label: userId.HasValue ? "Keep the current password" : "Do not create a password"),
                    validationGetter: control => new Validation(
                        (pbv, validator) => {
                    if (!validationShouldRun() || !control.IsCheckedInPostBack(pbv))
                    {
                        return;
                    }
                    if (user != null)
                    {
                        Salt               = facUser.Salt;
                        SaltedPassword     = facUser.SaltedPassword;
                        MustChangePassword = facUser.MustChangePassword;
                    }
                    else
                    {
                        genPassword(false);
                    }
                },
                        vl));

                var generatePassword = FormItem.Create(
                    "",
                    group.CreateInlineRadioButton(false, label: "Generate a " + (userId.HasValue ? "new, " : "") + "random password and email it to the user"),
                    validationGetter: control => new Validation(
                        (pbv, validator) => {
                    if (validationShouldRun() && control.IsCheckedInPostBack(pbv))
                    {
                        genPassword(true);
                    }
                },
                        vl));

                var newPassword      = new DataValue <string>();
                var confirmPassword  = new DataValue <string>();
                var newPasswordTable = EwfTable.Create(style: EwfTableStyle.StandardExceptLayout);
                newPasswordTable.AddItem(
                    new EwfTableItem(
                        "Password",
                        FormItem.Create(
                            "",
                            new EwfTextBox("", masksCharacters: true, disableBrowserAutoComplete: true)
                {
                    Width = Unit.Pixel(200)
                },
                            validationGetter: control => new Validation((pbv, v) => newPassword.Value = control.GetPostBackValue(pbv), vl)).ToControl()));
                newPasswordTable.AddItem(
                    new EwfTableItem(
                        "Password again",
                        FormItem.Create(
                            "",
                            new EwfTextBox("", masksCharacters: true, disableBrowserAutoComplete: true)
                {
                    Width = Unit.Pixel(200)
                },
                            validationGetter: control => new Validation((pbv, v) => confirmPassword.Value = control.GetPostBackValue(pbv), vl)).ToControl()));

                var providePasswordRadio = group.CreateBlockRadioButton(false, label: "Provide a " + (userId.HasValue ? "new " : "") + "password");
                providePasswordRadio.NestedControls.Add(newPasswordTable);
                var providePassword = FormItem.Create(
                    "",
                    providePasswordRadio,
                    validationGetter: control => new Validation(
                        (pbv, validator) => {
                    if (!validationShouldRun() || !control.IsCheckedInPostBack(pbv))
                    {
                        return;
                    }
                    FormsAuthStatics.ValidatePassword(validator, newPassword, confirmPassword);
                    var p              = new Password(newPassword.Value);
                    Salt               = p.Salt;
                    SaltedPassword     = p.ComputeSaltedHash();
                    MustChangePassword = false;
                },
                        vl));

                b.AddFormItems(
                    FormItem.Create("Password", ControlStack.CreateWithControls(true, keepPassword.ToControl(), generatePassword.ToControl(), providePassword.ToControl())));
            }

            b.AddFormItems(
                FormItem.Create(
                    "Role",
                    SelectList.CreateDropDown(
                        from i in availableRoles select SelectListItem.Create(i.RoleId as int?, i.Name),
                        user != null ? user.Role.RoleId as int? : null),
                    validationGetter: control => new Validation(
                        (pbv, validator) => {
                if (validationShouldRun())
                {
                    RoleId = control.ValidateAndGetSelectedItemIdInPostBack(pbv, validator) ?? default(int);
                }
            },
                        vl)));

            Controls.Add(b);
        }