【问题标题】:Controlling the TabControl active tab with a ComboBox使用 ComboBox 控制 TabControl 活动选项卡
【发布时间】:2011-07-29 05:10:06
【问题描述】:

我真正想要实现的是通过使用组合框作为导航控件来完全控制活动的 TabItem。

这是我到目前为止得到的:

    <TabControl Canvas.Left="26" Canvas.Top="27" Height="100" Name="TabControl1" Width="220">
        <TabItem Header="TabItem1" x:Name="TabItem1">
            <Grid />
        </TabItem>
        <TabItem Header="TabItem2" x:Name="TabItem2">
            <Grid />
        </TabItem>
    </TabControl>


    <ComboBox Canvas.Left="126" Canvas.Top="134" Height="23" Name="CmbTabs" Width="120" 
              ItemsSource="{Binding ElementName=TabControl1, Path=Items}" 
              SelectedValue="{Binding ElementName=TabControl1, Path=SelectedIndex}" 
              SelectedValuePath="TabIndex"
              DisplayMemberPath="Header"/>

仍然唯一真正有效的是当我按下组合框的切换按钮时显示的列表。 即使通过列表选择一个 tabitem 名称也不会做任何事情,它甚至不会更新组合框的选定值文本框。

有什么帮助吗?

编辑: 好的,史蒂夫罗宾斯的回答对于“控制”问题很有效。

如果在组合框下拉列表中选择一个项目不会更新组合框的值,该怎么办? (组合框文本框仍然是空白的!!)

【问题讨论】:

  • 您找到 DisplayMemberPath="Header" 不起作用的原因了吗?

标签: wpf combobox tabcontrol


【解决方案1】:

如果您尝试从 Combo 控制 TabControl,那么它看起来有点倒退。如果您更改选项卡控件上的 SelectedIndex 以将其绑定到组合,它应该可以工作:

<TabControl Canvas.Left="26" Canvas.Top="27" Height="100" Name="TabControl1" Width="220" SelectedIndex="{Binding ElementName=CmbTabs, Path=SelectedIndex}">
        <TabItem Header="TabItem1" x:Name="TabItem1">
            <Grid />
        </TabItem>
        <TabItem Header="TabItem2" x:Name="TabItem2">
            <Grid />
        </TabItem>
    </TabControl>

