根据状态在视图之间切换

时间:2012-01-06 10:52:10

标签: wpf mvvm

说我有一个显示用户好友列表的应用程序。好友列表以TabItem显示。用户必须先登录服务器才能获取好友列表。

我创建了两个用户控件,一个用于用户登录时,另一个用于未登录时。这条线上的东西:

UnloggedView.xaml

<UserControl x:Class="UnloggedView" ...>
    <TextBlock ...>You need to <Hyperlink Command="{Binding LoginCmd}">
        Login</Hyperlink>too see your friends list</TextBlock>
</UserControl>

LoggedView.xaml:

<UserControl x:Class="LoggedView" ...>
    ...
    <ListView ItemSource={Binding Path=friends}">...
</UserControl>

主窗口包含以下代码:

....
<TabItem Header="Friends">
    <vw:UnloggedView />
</TabItem>

我相信一切都符合MVVM的原则。 LoginCmd是在ViewModel中实现的DelegateCommand(来自prism)的简化变体。两个视图都正常工作,并且在填充(异步)列表时,会触发通知并更新视图。我很高兴。

所以我有两个问题:第一个问题是我如何激活LoginWindow(提示用户输入他的凭据?现在,我只是创建LoginWindow(一个视图对象)并将其与ShowDialog一起呈现。看起来我通过直接操作ViewModel中的UI来打破MVVM的规则。

主要问题是我登录服务器后,用LoggedView替换TabItem内容的正确方法是什么。根据MVVM主体,ViewModel不应该对View的内部有所了解。我在ViewModel中暴露了IsLogged属性(它将触发PropertyChanged通知)但是我应该绑定什么才能使一切发生?我真的不希望ViewModel操纵View。

由于

3 个答案:

答案 0 :(得分:9)

我看到这个问题出现了很多,并写了一些关于switching between Views/UserControls here的内容。通常我使用ContentControl并根据ContentTemplate切换DataTrigger,但是相同的主体可以切换TabControl的ItemTemplate

<DataTemplate x:Key="LoggedOutTemplate">
     <local:UnloggedView />
</DataTemplate> 

<DataTemplate x:Key="LoggedInTemplate">
     <local:LoggedView />
 </DataTemplate>

 <TabControl>
     <TabControl.Style>
         <Style TargetType="{x:Type TabControl}">
             <Setter Property="ItemTemplate" Value="{StaticResource LoggedOutTemplate}" />
             <Style.Triggers>
                 <DataTrigger Binding="{Binding IsLoggedIn}" Value="True">
                     <Setter Property="ItemTemplate" Value="{StaticResource LoggedInTemplate}" />
                 </DataTrigger>
             </Style.Triggers>
         </Style>
     </TabControl.Style>
 </TabControl>

您可能必须在DataTrigger绑定中使用ElementNameRelativeSource来查找IsLoggedIn

中的DataContext属性

对于从Logged Out视图触发Login Command,有多种方法可以执行此操作。

我首选的方法是使用某种消息传递系统,例如MVVM Light的Messenger或Microsoft Prism的EventAggregator,并在单击按钮时触发某种ShowLoginDialog消息,然后让ViewModel负责显示登录对话框订阅接收这些消息并处理它们。

另一种方法是使用RelativeSource绑定来查找Visual Tree中的对象,该对象的DataContext中包含LoginCommand,并绑定到该对象。

您可以看到here

的示例

答案 1 :(得分:1)

首先我会回答你第二个问题......只需创建一个Enum作为SessonState

 enum SesionState
 {
      LoggedOut=0,
      LoggedIn
 }

之后,在ViewModel中为名为SessionState的窗口创建一个属性,并在Login和LogOut时使用所需值更新该属性。

切换视图所需的Xaml

<Window>
        <Window.Resources>        
    <DataTemplate x:Key="LoggedOutView">
        <ViewLayer: LoggedOutView/>
    </DataTemplate>
    <DataTemplate x:Key="LoggedInView">
        <ViewLayer:LoggedInView/>
    </DataTemplate>
    <Style x:Key="mainContentControlStyle"
           TargetType="{x:Type ContentControl}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=SessionState}"
                         Value="0">
                <Setter Property="ContentTemplate" Value="{StaticResource ResourceKey=LoggedOutView}" />
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=Mode}"
                         Value="1">
                <Setter Property="ContentTemplate" Value="{StaticResource ResourceKey=LoggedInView}" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>
    <Grid>
        <TabControl>
           <TabItem>
                        <ContentControl Grid.Row="0"
                    Content="{Binding}"
                    Style="{StaticResource ResourceKey=mainContentControlStyle}">  
           </TabItem>
        </TabControl>
    </Grid>
</Window>

答案 2 :(得分:0)

第一个问题:您只需使用viewmodel中的ILogindialogservice即可。我使用following对话框和mvvm。它的“单元测试”能够并且不会破坏mvvm。

EDIT。在你的viewmodel中,你会有一个这样的行:

var result = this.loginservice.ShowDialog("Login", loginvm);