使用ComboBox控制TabControl活动选项卡

时间:2009-02-13 14:27:40

标签: wpf combobox tabcontrol

我真正想要实现的是通过使用组合框作为导航控件来完全控制活动的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"/>

当我按下组合框的togglebutton时,实际上唯一可行的是显示的列表。 即使在列表中选择tabitem名称也没有做任何事情,它甚至不更新组合框的选定值文本框。

任何帮助?

编辑: 好吧,史蒂夫罗宾斯的答案在“控制”问题上运作良好。

选择组合框下拉列表中的项目不会更新组合框的值这一事实呢? (组合框文本框仍然是空白!!)

4 个答案:

答案 0 :(得分:3)

如果你试图从Combo控制TabControl,那么它看起来有点倒退..如果你更改tab控件上的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>

答案 1 :(得分:0)

我一直在玩这个游戏。选择完成后,ComboBox的数据绑定源会发生变化。或者其他什么。

我添加了Diagnostics命名空间:

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

我改变了你的&lt; Grid /&gt;进入具有大喇叭数的TextBox,以便我可以看到事情确实在发生变化:

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

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

  

System.Windows.Data错误:39:   BindingExpression路径错误:'标题'   在'对象'上找不到的属性   ''TextBlock'(Name ='tb1')'。   BindingExpression:路径=报头;   DataItem ='TextBlock'(Name ='tb2');   目标元素是'TextBlock'   (名称= '');目标属性是'文字'   (输入'String')

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

    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?"

答案 2 :(得分:0)

根据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>

正如你所看到的,长度绑定工作正常,除了在下拉列表中,它是从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>

答案 3 :(得分:0)

我开始关注这个因为我对组合框有些问题。不幸的是,我还没有解决我的问题,但我可以为这个问题提供一些额外的见解和解决方法。首先,让我们从对原始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属性更改为“Name”。我想你会发现这消除了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内容显示为选择。毫无疑问,有一些明显的解释,但正如我所说,这是周五......

希望这有帮助!