2

I'm fairly new to C# and got a bit of a problem that I'm sure has a good solution using LINQ.

Background: There's a project I've inherited that uses CrystalReports, the reports themselves all have viewer-forms [containing components/controls] associated with them in a uniform way (I'm fairly sure they were machine generated), one of these is a component descended from ReportClass which contains a database property. The database property is the only thing that differentiates a method (Log_On_Database) that appears in all of these classes. What I would like to do is create a common base class that searches the form for ReportClass and uses that to populate its local database variable w/ the value from the property so I can implement Log_On_Database in a single location.

Question: How does one use LINQ to get all of the components (not [just] controls) belonging to a form and recursively for those that are controls (and thus can have their own)?

NOTE: Getting the result in a List would be great, as I could then test for a length of 0 (something went horribly wrong), 1 (expected), or more (and then I could do what I need to on those odd cases) -- even if this is all generated code, I don't trust it to not have been modified in horrendously painful ways.

Shark8
  • 4,095
  • 1
  • 17
  • 31

2 Answers2

1

So far I've got this:

    // Get all controls of a certain type:
    // http://stackoverflow.com/questions/3419159/how-to-get-all-child-controls-of-a-winform-of-a-specific-type-button-textbox
    public IEnumerable<Control> GetAll(Control control, Type type)
    {
        var controls = control.Controls.Cast<Control>();

        return controls.SelectMany(ctrl => GetAll(ctrl, type))
                                  .Concat(controls)
                                  .Where(c => c.GetType() == type);
    }

    protected ComponentCollection get_components(Component c)
    {
        Type parent = c.GetType();
        FieldInfo fieldInfo = parent.GetField("components", BindingFlags.Instance | BindingFlags.NonPublic);
        IContainer fieldData = (IContainer)fieldInfo.GetValue(components);

        return fieldData.Components;
    }

    protected void Log_On_Database()
    {
        // ReportClass decends from ReportDocument, which has the Database property we're interested in
        // this method grabs up any ReportDocument and decended objects. There should be only one.
        List<ReportDocument> reports = new List<ReportDocument>();

        // The list 'ctrls' contains all the Controls of 'this' form.
        List<Control> ctrls = GetAll(this, typeof(Control)).ToList();

        // Now we add all the components from all the controls which are ReportDocuments to the "reports" list.
        foreach (Control c in ctrls)
            foreach( Component x in get_components(c) )
            {
                if (x is ReportDocument)
                    reports.Add((ReportDocument)x);
            }

        switch (reports.Count)
        {
            case 0:
                MessageBox.Show("No report document found.");
                break;
            case 1:
                Log_On_Database( ((ReportDocument)reports[0]).Database );
                break;
            default:
                MessageBox.Show("Too many report documents found.");
                break;
        } // end switch

    } // end Log_On_Database

It would be nice to get it all in one LINQ statement.

Shark8
  • 4,095
  • 1
  • 17
  • 31
0

With linq you can't do recursive queries, so the GetAll should remain as it is.

But then you can do:

var reports = GetAll(this, typeof(Control))
            .SelectMany(c => get_components(c))
            .OfType<ReportDocument>()
            .ToList();
Gert Arnold
  • 105,341
  • 31
  • 202
  • 291