【问题标题】:How do I know, when a WPF tab control page was successfully selected?我如何知道 WPF 选项卡控制页何时成功选择?
【发布时间】:2020-12-24 15:11:54
【问题描述】:

我想要什么

我需要在我的选项卡控件中选择一个选项卡并直观地滚动到某个元素。

我的标准 WPF 选项卡控件的 ItemSource 绑定到一组视图模型,这些视图模型没有引用其对应的视图(标准 MVVM 模式)。选项卡控件的 SelectedItem 属性也有一个绑定。

所需选项卡的视图模型和目标控件的名称作为参数给出。

我做了什么

当我打开我的窗口并且选项卡控件是Loaded 时,我通过设置 SelectedItem 属性导航到所需的选项卡。结果,显示了正确的标签页,并且我还在TabControl 上获得了所需的SelectionChanged 事件。

因为我的视图模型中没有对我的视图的引用,所以我需要沿着可视化树向下查找要查找的控件,以便将其显示在视图中。当我将选项卡控件的整个可视化树打印到日志文件中时,可以清楚地看到,我得到了 initial 选项卡页的可视化树,而 not新选的一个。我希望在新标签页加载后获得第二个Loaded 事件,但那没有发生。

问题

我如何知道我新选择的标签页何时加载,以便我可以沿着正确的可视化树走下去?

代码 sn-p

这是我用于遍历可视化图表的函数。我通过提供我的选项卡控件作为输入参数来调用它。我得到了完美的视觉树,但来自初始页面,而不是新选择的页面。

    public void FindVisualChildren(DependencyObject obj, string prefix = "")
    {
        if (obj != null)
        {
            var output = $"{prefix} obj = {obj}";

            if (obj is FrameworkElement frameworkElement && !string.IsNullOrEmpty(frameworkElement.Name))
                output += $", name = \"{frameworkElement.Name}\"";

            if (obj is TextBlock textBlock && !string.IsNullOrEmpty(textBlock.Text))
                output += $", text = \"{textBlock.Text}\"";

            if (obj is TextBox textBox && !string.IsNullOrEmpty(textBox.Text))
                output += $", text = \"{textBox.Text}\"";

            this.WarningWriteLine(() => output);

            for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {
                var child = VisualTreeHelper.GetChild(obj, i);

                FindVisualChildren(child, prefix + "  ");
            }
        }
    }

【问题讨论】:

  • 也许您需要将 TabItem(或 TabControl 的 SelectedItem 属性)而不是 TabControl 传递给 FindVisualChildren 函数。
  • 你好斯科特。当我在我的函数中使用选项卡控件时,我也会得到所有选项卡项。选项卡页内容的可视化树位于选项卡控件本身,而不是选项卡项上。标签项的可视化树只包含实际的标签(标题等)

标签: c# wpf visual-studio


【解决方案1】:

TabControl.SelectionChanged 出现的那一刻,与 TabControl.SelectedItem 相关的新内容已加载并可供访问。

您不必遍历完整的TabControl 树来获取内容元素。您可以通过从选定内容的容器开始遍历来缩短遍历:

// Handle TabControl.SelectionChanged routed event
private void OnSelectionChanged(object sender, RoutedEventArgs e)
{
  var tabControl = sender as TabControl;
  object selectedContentItem = tabControl.SelectedContent;
  
  // Visual tree root of content sub-tree
  var selectedContentItemConmtainer =
    tabControl.ItemContainerGenerator.ContainerFromItem(selectedContentItem);

  // Print sub-tree of currently selected content
  FindVisualChildren(selectedContentItemConmtainer);
}

替代版本

当使用单个数据模型,然后使用数据模板设计选项卡项和选项卡内容时,之前的版本将无济于事,因为TabControl.SelectedContentTabControl.SelectedItem 是相等的(参考)。

// Handle TabControl.SelectionChanged routed event
private void OnSelectionChanged(object sender, RoutedEventArgs e)
{
  var tabControl = sender as TabControl;
  
  // Visual tree root of content sub-tree
  var tabContentHost = tabControl.Template.FindName("PART_SelectedContentHost", tabControl) as ContentPresenter;

  // Print sub-tree of currently selected content
  FindVisualChildren(tabContentHost);
}

【讨论】:

  • 谢谢 BionicCode,但您的代码对我不起作用。我只使用您的代码获取所选标签项的内容,但我需要标签页的内容。即使我把代码加锁以避免运行时干扰,在将整个选项卡控件可视化树写入日志文件时,我仍然只能得到初始选项卡的可视化树。
  • 我已经测试了代码并且它可以工作。因为我们正在访问tabControl.SelectedContent,所以没有机会获得 TabItem,这将返回选项卡的实际内容,而不是像 tabControl.SelectedItem 那样的标题(TabItem)。你能展示一下你是如何设置你的 TabControl 的吗?也许我们需要根据您的不同设置进行调整。
  • 如果您的内容绑定到项目所绑定的同一个模型,那么这两个项目当然是相同的。请检查我更新答案的替代版本,如果这有帮助,请告诉我。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-20
  • 2013-02-13
  • 1970-01-01
  • 1970-01-01
  • 2011-12-31
  • 1970-01-01
相关资源
最近更新 更多