递归silverlight元素查找器扩展方法

时间:2010-01-26 14:38:18

标签: silverlight recursion methods yield

我需要一个扩展方法来遍历Silverlight页面上的所有文本框。假设我总是使用网格布局,那么我有:

public static IEnumerable<UIElement> Traverse(this UIElementCollection source, Func<Grid, UIElementCollection> fnRecurse)
    {
        foreach (UIElement item  in source)
        {
            yield return item;
            var g = source.OfType<Grid>();
            foreach (Grid itemsub in g)
                {
                    var t = fnRecurse(itemsub);
                      t.Traverse(fnRecurse);
                      yield return itemsub;

                };
        }

    }

现在,我可以这样称呼:

baseLayout.Children.Traverse(x =>  x.Children ).OfType<TextBox>().ForEach(
                w =>
                {
                    //Text Box Extension Method, clears the text
                    w.reset();
                });

这永远不会开火。我相信OfType无法区分UI元素。

我该怎么做?我想要展平视觉树然后循环。不只是文本框,而是只是获得我想要的任何东西。我在错误的地方或经常屈服吗?

编辑:

当前代码

public static IEnumerable<UIElement> Traverse(this UIElementCollection source, Func<Grid, UIElementCollection> fnRecurse)
    {

        foreach (UIElement item  in source)
        {
           source.OfType<Grid>().Select(x => x.Children).ForEach(v => Traverse(v, fnRecurse));
           yield return item;
        }

    }

并且网格是

<Grid x:Name="LayoutRoot">
    <Grid.RowDefinitions>
        <RowDefinition Height="42"/>
        <RowDefinition Height="42"/>
        <RowDefinition Height="45"/>
        <RowDefinition Height="43"/>
        <RowDefinition Height="47"/>
        <RowDefinition Height="46"/>
        <RowDefinition/>
        <RowDefinition Height="67"/>
    </Grid.RowDefinitions>
    <Button x:Name="saveAddressButton" Height="24" HorizontalAlignment="Left" Margin="8,0,0,8" VerticalAlignment="Bottom" Width="135" Content="Save and Add More" Click="saveClick" Style="{StaticResource SaveButton}" Foreground="White" Grid.Row="7"/>
    <Button x:Name="saveAddressButton_Copy" Height="24" HorizontalAlignment="Right" Margin="0,0,8,8" VerticalAlignment="Bottom" Width="81" Content="Clear" Click="clearClick" Style="{StaticResource SaveButton}" Foreground="White" Grid.Row="7"/>
    <Grid Height="30" VerticalAlignment="Top" Margin="8,9,8,0">
        <TextBlock Margin="8,0,0,0" Text="AddressLine1" FontSize="16" Foreground="White" HorizontalAlignment="Left" Width="121"/>
        <TextBox x:Name="inputAddressLine1" TextWrapping="Wrap" Margin="218,0,0,0"/>
    </Grid>
    <Grid Height="30" Margin="8,8,8,0" VerticalAlignment="Top" Grid.Row="2">
        <TextBlock Margin="8,0,0,0" Text="AddressLine2" FontSize="16" Foreground="White" HorizontalAlignment="Left" Width="146"/>
        <TextBox x:Name="inputAddressLine2" TextWrapping="Wrap" Margin="219,0,0,0"/>
    </Grid>
    <Grid Height="30" Margin="8,0,8,7" VerticalAlignment="Bottom" Grid.Row="1">
        <TextBlock Margin="8,0,0,0" Text="Post Code" FontSize="16" Foreground="White" HorizontalAlignment="Left" Width="107"/>
        <TextBox x:Name="inputPostCode" TextWrapping="Wrap" Margin="219,0,0,0"/>
    </Grid>
    <Grid Height="30" VerticalAlignment="Bottom" Margin="8,0,8,9" Grid.Row="4">
        <TextBox x:Name="inputCounty" TextWrapping="Wrap" Margin="220,0,0,0"/>
        <TextBlock Margin="8,0,0,0" Text="County" FontSize="16" Foreground="White" HorizontalAlignment="Left" Width="155"/>
    </Grid>
    <Grid Margin="8,8,8,5" Grid.Row="3">
        <TextBox x:Name="inputTown" TextWrapping="Wrap" Margin="219,0,0,0"/>
        <TextBlock Margin="8,0,0,0" Text="AddressLine2" FontSize="16" Foreground="White" HorizontalAlignment="Left" Width="165"/>
    </Grid>
    <Grid Margin="8" Grid.Row="5">
        <TextBlock Text="Number" FontSize="16" Foreground="White" Margin="8,0,0,0" HorizontalAlignment="Left" Width="178"/>
        <TextBox x:Name="inputNumber" TextWrapping="Wrap" Margin="220,0,0,0"/>
    </Grid>
</Grid>

仍然只能让它达到一个深度,返回6个网格和一个按钮!

我目前的Traverse功能是:

public static IEnumerable<UIElement> Traverse(this UIElementCollection source)
    {
        source.OfType<Grid>().SelectMany(v => Traverse(v.Children));
        //This is the top level.
        foreach (UIElement item in source)
        {
            yield return item;
        }
    }

这只知道我们正在处理网格,所以不需要第二个参数。我只是从迭代器盒中产生,而不是从第一行返回,它应该回调到具有子网格的Traverse函数。

1 个答案:

答案 0 :(得分:3)

以下是我用来提供此类功能的功能: -

    public static IEnumerable<DependencyObject> Descendents(this DependencyObject root)
    {
        int count = VisualTreeHelper.GetChildrenCount(root);
        for (int i = 0; i < count; i++)
        {
            var child = VisualTreeHelper.GetChild(root, i);
            yield return child;
            foreach (var descendent in Descendents(child))
                yield return descendent;
        }
    }

使用该扩展方法,您的代码将变为: -

 foreach(var txt in baseLayout.Descendents().OfType<TextBox>())
 {
     txt.reset();
 }

请注意,避免使用“foreach”扩展方法是一种选择。我不喜欢LINQEsq扩展方法的想法实际上是变异或为他的应用程序做任何事情。我更喜欢使用正确的foreach来实际操作查询结果。

编辑为“扩展方法瘾君子”(如果您明白 那么): -

 public static IEnumerable<T> ForEach(this IEnumerable<T> items, Action<T> fn)
 {
     foreach (T item in items)
        fn(item);
 }

编辑您的代码出了什么问题。

主要是您的Traverse方法中的这一行主要是您的问题的主要原因: -

 t.Traverse(fnRecurse);

它会返回IEnumerable<UIElement>,但您不会对其执行任何操作,甚至不会将结果存储在变量中。

这一行: -

var g = source.OfType<Grid>();
对于找到的每个UIElement,

会导致枚举的每个网格都被枚举。因此,例如,如果source包含TextBox和2 Grids,则上面的行被调用3次。两个网格都将在内部foreach内运行两次。

这一行: -

yield return itemsub;

好的itemsub始终为Grid,后续的TypeOf<TextBox>会将其过滤掉。

因此,此代码将返回的唯一TextBox是在初始UIElementCollection中找到的任何TextBox。