代码之家  ›  专栏  ›  技术社区  ›  Nikos Tsokos

使用组合框控制TabControl活动选项卡

  •  4
  • Nikos Tsokos  · 技术社区  · 17 年前

    我真正想要实现的是通过使用combobox作为导航控件来完全控制活动的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名称也没有任何作用,它甚至不会更新combobox的selected value文本框。

    编辑: 史蒂夫·罗宾斯的回答在“控制”问题上很有效。

    在combobox下拉列表中选择项目不会更新combobox的值,这一事实又如何呢?(组合框文本框仍然为空!!)

    4 回复  |  直到 14 年前
        1
  •  3
  •   Steven Robbins    17 年前

    如果您试图从组合中控制TabControl,那么在我看来它有点落后。。如果更改tab控件上的SelectedIndex以将其绑定到combo,则应该可以:

    <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
  •  1
  •   dan    16 年前

        <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之间创建双向绑定(本例中为默认绑定),而不是创建从选项卡控件到组合框的绑定,反之亦然。接下来,让我们向TabItems添加一些内容。此时,与Steve的建议类似,我们已经解决了“控制”问题。也就是说,更改选定的TabItem会更改选定的ComboBox项(您必须在这一点上信任我或继续阅读!),更改ComboBox会更改选定的TabItem。伟大的

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

        <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中的内容。当此代码运行时,您会注意到组合框现在显示所选选项卡项的内容。此外,列表现在为每个选项卡项显示“System.Windows.Controls.TabItem…”。我们可以将TextBlock绑定更改为{binding Header}并显示Header对象,但组合框仍然显示所选TabItem的内容。由于周五晚上已经很晚了,而且世界上没有足够的啤酒,所以我没有探究可能的原因。然而,我确实有一个解决办法!

    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的字符串化标题对象。这适用于简单的头对象,但显然有局限性。现在让我们考虑一下如何在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集合转换为string集合。不要忘记创建转换器静态资源!看起来像这样。

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

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

    仍然让我困惑的是,为什么组合框会在列表中显示TabItem标题和TabItem内容作为选择。毫无疑问,有一些明显的解释,但正如我所说,今天是星期五。。。

        3
  •  0
  •   hughdbrown    17 年前

    我一直在玩这个。选择后,组合框数据绑定的源会发生一些变化。或者别的什么。

    我添加了诊断名称空间:

    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路径错误:“标头” 在“对象”上找不到属性 “TextBlock”(Name='tb1')”。 BindingExpression:Path=Header; DataItem='TextBlock'(Name='tb2'); 目标元素是“TextBlock” (名称=“”);目标属性为“Text” (键入“字符串”)

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

        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
  •  0
  •   Robert Macnee    17 年前

    根据hughdbrown的回答,我发现 this post on MSDN 这将您的问题描述为bug。您可以使用这个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>
    
    推荐文章