Silverlight TabControl - 从给定的控件中查找和选择一个TabItem

6
我正在构建一个LOB应用程序,它有一个主要部分和一个TabControl,其中包含各种TabItems。在保存时,任何错误字段都会被突出显示,并且第一个错误字段会获得焦点。
如果唯一的错误字段位于未选择的选项卡上,则该选项卡应变为选定状态,并突出显示并获得焦点。但是我无法做到这一点。
看起来发生的情况是未选择的选项卡不在可视树中,因此您无法导航回拥有TabItem的选项卡并将其设置为TabControl中当前选定的TabItem。
有人有关于如何完成这个功能的想法吗?
5个回答

4

加载TabItem:

tabControl.SelectedItem = tabItemOfInterest;
tabControl.UpdateLayout();

这会导致tabItemOfInterest与TabItem内的所有控件一起加载。

仅下面一行不会加载tabItemOfInterest:

tabControl.SelectedItem = tabItemOfInterest;

不过,我非常感兴趣的是David采取的方法来找到错误控制。


1

我在我的一个网站(YinYangMoney)上使用TabControl进行导航,并构建了一些扩展方法,帮助我使用标签名称选择选项卡。以下是应该适用于您的代码片段。

扩展类:

using System;
using System.Linq;
using System.Windows.Controls;

namespace MyApplication
{
    internal static class Extensions
    {
        // Extension for TabControl
        internal static void SelectTab(this TabControl tabControl, this TabItem tabItem)
        {
            if (tabControl == null || tabItem == null)
                return null;

            SelectTab(tabControl, tabItem.Tag);
        }

        // Extension for TabControl
        internal static void SelectTab(this TabControl tabControl, string tabTagName)
        {
            if (tabControl == null)
                return null;

            // Find the TabItem by its Tag name
            TabItem mainTabItem = tabControl.FindByTag(tabTagName);
            if (mainTabItem == null)
                return;

            // Check to see if the tab needs to be selected
            if (tabControl.SelectedItem != mainTabItem)
                tabControl.SelectedItem = mainTabItem;
        }

        // Extension for TabControl
        internal static TabItem FindByTag(this TabControl tabControl, string tagFragment)
        {
            if (tabControl == null || tagFragment == null)
                return null;

            return tabControl.Items
                    .OfType<TabItem>()
                    .Where(item => item.Tag != null && item.Tag.ToString().StartsWithIgnoreCase(tagFragment))
                    .FirstOrDefault();
        }

        // Extension for string
        internal static bool StartsWithIgnoreCase(this string source, string target)
        {
            return source.StartsWith(target, StringComparison.CurrentCultureIgnoreCase);
        }
    }
}

您的TabControl和TabItems的XAML可能如下所示:

<Controls:TabControl x:Name="x_TabControl">
    <Controls:TabItem Header="Welcome" Tag="/Home/Welcome" x:Name="x_WelcomeTab" />
    <Controls:TabItem Header="FAQ" Tag="/Home/FAQ" />
    <Controls:TabItem Header="Contact Us" Tag="/Home/Contact_Us" />
    <Controls:TabItem Header="Privacy Policy" Tag="/Home/Privacy_Policy" />
    <Controls:TabItem Header="My Account" Tag="/Home/My_Account" />
</Controls:TabControl>

你可以这样选择欢迎的 TabItem:

x_TabControl.SelectTab("/Home/Welcome");  

或者

x_TabControl.SelectTab(x_WelcomeTab);

问题在于找到要选择的TabItem,该TabItem基于将显示在其中的控件。 - David Gray Wright

1

我的解决方案使用附加属性TabItem。 创建类TabItemExtender

/// <summary>
/// TabItem Extender class with TabItem property
/// </summary>
public class TabItemExtender
{
    #region property getters/setters
    /// <summary>
    /// TabItem attached dependency property
    /// </summary>
    public static readonly DependencyProperty TabItemProperty = DependencyProperty.RegisterAttached("TabItem", typeof(TabItem), typeof(TabItemExtender), null);

    /// <summary>
    /// TabItem Property getter
    /// </summary>
    public static TabItem GetNavigateUri(DependencyObject source)
    {
        return (TabItem)source.GetValue(TabItemExtender.TabItemProperty);
    }

    /// <summary>
    /// TabItem Property setter
    /// </summary>
    public static void SetNavigateUri(DependencyObject target, TabItem value)
    {
        target.SetValue(TabItemExtender.TabItemProperty, value);
    }
    #endregion
}

TabControl 的加载事件中执行以下操作:
private void ExtendedTabControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
    foreach (object item in this.Items)
    {
        var tabItem = item as TabItem;
        if (tabItem != null && tabItem.Content != null)
        {
            var element = (FrameworkElement)tabItem.Content;
            element.SetValue(TabItemExtender.TabItemProperty, tabItem);
        }
    }
}

在设置焦点之前:

var element = (UIElement)control;
while (element != null)
{
    //Get TabItem
    var tabItem = (TabItem)element.GetValue(TabItemExtender.TabItemProperty);

    if (tabItem != null)
    {
        if (!tabItem.IsSelected && tabItem.IsEnabled)
        {
            tabItem.IsSelected = true;
            ((TabControl)tabItem.Parent).UpdateLayout();
        }

        break;
    }

    element = (UIElement)VisualTreeHelper.GetParent(element);
}

control.Focus();

1

我是如何解决这个问题的(通过询问首席架构师)...

创建一个带有一个方法Activate的接口ITabActivator。

创建一个从Grid和ITabActivator派生的类TabPageActivator。其构造函数接受TabITem和TabControl。

将TabPageActivator添加到TabItem.Contents而不是添加简单的Grid。

更改父级检测以使用...

DependencyObject parent = _Control.Parent;

...而不是使用VisualTreeHelper。

因此,当您导航层次结构时,请测试...

if (parent is TabActivator) (parent as ITabActivator).Activate()

...所以当调用Activate时

m_TabControl.SelectedItem = m_TabItem; //来自构造函数参数。

...并且不要忘记您可能有嵌套选项卡,因此您需要继续向上遍历层次结构。


你肯定看到每个选项卡都会先被选择,然后才显示最终的验证选项卡吧? - Chris S
不,它没有问题,一切都正常。我的解决方案可能有点简短了,如果您需要,我可以详细说明一下? - David Gray Wright
小细节:我认为在控件上使用非默认构造函数是一个非常糟糕的想法。这会使控件不太友好于 XAML... - TDaver

0

我知道一种方法,但它很丑陋。它需要使用一个间隔为几毫秒的DispatcherTimer。在Page_Loaded中,您将启动计时器。然后,在每次tick上,它会将一个选项卡项的IsSelected设置为true。在下一个tick上,它选择下一个选项卡项等,直到所有选项卡都被选择。然后,您必须再次选择第一个项目并结束计时器。这将强制加载选项卡项中的视觉效果。

在此操作期间,您还必须使用边框或其他东西覆盖TabControl。否则,用户将快速看到所有选项卡项闪过。


是的,这不是一个很好的解决方案。 - David Gray Wright
真的。我亲自使用这个解决方案,但每次看到代码都让我感到不舒服。 :) 如果我再做一遍,我会封装它为一个行为,使其不可见。不幸的是,我想不出另一种方法来渲染视觉效果。 - Henrik Söderlund

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接