在WPF MVVM应用程序中的选项卡之间正确导航

时间:2013-05-16 15:49:37

标签: c# wpf xaml mvvm tabs

我正在使用M-V-VM模式进行WPF应用程序(如果相关,我正在使用galasoft),但是当我浏览tabcontrol时遇到问题。
我在运行中添加标签。所有绑定似乎都很顺利:在选项卡内或选项卡的标题中。 我将tabcontrol绑定到一个可观察列表。通过一个接口我将几种类型的viewmodel添加到这个列表中,绑定似乎是正确的 我的XAML代码如下所示:

<Grid>
    <Grid.Resources>
        <DataTemplate x:Key="itemTemplate">
            <TechnicalControls:ItemTab />
        </DataTemplate>
    </Grid.Resources>

    <TabControl Grid.Row="1" x:Name="MainTab" Grid.Column="1" 
            ItemsSource="{Binding TabViewModels}"
            SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}"

            ItemContainerStyleSelector="{StaticResource LastItemStyleSelector}"
            ItemTemplate="{StaticResource itemTemplate}"
            >

        <TabControl.Resources>
            <DataTemplate DataType="{x:Type VM:JobViewModel}" x:Shared="False" >
                <FunctionnalControls:Job />
            </DataTemplate>
            <DataTemplate DataType="{x:Type VM:ExcelJobViewModel}" x:Shared="False">
                <FunctionnalControls:ExcelJob />
            </DataTemplate>
            <DataTemplate DataType="{x:Type VM:MonitoringViewModel}" x:Shared="False">
                <FunctionnalControls:Monitoring />
            </DataTemplate>
            <DataTemplate DataType="{x:Type VM:ErrorViewModel}" x:Shared="False">
                <FunctionnalControls:Error />
            </DataTemplate>                

        </TabControl.Resources>
    </TabControl>
</Grid>

例如,如果我从ExcelJob转到另一个ExcelJob用户控件,则新的usercontrol未正确加载但它会更改然后它可以正常工作,例如,如果我只是通过监视,我可以转到ExcelJob到另一个ExcelJob。

我已经看过这个this,但它对我不起作用 我也看过this:它说我们不应该使用输入,因为你可以集中注意力。我试图将用户控件上的IsEnabled属性设置为false。当标签发生变化时我就做到了。它不起作用......

我能看到的唯一解决方案是通过另一个新的用户控件,没有其他目的,每次更改选项卡时都会使用,但这很难看,而且我很确定,微软想到了这个并且出现了有更好的解决方案。

如果有必要,我可以放置视图模型的代码。

编辑:只是为了澄清,当我点击具有相同控件的其他选项卡时,它不会向我显示新的用户控件,而是向我显示上一个。为了看到新的一个,我必须换到另一个选项卡与另一个用户控件然后再回到我想看的那个。
我查看调试,当我点击其他选项卡时,它不会调用viewmodel

<UserControl x:Class="App.ExcelJob"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             DataContext="{Binding Main.ExcelJobVM, Source={StaticResource Locator }}">
    <Grid >
        <Label>Futur Excel Job</Label>
        <TextBox Width="200" Height="60" Text="{Binding Header}"/>
    </Grid>
</UserControl>

因此Main返回Mainviewmodel,Main.ExcelJobVM返回usercontrol的良好viewmodel。返回的isntance基于选定的索引 我唯一需要的是通过加载好的viewmodel来强制重新绘制usercontrol或调用方法来更新datacontext。我试过,到目前为止我都失败了。我不确定我在做什么,因为我想使用tabcontrol的事件SelectionChanged,但它会在后面的代码中,我不知道它是否仍然会尊重MVVM模式。

1 个答案:

答案 0 :(得分:3)

问题是您的DataContext中有UserControl硬编码,而您的UserControl是模板。

当您切换到使用相同Template的标签页时,WPF无需重新绘制模板,只会更改模板后面的DataContext。但在您的情况下,DataContext中有UserControl硬编码,因此它不使用TabItem中的现有数据上下文。

最佳解决方案是从UserControl中删除DataContext绑定,并在所选项目发生更改时从TabItem继承。

例如:

WPF说

  

用户已选择ExcelJobA进行显示。由于DataTemplate,让我使用ExcelJob UserControl

绘制它
<TabItem>
    <ContentPresenter DataContext="ExcelJobA">
        <local:ExcelJob DataContext="{Binding Main.ExcelJobVM, Source={StaticResource Locator }}" />
    </ContentPresenter>
</TabItem>

因此创建了ExcelJob UserControl,默认情况下,UserControl将继承ExcelJobA的DataContext。

当用户将所选标签更改为ExcelJobB时,WPF会

  

嘿,用户已更改为ExcelJobB。由于DataTemplate,让我使用ExcelJob UserControl绘制它,但是等待!我已经显示了ExcelJob UserControl,所以我只需将其背后的DataContext更改为ExcelJobB

<TabItem>
    <ContentPresenter DataContext="ExcelJobB">
        <local:ExcelJob DataContext="{Binding Main.ExcelJobVM, Source={StaticResource Locator }}" />
    </ContentPresenter>
</TabItem>

因此,实际显示的ExcelJob UserControl不会被重新创建或重新绘制,但只有后面的DataContext更改。

HOWEVER ,因为您已对DataContext内的UserControl进行了硬编码,所以从所选项目中获取的实际数据上下文不会被使用,因为从DataContext内部指定的<Tag>始终优先于将从可视树中继承的DataContext

您需要从DataContext内删除UserControl绑定,并让它从TabControl的SelectedItem正常传递,并且它可以正常工作。

<TabItem>
    <ContentPresenter DataContext="ExcelJobA">
        <local:ExcelJob /> <!-- DataContext inherited from the ContentPresenter -->
    </ContentPresenter> 
</TabItem>