Looping through controls on a window.

DoJa

Active member
Joined
Mar 23, 2014
Messages
33
Programming Experience
1-3
I have a number of list box controls which I would like to clear using a loop rather than typing individually.

I was able to do this quite easily in winforms but cannot fathom how to achieve the same under wpf.

The closest I have got is this:

C#:
foreach (Control c in this.gdLayout.Children)
            {


                if (c.GetType() == typeof(ListBox)) //where control is of type list box
                {

                    //Clear items
                    c.Items.Clear();
                }

   
            }

As above it won't compile because "Error 1 'System.Windows.Controls.Control' does not contain a definition for 'Items' and no extension method 'Items' accepting a first argument of type 'System.Windows.Controls.Control' could be found."

if I change the foreach from control to listbox then it will compile, but then at runtime it fails with the unhandled exception "Additional information: Unable to cast object of type 'System.Windows.Controls.DataGrid' to type 'System.Windows.Controls.ListBox'."

Could anyone help me out here?
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
3,130
Location
Sydney, Australia
Programming Experience
10+
The issue is that you have declared `c` as type Control and then you try to access the Items property of `c`. If you want to get the Items property of a ListBox then you have to do it on a reference of type ListBox. Keeping as closely as possible to your existing code, that would look like this:
foreach (Control c in this.gdLayout.Children)
{
    if (c.GetType() == typeof(ListBox))
    {
        ((ListBox) c).Items.Clear();
    }
}
That's called a cast, which I hope that you've at least heard of, even if you weren't sure what it meant. In this case, it's an explicit cast, which means putting the type in parentheses in front of an expression to expose the result of that expression as a different type, even though it's the same object.

An explicit cast is really not the best way to do it in this case though. Before LINQ, I would have done this using a conditional cast:
foreach (Control c in this.gdLayout.Children)
{
    ListBox lb = c as ListBox;

    if (lb != null)
    {
        lb.Items.Clear();
    }
}
The `as` operator performs a cast to the specified type and either returns the original object, if it is that type, or `null` otherwise. That's why it should always be followed by an `if` statement testing for `null`.

With the advent of LINQ though, you can filter in the `foreach` statement itself and know that every object within the loop is the correct type:
foreach (Control c in this.gdLayout.Children.OfType<ListBox>())
{
    lb.Items.Clear();
}
The OfType method performs the cast internally and exposes a new list that contains only the items of that type. Note that the OfType method can only be called on an object that implements the IEnumerable<T> interface. On occasions where your list implements only IEnumerable but not its generic counterpart, you can produce an IEnumerable<T> with the Cast<T> method:
foreach (Control c in this.gdLayout.Children.Cast<Control>().OfType<ListBox>())
{
    lb.Items.Clear();
}
 

DoJa

Active member
Joined
Mar 23, 2014
Messages
33
Programming Experience
1-3
jmcilhinney, you are a legend. If you lived nearer I'd buy you a beer!

Thanks for the in depth explanation.
 
Top Bottom