在 TabControl 中获取可见的 TabItems
Get visible TabItems in TabControl
我有一个可滚动的 TabControl TabItems/Header。我的 TabControl ItemsSources 绑定到 ObservableCollection。有什么方法可以在 TabControl 中获取可见的 TabItems。
假设我有 20 个 TabItem,只有 7 个可见,或者 10 个或更多,具体取决于用户执行的操作类型(例如减少 window)。如何以编程方式检索可见的 TabItems?
这是 XAML 代码:
<TabControl x:Name="tabControl"
ItemsSource="{Binding Data}"
ScrollViewer.CanContentScroll="True"
Width="440"
Height="350"
TabStripPlacement="Top"
Background="LightGray"
BorderBrush="Blue">
<TabControl.Template>
<ControlTemplate TargetType="TabControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Hidden" >
<TabPanel x:Name="HeaderPanel"
Panel.ZIndex ="1"
KeyboardNavigation.TabIndex="1"
Grid.Column="0"
Grid.Row="0"
Margin="2,2,2,0"
IsItemsHost="true" />
</ScrollViewer>
<ContentPresenter x:Name="PART_SelectedContentHost"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Margin="{TemplateBinding Padding}"
ContentSource="SelectedContent" Grid.Row="1" />
</Grid>
</ControlTemplate>
</TabControl.Template>
</TabControl>
<Button x:Name="button"
Content="Add Items"
Margin="5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="120"
Click="Button_Click" />
<Button x:Name="button2"
Content="TabItems in View"
Margin="5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="120"
Click="Button_Click2" />
下面是我的代码:
public partial class MainWindow : Window
{
private static int counter = 0;
public List<TabItem> visibleItems = new List<TabItem>();
public ObservableCollection<string> Data { get; set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.Data = new ObservableCollection<string>()
{
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter
};
}
private void Button_Click( object sender, RoutedEventArgs e )
{
// add tab item and reduce the TabControl window:
this.Data.Add( "newItem" + ++counter );
this.tabControl.Width = 330d;
}
private void Button_Click2( object sender, RoutedEventArgs e )
{
visibleItems = new List<TabItem>();
foreach( var item in tabControl.Items )
{
TabItem tabItem = this.tabControl.ItemContainerGenerator.ContainerFromItem( item ) as TabItem;
if( tabItem != null && tabItem.Visibility == Visibility.Visible )
{
visibleItems.Add( tabItem );
}
}
Debug.WriteLine( $"{visibleItems.Count}" ); // always return 20 ...
}
}
Button_Click2 事件处理程序是我实现逻辑以检索可见 TabItems 的地方。但它不起作用。它总是 return 我的项目来源总数。
您将不得不迭代项目容器并累积项目宽度。然后获取TabPanel的ScrollViewer
,根据ScrollViewer.HorizontalOffset
(起始位置)和ScrollViewer.ViewportWidth
(确定实际可见项)收集可见项。
关键是滚动查看器的宽度是以 DIP 而非项目来衡量的。当您可以确保所有项目容器具有相同的宽度时,该算法会更精确。
您可以在 How to: Find DataTemplate-Generated Elements 找到 FindVisualChild 的实现。
private void Button_Click2(object sender, RoutedEventArgs e)
{
var scrollViewer = FindVisualChild<ScrollViewer>(this.tabControl);
if (scrollViewer != null)
{
int startIndex = GetFirstVisibleItemIndex(scrollViewer.HorizontalOffset);
double totalItemContainerWidth = 0;
List<(object Item, TabItem ItemContainer)> visibleItems = GetVisibleItems(startIndex, scrollViewer.ViewportWidth);
}
}
private int GetFirstVisibleItemIndex(double horizontalScrollViewerOffset)
{
double totalItemContainerWidth = 0;
int itemIndex = 0;
while (totalItemContainerWidth < horizontalScrollViewerOffset)
{
var hiddenItemContainer = this.tabControl.ItemContainerGenerator.ContainerFromIndex(++itemIndex) as FrameworkElement;
totalItemContainerWidth += hiddenItemContainer.ActualWidth;
}
return itemIndex;
}
// Returns a collection of tuples (item and item container tuples)
private List<(object Item, TabItem ItemContainer)> GetVisibleItems(int startIndex, double viewportWidth)
{
var visibleItems = new List<(object Item, TabItem ItemContainer)>();
double totalItemContainerWidth = 0;
for (int currentVisibleItemIndex = startIndex; currentVisibleItemIndex < this.tabControl.Items.Count; currentVisibleItemIndex++)
{
var visibleItemContainer = this.tabControl.ItemContainerGenerator.ContainerFromIndex(currentVisibleItemIndex) as TabItem;
totalItemContainerWidth += visibleItemContainer.ActualWidth;
if (totalItemContainerWidth > viewportWidth + visibleItemContainer.ActualWidth)
{
break;
}
object visibleItem = this.tabControl.Items[currentVisibleItemIndex];
visibleItems.Add((visibleItem, visibleItemContainer));
}
return visibleItems;
}
我有一个可滚动的 TabControl TabItems/Header。我的 TabControl ItemsSources 绑定到 ObservableCollection。有什么方法可以在 TabControl 中获取可见的 TabItems。
假设我有 20 个 TabItem,只有 7 个可见,或者 10 个或更多,具体取决于用户执行的操作类型(例如减少 window)。如何以编程方式检索可见的 TabItems?
这是 XAML 代码:
<TabControl x:Name="tabControl"
ItemsSource="{Binding Data}"
ScrollViewer.CanContentScroll="True"
Width="440"
Height="350"
TabStripPlacement="Top"
Background="LightGray"
BorderBrush="Blue">
<TabControl.Template>
<ControlTemplate TargetType="TabControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Hidden" >
<TabPanel x:Name="HeaderPanel"
Panel.ZIndex ="1"
KeyboardNavigation.TabIndex="1"
Grid.Column="0"
Grid.Row="0"
Margin="2,2,2,0"
IsItemsHost="true" />
</ScrollViewer>
<ContentPresenter x:Name="PART_SelectedContentHost"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Margin="{TemplateBinding Padding}"
ContentSource="SelectedContent" Grid.Row="1" />
</Grid>
</ControlTemplate>
</TabControl.Template>
</TabControl>
<Button x:Name="button"
Content="Add Items"
Margin="5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="120"
Click="Button_Click" />
<Button x:Name="button2"
Content="TabItems in View"
Margin="5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="120"
Click="Button_Click2" />
下面是我的代码:
public partial class MainWindow : Window
{
private static int counter = 0;
public List<TabItem> visibleItems = new List<TabItem>();
public ObservableCollection<string> Data { get; set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.Data = new ObservableCollection<string>()
{
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter,
"item" + ++counter
};
}
private void Button_Click( object sender, RoutedEventArgs e )
{
// add tab item and reduce the TabControl window:
this.Data.Add( "newItem" + ++counter );
this.tabControl.Width = 330d;
}
private void Button_Click2( object sender, RoutedEventArgs e )
{
visibleItems = new List<TabItem>();
foreach( var item in tabControl.Items )
{
TabItem tabItem = this.tabControl.ItemContainerGenerator.ContainerFromItem( item ) as TabItem;
if( tabItem != null && tabItem.Visibility == Visibility.Visible )
{
visibleItems.Add( tabItem );
}
}
Debug.WriteLine( $"{visibleItems.Count}" ); // always return 20 ...
}
}
Button_Click2 事件处理程序是我实现逻辑以检索可见 TabItems 的地方。但它不起作用。它总是 return 我的项目来源总数。
您将不得不迭代项目容器并累积项目宽度。然后获取TabPanel的ScrollViewer
,根据ScrollViewer.HorizontalOffset
(起始位置)和ScrollViewer.ViewportWidth
(确定实际可见项)收集可见项。
关键是滚动查看器的宽度是以 DIP 而非项目来衡量的。当您可以确保所有项目容器具有相同的宽度时,该算法会更精确。
您可以在 How to: Find DataTemplate-Generated Elements 找到 FindVisualChild 的实现。
private void Button_Click2(object sender, RoutedEventArgs e)
{
var scrollViewer = FindVisualChild<ScrollViewer>(this.tabControl);
if (scrollViewer != null)
{
int startIndex = GetFirstVisibleItemIndex(scrollViewer.HorizontalOffset);
double totalItemContainerWidth = 0;
List<(object Item, TabItem ItemContainer)> visibleItems = GetVisibleItems(startIndex, scrollViewer.ViewportWidth);
}
}
private int GetFirstVisibleItemIndex(double horizontalScrollViewerOffset)
{
double totalItemContainerWidth = 0;
int itemIndex = 0;
while (totalItemContainerWidth < horizontalScrollViewerOffset)
{
var hiddenItemContainer = this.tabControl.ItemContainerGenerator.ContainerFromIndex(++itemIndex) as FrameworkElement;
totalItemContainerWidth += hiddenItemContainer.ActualWidth;
}
return itemIndex;
}
// Returns a collection of tuples (item and item container tuples)
private List<(object Item, TabItem ItemContainer)> GetVisibleItems(int startIndex, double viewportWidth)
{
var visibleItems = new List<(object Item, TabItem ItemContainer)>();
double totalItemContainerWidth = 0;
for (int currentVisibleItemIndex = startIndex; currentVisibleItemIndex < this.tabControl.Items.Count; currentVisibleItemIndex++)
{
var visibleItemContainer = this.tabControl.ItemContainerGenerator.ContainerFromIndex(currentVisibleItemIndex) as TabItem;
totalItemContainerWidth += visibleItemContainer.ActualWidth;
if (totalItemContainerWidth > viewportWidth + visibleItemContainer.ActualWidth)
{
break;
}
object visibleItem = this.tabControl.Items[currentVisibleItemIndex];
visibleItems.Add((visibleItem, visibleItemContainer));
}
return visibleItems;
}