【讨论】:

    【解决方案2】:

    我开始研究这个是因为我在使用组合框时遇到了一些问题。不幸的是,我还没有解决我的问题,但我可以提供一些额外的见解和解决这个问题的方法。首先,让我们从我对原始 xaml 的更改开始。

        <TabControl Height="100" Name="TabControl1" Width="220">
            <TabItem Header="TabItem1" x:Name="TabItem1">
                <TextBlock Text="TabItem1 Content" />
            </TabItem>
            <TabItem Header="TabItem2" x:Name="TabItem2">
                <TextBlock Text="TabItem2 Content" />
            </TabItem>
        </TabControl>
    
    
        <ComboBox Height="23" Name="CmbTabs" Width="120" 
            ItemsSource="{Binding ElementName=TabControl1, Path=Items}"
            SelectedIndex="{Binding ElementName=TabControl1, Path=SelectedIndex}"
            DisplayMemberPath="Name"
            >
        </ComboBox>
    

    请注意,我们可以在选项卡的 SelectedIndex 和组合控件之间创建 TwoWay 绑定(本例中的默认值),而不是创建从选项卡控件到 ComboBox 的绑定,反之亦然。接下来,让我们向 TabItems 添加一些内容。在这一点上,类似于史蒂夫的建议,我们已经解决了“控制”问题。也就是说,更改选定的 TabItem 会更改选定的 ComboBox 项(您必须相信我,否则请继续阅读!)并且更改 ComboBox 会更改选定的 TabItem。太好了!

    上述 xaml 还将 DiplayMemberPath 属性更改为“名称”。我想你会发现这消除了hughdbrown的“奇怪结果”。回想一下,Header 属性(一个对象)是由 ContentPresenter 包装的。我相信,如果没有提供模板,则默认行为是将 Header 对象显示为 TextBlock 中的字符串。因此,“奇怪的结果”正确报告 TextBlock 控件不包含 Header 属性。

    现在让我们对之前的 ComboBox xaml 进行一些更改。

        <ComboBox Height="23" Name="CmbTabs" Width="120" 
            ItemsSource="{Binding ElementName=TabControl1, Path=Items}"
            SelectedIndex="{Binding ElementName=TabControl1, Path=SelectedIndex}"
            >
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding}"/>
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
    

    这实际上产生了一个有趣的结果,并利用了我们添加到每个 TabItem 的内容。当此代码运行时,您会注意到 ComboBox 现在显示所选 TabItem 的内容。此外,该列表现在为每个 TabItem 显示“System.Windows.Controls.TabItem...”。我们可以将 TextBlock 绑定更改为 {Binding Header} 并显示 Header 对象,但 ComboBox 仍然显示所选 TabItem 的内容。由于星期五晚上很晚,世界上没有足够的啤酒,我没有调查可能的原因。不过,我确实有一个解决方法!

    首先,让我们创建一个 ValueConverter 来将 TabControl 的 Items 集合转换为我们可以使用的东西。这是代码。

    public class TabItemCollectionConverter : IValueConverter
    {
        #region IValueConverter Members
    
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            ItemCollection collection = value as ItemCollection;
            IList<string> names = new List<string>();
            foreach (TabItem ti in collection.SourceCollection)
            {
                names.Add(ti.Header.ToString());
            }
            return names;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    
        #endregion
    }
    

    转换器只是从 TabControl 的 Items 集合中创建一个新集合,该集合包含来自每个 TabItem 的字符串化 Header 对象。这适用于简单的 Header 对象,但显然有局限性。现在让我们考虑如何在 xaml 中使用它。

        <ComboBox Height="23" Name="CmbTabs" Width="120" 
            ItemsSource="{Binding ElementName=TabControl1, Path=Items, Converter={StaticResource ItemConverter}}"
            SelectedIndex="{Binding ElementName=TabControl1, Path=SelectedIndex}"
            >
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding}"/>
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
    

    请记住,在 ItemsSource 绑定中使用的 ValueConverter 返回一个新集合。在本例中,我们将 TabControl 的 Items 集合转换为字符串集合。不要忘记创建转换器 StaticResource!它看起来像这样。

        <local:TabItemCollectionConverter x:Key="ItemConverter"/>
    

    现在,使用转换器,整个蜡球按预期工作。

    仍然让我感到困惑的是,为什么 ComboBox 在列表中显示 TabItem 标题并将 TabItem 内容显示为选择项。毫无疑问,有一些明显的解释,但正如我所说,现在是星期五......

    希望这会有所帮助!

    【讨论】:

    • 我刚刚注意到 Robert 的帖子,其中他提供了一个链接,声称 ComboBox 当前项目显示的问题是一个错误。感谢罗伯特为我节省了一些头/墙撞击!
    【解决方案3】:

    我一直在玩这个。在进行选择之后,ComboBox 的数据绑定的来源发生了一些变化。什么的。

    我添加了诊断命名空间:

    xmlns:debug="clr-namespace:System.Diagnostics;assembly=WindowsBase"
    

    我将您的 更改为带有大喇叭数字的文本框,以便我可以看到事情确实在发生变化:

        <TabItem Header="TabItem1" x:Name="TabItem1">
            <TextBlock Name="tb1" FontSize="24" Text="1" Width="100" Height="26" />
        </TabItem>
    

    当我运行应用程序时,我发现诊断报告了一个奇怪的结果:

    System.Windows.Data 错误:39: BindingExpression 路径错误:“标头” 在“对象”上找不到属性 ''文本块'(名称='tb1')'。 绑定表达式:路径=标题; DataItem='TextBlock' (名称='tb2'); 目标元素是'TextBlock' (名称='');目标属性是“文本” (输入“字符串”)

    我尝试在启动时在代码中设置一次数据绑定:

        public Window1()
        {
            InitializeComponent();
    
            Binding positionBinding = new Binding("Items");
            positionBinding.ElementName = "TabControl1";
            positionBinding.Path = new PropertyPath("Items");
            positionBinding.Mode = BindingMode.OneTime;
    
            CmbTabs.SetBinding(ComboBox.ItemsSourceProperty, positionBinding);
            CmbTabs.DisplayMemberPath = "Header";
        }
    

    在选择和更改 TabItem 后,它仍然会切换 CombobBox 以不显示任何选定项。就好像TabControl改变选择后DataContext切换到TabControl.TabItem.TextBlock一样。

    所以我没有确切的答案给你,但我有一些结果让你努力。

    Bea Stollnitz 有一篇关于使用这种诊断技术的好文章。 "How can I debug WPF bindings?"

    【讨论】:

      【解决方案4】:

      根据hughdbrown 的回答,我发现this post on MSDN 将您的问题描述为一个错误。您可以使用此 XAML 重现它(与您的 XAML 存在相反的问题):

      <Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
          <TabControl Name="TabControl1" Width="220" Height="100" Canvas.Left="26" Canvas.Top="27">
              <TabItem x:Name="TabItem1" Header="TabItem1">
                  foobar
              </TabItem>
              <TabItem x:Name="TabItem2" Header="TabItem2">
                  fizzbuzz
              </TabItem>
          </TabControl>
          <ComboBox Name="CmbTabs" Width="120" Height="23" Canvas.Left="126" Canvas.Top="134"
                    ItemsSource="{Binding ElementName=TabControl1, Path=Items}"
                    DisplayMemberPath="Length"
                    SelectedIndex="{Binding ElementName=TabControl1, Path=SelectedIndex}"/>
      </Canvas>
      

      正如您所见,Length 绑定工作正常,除了在下拉列表中,它离开 TabItem 而不是里面的字符串。

      我不确定它是否适合您的目的,但您可以通过稍微不那么优雅并在 ComboBoxItems 中重现您的标题来绕过它:

      <Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
          <TabControl Name="TabControl1" Width="220" Height="100" Canvas.Left="26" Canvas.Top="27">
              <TabItem x:Name="TabItem1" Header="TabItem1">
                  <Grid/>
              </TabItem>
              <TabItem x:Name="TabItem2" Header="TabItem2">
                  <Grid/>
              </TabItem>
          </TabControl>
          <ComboBox Name="CmbTabs" Width="120" Height="23" Canvas.Left="126" Canvas.Top="134"
                    SelectedIndex="{Binding ElementName=TabControl1, Path=SelectedIndex}">
              <ComboBoxItem>TabItem1</ComboBoxItem>
              <ComboBoxItem>TabItem2</ComboBoxItem>
          </ComboBox>
      </Canvas>
      

      【讨论】:

        猜你喜欢
        • 2011-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多