wpf如何从嵌套视图切换父选项卡

时间:2015-08-13 21:53:28

标签: c# wpf

我有一个像这样的tabcontrol:

<TabControl>
    <local:TabItem x:Name="son" Header="son">
        <local_son:SonView />
    </local:TabItem>
    <local:TabItem x:Name="daughter" Header="daughter">
        <local_daughter:DaughterView />
    </local:TabItem>
</TabControl>

在DaughterView中有一个按钮,我想点击此按钮切换到 儿子选项卡。我的问题是如何在DaughterView中找到儿子选项卡的tabindex?

提前谢谢!

2 个答案:

答案 0 :(得分:0)

在选项卡内部有一个按钮切换到另一个选项卡似乎有点奇怪。我认为这将是一个工具栏按钮或类似的东西。即选项卡外面。但是如果你坚持:))我会使用信使/事件聚合器模式并发布一个事件并让视图订阅并切换选项卡。我不会让孩子看到自己这样做。

答案 1 :(得分:0)

您需要将“SelectedIndex”绑定到视图模型中的属性。我个人喜欢保持类型安全且能够进行单元测试,因此当我需要在代码中操作TabControl时,我通常首先为每个选项卡声明一个带有一个值的枚举:

public enum MyTabs : int
{
    [Description("Tab 1")]
    Tab1,

    [Description("Tab 2")]
    Tab2,

    [Description("Tab 3")]
    Tab3
}

description属性是我希望在标题页中显示的文本,稍后会详细介绍。我的视图模型包含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内容区域中显示的内容。标头使用枚举中声明的“Description”属性,EnumDescriptionConverter的代码位于another page on this site。要为每个页面指定内容,我为每个枚举值创建一个ControlTemplate,并将其键入枚举值本身。然后使用数据模板选择适用于每个选项卡的数据模板:

<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,它只需要一个绑定(即其中一个枚举值)并使用它来查找相应的ControlTemplate:

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键。其他一切都是自动发生的,最重要的是它既是类型安全的又是可单元测试的。