views:

333

answers:

4

Is there an elegant way of doing this? Perhaps with Linq?

For something like this:

List<ControlCollection> list = { ... }

List<Control> merged = list.MergeAll();

EDIT: The final collection will be single dimensional in a sense that all controls will be there, not in a nested way.

A: 

Linq has Concat and Union methods. Is one of those what you're looking for?

Skinniest Man
Thanks close, but they would still require me to do a loop on them for each element. I was looking to do it with a single call or something.
Joan Venge
Why would you need a loop? You'd just use `ToList()` on the result to get a `List<Control>`.
Pavel Minaev
+1  A: 

How about this:

public static void Append(this System.Windows.Forms.Control.ControlCollection collection, System.Windows.Forms.Control.ControlCollection newCollection)
{
    Control[] newControl = new Control[newCollection.Count];
    newCollection.CopyTo(newControl, 0);
    collection.AddRange(newControl);
}

Usage:

    Form form1 = new Form();
    Form form2 = new Form();
    form1.Controls.Append(form2.Controls);


This will flatten the control tree:

public static void FlattenAndAppend(this System.Windows.Forms.Control.ControlCollection collection, System.Windows.Forms.Control.ControlCollection newCollection)
{
    List<Control> controlList = new List<Control>();
    FlattenControlTree(collection, controlList);

    newCollection.AddRange(controlList.ToArray());
}

public static void FlattenControlTree(System.Windows.Forms.Control.ControlCollection collection, List<Control> controlList)
{
    foreach (Control control in collection)
    {
        controlList.Add(control);
        FlattenControlTree(control.Controls, controlList);
    }
}
Philip Wallace
Thanks, I actually forgot about something. Does it handle controls within controls so getting rid of all nested levels?
Joan Venge
Yes. When a control moves, so do all it's children.
Philip Wallace
No I mean, does it add them so the list is not multi dimensional.
Joan Venge
No, it doesn't. Should it?
Philip Wallace
why do you use Array.CreateInstance ? Isn't it easier to write `new Control[newCollection.Count]` ?
Thomas Levesque
Yes, I forgot to add it to the question, will edit now.
Joan Venge
Thanks my code also does a similar thing, so I guess this is the only way.
Joan Venge
A: 

Tree extension method

static class TreeExtensions
{
  public static IEnumerable<R>TraverseDepthFirst<T, R>(
      this T t,
      Func<T, R> valueselect,
      Func<T, IEnumerable<T>> childselect)
  {
    yield return valueselect(t);

    foreach (var i in childselect(t))
    {
      foreach (var item in i.TraverseDepthFirst(valueselect, childselect))
      {
        yield return item;
      }
    }
  }
}

Usage:

Control c = root_form_or_control;

var allcontrols = c.TraverseDepthFirst(
     x => x, 
     x => x.Controls.OfType<Control>())).ToList();
leppie
A: 
var merged = (from cc in list
              from Control control in cc
              select cc)
             .ToList();

or (same thing with explicit LINQ method calls):

var merged = list.SelectMany(cc => cc.Cast<Control>()).ToList();

[EDIT] Reflecting the newly added nesting requirement:

static IEnumerable<T> FlattenTree<T>(
    this IEnumerable<T> nodes,
    Func<T, IEnumerable<T>> childrenSelector)
{
    foreach (T node in nodes)
    {
        yield return node;
        foreach (T child in childrenSelector(node))
        {
            yield return child;
        }
    }
}

var merged = list.SelectMany(cc => cc.Cast<Control>())
                 .FlattenTree(control => control.Controls.Cast<Control>())
                 .ToList();
Pavel Minaev