Вам необходимо привязать «SelectedIndex» к собственности в вашей модели. Я лично, как держать вещи типа безопасным и в состоянии быть блок-тестирования, поэтому, когда мне нужно манипулировать TabControls в коде я обычно начинаю объявить перечисление с одним значением для каждой вкладки:
public enum MyTabs : int
{
[Description("Tab 1")]
Tab1,
[Description("Tab 2")]
Tab2,
[Description("Tab 3")]
Tab3
}
Атрибут Описание является текст, который я хочу отобразить в заголовке вкладки, подробнее об этом в одно мгновение. На моем взгляде модель содержит член MyTabs типа, который обновляется каждый раз, когда пользователь нажимает на вкладку, и которую я могу также установить вручную сам с помощью кода:
public class MyViewModel : ViewModelBase
{
private MyTabs _CurrentTab;
public MyTabs CurrentTab
{
get { return this._CurrentTab;}
set { this._CurrentTab = value; RaisePropertyChanged(() => this.CurrentTab); }
}
}
Теперь вам нужно связать свой TabControl этого свойства:
<TabControl
ItemsSource="{Binding Source={StaticResource MyTabs}}"
SelectedIndex="{Binding CurrentTab, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Path=., Converter={StaticResource EnumDescriptionConverter}}" />
</Style>
</TabControl.Resources>
</TabControl>
к сожалению WPF привязка не достаточно умна, чтобы работать с целыми перечислениями, поэтому я также использую конвертер, чтобы бросить между перечислениями и целыми числами:
public class EnumToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (int)value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Enum.ToObject(targetType, value);
}
}
есть несколько других вещей, происходящих здесь ... прежде всего вы заметите, что я вообще не объявляю TabItems. Это потому, что я генерирую их автоматически из значений Enum; если я добавлю новое значение в перечисление MyTab, тогда появится волшебная вкладка! В этом случае я привязка к статическому ресурсу с ключом «MyTabs», это ObjectDataProvider, которая перечисляет значения в моем перечислении:
<ObjectDataProvider x:Key="MyTabs" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:MyTabs"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
Это поднимает вопрос о том, как язычки знают, что отображать в их заголовков и областей содержимого tabitem. В заголовках используется атрибут «Описание», объявленный в перечислении, код для EnumDescriptionConverter находится на another page on this site. Чтобы указать контент для каждой страницы, я создаю ControlTemplate для каждого из моих значений перечисления и нажимаю его на значение enum. Шаблон данные затем используется для выбора подходящей для использования для каждой вкладки:
<Window.Resources>
<ControlTemplate x:Key="{x:Static local:MyTabs.Tab1}">
<TextBlock Text="This is the first tab" />
</ControlTemplate>
<ControlTemplate x:Key="{x:Static local:MyTabs.Tab2}">
<TextBlock Text="This is the second tab" />
</ControlTemplate>
<ControlTemplate x:Key="{x:Static local:MyTabs.Tab3}">
<TextBlock Text="This is the third tab" />
</ControlTemplate>
<DataTemplate DataType="{x:Type local:MyTabs}">
<ContentControl>
<ContentControl.Template>
<MultiBinding Converter="{StaticResource ResourceKey=BindingToResourceConverter}">
<Binding RelativeSource="{RelativeSource AncestorType={x:Type Window}}" Path="Resources" />
<Binding />
</MultiBinding>
</ContentControl.Template>
</ContentControl>
</DataTemplate>
</Window.Resources>
Заключительная частью головоломки является BindingToResourceConverter, который просто принимает привязку (то есть одно из значений перечислений) и использует его найдите соответствующий контрольный образец:
public class BindingToResourceConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (values[0] as ResourceDictionary)[values[1]];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
И все! Каждый раз, когда я хочу добавить новую страницу в TabControl, я просто добавляю значение в соответствующее перечисление и создаю ключ ContentControl к этому значению. Все остальное происходит автоматически, и лучше всего это как безопасно, так и выборочно.
Не было совершено преступления, но я не понимаю вашу реализацию здесь.Это действительно бессмысленно. Вы просто закодировали 3 вкладки, но перенесли часть «жесткого кодирования» представления (XAML) на виртуальную машину. Вы уже привязаны к коллекции, было бы намного проще сделать заголовок собственностью коллекции. Вы также создаете необходимость в наборе ненужных шаблонов, вы запрещаете редактирование пользовательского интерфейса в порядке вкладок/вкладок с момента его жесткого кодирования, и вы также можете разбить локализацию XAML, если вам это нужно. И, конечно же, вы не можете добавлять/удалять вкладки во время выполнения с этим. – SledgeHammer
Без обид! :) Да, перемещение XAML в виртуальную машину на самом деле является целым пунктом упражнения, поскольку оно облегчает модульное тестирование, например. действительно ли нажатие на кнопку вызывает активацию соответствующей вкладки? В этом конкретном примере перечислялось перечисление для создания вкладок, но я использую шаблоны данных, поэтому вам нечего останавливать на использовании ObservableCollection, содержащих эти перечисления (в штучной упаковке) и других типов данных, что также решает проблему добавления и удаления вкладок. Полностью согласен с вашей локализацией, на самом деле я не использую атрибут Description, но я хотел, чтобы все было просто. :) –