Управление активной вкладкой TabControl с помощью ComboBox - PullRequest
4 голосов
/ 13 февраля 2009

Чего я действительно пытаюсь добиться, так это полностью контролировать активный 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"/>

Тем не менее, единственное, что действительно работает, - это список, который появляется, когда я нажимаю кнопку-переключатель в выпадающем списке. Даже выбор имени табуляции через список ничего не делает, он даже не обновляет выбранное значение в поле со списком.

Любая помощь?

Edit: Хорошо, ответ Стива Роббинса отлично сработал для «контролирующего» вопроса.

А как насчет того, что выбор элемента в выпадающем списке в выпадающем списке не обновляет значение в выпадающем списке? (текстовое поле со списком остается пустым !!)

Ответы [ 4 ]

3 голосов
/ 13 февраля 2009

Если вы пытаетесь управлять TabControl из Combo, тогда он выглядит немного назад для меня ... если вы измените SelectedIndex на элементе управления Tab, чтобы связать его с 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>
0 голосов
/ 19 сентября 2009

Я начал смотреть на это из-за некоторых проблем со списком. К сожалению, я не решил свою проблему, но я могу дать дополнительное понимание и обойти эту проблему. Прежде всего, давайте начнем с моих изменений в исходном 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>

Обратите внимание, что вместо создания привязки из элемента управления вкладками к ComboBox и наоборот мы можем создать привязку TwoWay (по умолчанию в данном случае) между SelectedIndex вкладки и элементами управления комбинированным. Далее, давайте добавим некоторый контент в TabItems. На данный момент, подобно предложению Стива, мы исправили «контролирующую» проблему. То есть, изменение выбранного TabItem изменяет выбранный элемент ComboBox (вам придется доверять мне в этом вопросе или продолжайте читать!), А изменение ComboBox изменяет выбранный TabItem. Отлично!

Приведенный выше xaml также изменяет свойство DiplayMemberPath на «Имя». Я думаю, вы обнаружите, что это устраняет «странный результат» Хьюдбрауна. Напомним, что свойство Header (объект) заключено в ContentPresenter. Я полагаю, что, если шаблон не указан, поведение по умолчанию заключается в отображении объекта Header в виде строки в TextBlock. Таким образом, «странный результат» правильно сообщает, что элемент управления TextBlock не содержит свойства Header.

Теперь давайте внесем некоторые изменения в предыдущий xaml ComboBox.

    <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. Кроме того, в списке теперь отображается «System.Windows.Controls.TabItem ...» для каждого TabItem. Мы можем изменить привязку TextBlock на {Binding Header} и отобразить объект Header, но ComboBox по-прежнему отображает содержимое выбранного TabItem. Поскольку в пятницу вечером уже поздно и в мире просто не хватает пива, я не стал искать возможные причины для этого. Тем не менее, у меня есть обходной путь!

Во-первых, давайте создадим ValueConverter для преобразования коллекции Items TabControl в то, что мы можем использовать. Вот код.

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
}

Преобразователь просто создает новую коллекцию из коллекции Items TabControl, которая содержит строковый объект Header из каждого TabItem. Это прекрасно работает для простых объектов 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>

Помните, что ValueConverter, используемый в привязке ItemsSource, возвращает новую коллекцию. В этом случае мы конвертируем коллекцию Items TabControl в коллекцию строк. Не забудьте создать конвертер StaticResource! Это выглядит примерно так.

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

Теперь, используя конвертер, весь шарик воска работает как положено.

Что меня по-прежнему удивляет, так это то, что ComboBox отображает заголовки TabItem в списке и содержимое TabItem в качестве выбора. Без сомнения, есть какое-то очевидное объяснение, но, как я уже сказал, сегодня пятница ...

Надеюсь, это поможет!

0 голосов
/ 17 февраля 2009

Основываясь на ответе hughdbrown, я нашел этот пост на 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>
0 голосов
/ 16 февраля 2009

Я много играл с этим. Что-то в источнике привязки данных ComboBox меняется после того, как выбор сделан. Или что-то.

Я добавил пространство имен диагностики:

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

И я изменил ваш на TextBox с большими звуковыми числами, чтобы я мог видеть, что все действительно меняется:

    <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» (Имя = ''); целевое свойство 'Текст' (введите '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";
    }

И он по-прежнему переключает CombobBox, чтобы не отображать выбранный элемент после выбора и изменения TabItem. Как будто DataContext переключается на TabControl.TabItem.TextBlock после того, как TabControl меняет выбор.

Так что у меня точно нет ответа для вас, но у меня есть некоторые результаты, над которыми вы можете поработать.

У Bea Stollnitz есть хорошая статья об использовании этого метода диагностики. «Как отладить привязки WPF?»

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...