2010-01-26 38 views
2

我需要一個擴展方法來遍歷我的Silverlight頁面上的所有文本框。假設我總是使用網格佈局,然後我有:遞歸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個網和一個按鈕!

我現在的導線功能是:

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函數中。

回答

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); 
} 

編輯什麼是你的代碼錯誤。

主要嘛此行深在你遍歷方法是你的問題的主要原因: -

t.Traverse(fnRecurse); 

它returnns一個IEnumerable<UIElement>但你不要用它做任何事情,甚至沒有結果存儲在一個變量。

而且這條線: -

var g = source.OfType<Grid>(); 

會導致每個網格發現例舉爲,每一個的UIElement發現。所以例如,如果源文件包含一個TextBox和2個網格,則上面的這一行被調用3次。這兩個網格將通過內部foreach兩次運行。

而且這條線: -

yield return itemsub; 

以及itemsub始終是一個Grid並將於隨後的TypeOf<TextBox>被過濾掉。

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

+0

安東尼,我是一個擴展方法癮君子,因此我的ForEach!當然,這不會讓我使用收益率。 Ta代碼,我會把它,但我會避免一段時間滴答打開一個純粹的擴展方法出現! – DavidA 2010-01-26 16:29:36

+0

你的方法有效,我的方法不行。這是一天結束......我需要知道爲什麼:) – DavidA 2010-01-26 16:45:51

+0

@DavidA:我已經爲你添加了一個ForEach,儘管我對它的感覺如何(相信我,這真的是一個壞主意)。這將幫助我向您描述爲什麼您的原始代碼不起作用,如果a)您展示您的ForEach擴展方法和b)迭代的XAML樣本。 – AnthonyWJones 2010-01-26 16:52:20