I have a method where, given a VBComponent, I can access the .Designer and from there, the .Controls collection:
private void DeclareControlsAsMembers(VBComponent form)
{
var designer = form.Designer;
if (designer == null)
{
return;
}
// using dynamic typing here, because not only MSForms could have a Controls collection (e.g. MS-Access forms are 'document' modules).
foreach (var control in ((dynamic)designer).Controls)
{
var declaration = new Declaration(_qualifiedName.QualifyMemberName(control.Name), ...);
OnNewDeclaration(declaration);
}
}
The problem with this method is that, when the host is MS-Access, form.Designer is null, so the method returns early.
The dynamic cast here isn't particularly useful, it seems I could be casting to a UserForm interface and it would "just work" - at least in an Excel host.
But since MS-Access' forms don't have a designer (???), how do I go about iterating controls on a MS-Access form, given C# code that's a VBE add-in (i.e. which can only easily access whatever the VBIDE API makes available)?
You can't iterate the controls on an Access form unless the form is open. Opening forms, even to design mode, is expensive, as controls need to be rendered, and being Access, the bound properties are resolved to database objects. There's also the issue of sub-forms and sub-reports.
But, this VBA code will take your vbComponent, open the form (if it isn't already open, in design mode), and then return the controls collection from the vbComponent's Properties collection. If the form wasn't open at the outset, then it is closed.
This code is fairly easy to replicate for Access Reports.