Search code examples
wpfmvvmtabcontroltabitem

Can only iterate controls from first TabItem of a TabControl


I am a beginner of wpf. I am going get numbers of buttons from two tabs. It should be 5. However, I can only get the two buttons from the first tab.

I have tried to switch to the tab2, then no buttons can be found.

Do you know how I can get all the controls from all tabs? I am going to bind events to all canvas and buttons in each tab.

Here is my MainWindow:

<TabControl Name="tabMain" ItemsSource="{Binding Tabs}">
        <TabControl.ItemTemplate>
            <!-- this is the header template-->
            <DataTemplate>
                <TextBlock
                Text="{Binding Header}" />
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <DataTemplate DataType="{x:Type local:MyTabItem}">
                <ItemsControl ItemsSource="{Binding Content}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <Canvas />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate DataType="{x:Type local:MyTabItemCtrl}" >
                            <Button Content="{Binding Name}" />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                    <ItemsControl.ItemContainerStyle>
                        <Style TargetType="ContentPresenter">
                            <Setter Property="Canvas.Left" Value="{Binding Path=PosX}" />
                            <Setter Property="Canvas.Top" Value="{Binding Path=PosY}" />
                        </Style>
                    </ItemsControl.ItemContainerStyle>
                </ItemsControl>
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>

MainWindow.xaml.cs:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    var btns = UIHelper.FindVisualChildren<Button>(tabMain).ToList();
    // btns == 2
}

ViewModels:

public sealed class MyViewModel
{
    public ObservableCollection<MyTabItem> Tabs { get; set; }
    public MyViewModel()
    {
        Tabs = new ObservableCollection<MyTabItem>();

        ObservableCollection<MyTabItemCtrl> ct1 = new ObservableCollection<MyTabItemCtrl>();
        ct1.Add(new MyTabItemCtrl { Name = "aaa", Age = 5, PosX=10, PosY=10 });
        ct1.Add(new MyTabItemCtrl { Name = "bbb", Age = 5, PosX = 50, PosY = 10 });

        ObservableCollection<MyTabItemCtrl> ct2 = new ObservableCollection<MyTabItemCtrl>();
        ct2.Add(new MyTabItemCtrl { Name = "aaa", Age = 6, PosX = 10, PosY = 10 });
        ct2.Add(new MyTabItemCtrl { Name = "bbb", Age = 6, PosX = 55, PosY = 10 });
        ct2.Add(new MyTabItemCtrl { Name = "ccc", Age = 6, PosX = 100, PosY = 10 });

        Tabs.Add(new MyTabItem { Header = "One", Content = ct1 });
        Tabs.Add(new MyTabItem { Header = "Two", Content = ct2 });
    }
}
public sealed class MyTabItem
{
    public string Header { get; set; }
    public ObservableCollection<MyTabItemCtrl> Content { get; set; }
}

public sealed class MyTabItemCtrl
{
    public string Name { get; set; }
    public int Age { get; set; }
    public double PosX { get; set; }
    public double PosY { get; set; }
}

Solution

  • Only the currently visible TabItem is loaded into the visual tree so you won't be able to use your UIHelper.FindVisualChildren method to find any elements that is not currently visible.

    When you switch away from a tab, its content are unloaded from the visual tree. So these Buttons don't really exist when another tab is selected.

    A ContentTemplate is just a template. It has to be realized before any actual Button elements are created and added to the visual tree.