【问题标题】:Lifetime for the view / viewmodel when using data templates使用数据模板时视图/视图模型的生命周期
【发布时间】:2012-06-21 11:48:38
【问题描述】:

我正在定义一个策略,其中主视图将使用数据模板在视图之间切换。目前它可以在 3 个视图之间切换:

  • ApplicationView:它实际上是由许多 不同的视图,主要使用选项卡/停靠进行分层。这是一个 处理应用程序数据的视图。
  • Lo​​gInView:用于登录用户。
  • DialogView:用于显示对话框视图。此视图还将使用数据模板来选择所需的适当视图。

这个想法是当需要显示一个对话框视图时,它被设置为主视图上的当前视图。选择完成后,此信息将传递给 ApplicationView,或作为 ApplicationView 一部分的视图。在显示 DialogView 时,不能从内存中释放 ApplicationView,因为 ApplicationViewModel 仍将处理数据(它需要在后台持续工作)。

我正在考虑使用 DataTemplates 来实现这一点,并将 ContentControl 的内容绑定到 CurrentView:

// in MainView
DataTemplate DataType="{x:Type vm:ApplicationViewModel}">
    <vw:ApplicationView />
</DataTemplate>

.....

// in MainViewModel
public ViewModelBase CurrentView { get; set; }

基本上,我试图避免将模式窗口用于对话框。

1) 这个策略可以吗,还是有一些我不知道的问题?

2) 当我切换到 DialogView(我实际上是在切换视图模型)时,ApplicationView/ApplicationViewModel 会发生什么情况?我是否需要将 ApplicationViewModel 的引用存储在某处,这样它就不会被垃圾收集?我没有对此进行测试,但可能在我设置 CurrentView 时会创建一个新的 ViewModel/View 实例。

3) 连接到第二个问题,当使用 DataTemplates 时,以前使用的 View/ViewModel 会发生什么,现在被不同的 view/viewmodel 替换了?

【问题讨论】:

    标签: wpf mvvm


    【解决方案1】:
    1. 我认为您切换视图的方式没有任何问题,尽管通常您不想在显示对话框时摆脱应用程序。

      我过去所做的是将CurrentViewDialogView 放在Grid 中,使它们彼此重叠,然后让ApplicationViewModel 包含@987654326 @ 和 IsDialogVisible 属性,当您想要显示对话框时,只需填充这两个字段。 (见下文示例)

    2. 如果您想返回 ApplicationViewModel 并避免创建新的 ApplicationViewModel,则必须将其存储在某处

    3. WPF 会处理不再可见的 UI 对象,因此将 CurrentViewLogin 切换到 Application 将摆脱 LoginView 并创建一个 ApplicationView

      ContentControl 的内容被设置为您的ViewModel,因此 ViewModel 实际上被放入应用程序VisualTree。每当 WPF 在其 VisualTree 中遇到一个它不知道如何绘制的对象时,它将使用包含该对象的 .ToString()TextBlock 来绘制它。通过定义DataTemplate,您是在告诉 WPF 如何绘制对象,而不是使用其默认的.ToString() 方法。一旦对象离开VisualTree,为渲染该对象而创建的任何视觉对象都将被销毁。

    虽然我会继续使用您当前的视图切换方法,但我不会将这种方法用于 LoginApplicationDialog 视图。

    通常LoginView 在登录时应该只显示一次,但如果您允许用户在登录后切换登录名,它可能会再次显示在Dialog 中。因此,我通常会显示LoginView在启动代码中,然后在登录成功后显示ApplicationView

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
    
        var login = new LoginDialog();
        var loginVm = new LoginViewModel();
    
        login.DataContext = loginVm;
        login.ShowDialog();
    
        if (!login.DialogResult.GetValueOrDefault())
        {
            Environment.Exit(0);
        }
    
        // Providing we have a successful login, startup application
        var app = new ApplicationView();
        var context = new ApplicationViewModel(loginVm.CurrentUser);
        app.DataContext = context;
        app.Show();
    }
    

    就像我之前说的,我不想在显示Dialog 时隐藏Application,所以我会将Dialog 作为Application 的一部分

    这是一个DataTemplate 的示例,我将使用我自己的ApplicationViewModel,将我自己的custom Popup from my blog 用于对话框

    <Grid x:Name="ApplicationView">
        <ContentControl Content="{Binding CurrentView}" />
        <local:PopupPanel x:Name="DialogPopup"
                          Content="{Binding DialogContent}"
                          local:PopupPanel.IsPopupVisible="{Binding IsDialogVisible}"
                          local:PopupPanel.PopupParent="{Binding ElementName=ApplicationView}" />
    </Grid>
    

    【讨论】:

    • 嗨,Rachel,应用程序本身应该“永不”关闭,因此应在每次换班时在应用程序运行期间完成登录。这就是为什么我想让 LoginView 成为应用程序的一部分。所以,如果我理解正确的话,ApplicationView 将在我切换 CurrentView 后立即被释放,无论我是否保留对 ApplicationViewModel 的引用以防止它被释放?但是如果我隐藏视图,视图不会被释放?
    • @Goran 我明白了,在这种情况下,我只需将Login 作为对话框弹出窗口之一,甚至可能是应用程序的CurrentViews 之一。如果ApplicationView 不可见,它将被卸载,或者通过将其Visibility 属性设置为不可见的东西,或者通过更改CurrentView 使其不存在。如果您有要保存的数据,则应将其存储在 ApplicationViewModel 中,因此下次显示 ApplicationView 时,它将加载回用户上次所在的位置。
    【解决方案2】:

    就个人而言,我会发现在标准网格中使用 ZOrdering 更容易,并将所有内容放在同一个视图中 - 使用 ViewModel 来管理可见性。

    例如

    <Grid>
        <Grid Visibility="{Binding IsView1Visible, 
            Converter={StaticResource BoolToVisibilityConverter}}">
            <!-- view 1 contents -->
        </Grid>
        <Grid Visibility="{Binding IsView2Visible, 
            Converter={StaticResource BoolToVisibilityConverter}}">
            <!-- view 2 contents -->
        </Grid>
        <Grid Visibility="{Binding IsView3Visible, 
            Converter={StaticResource BoolToVisibilityConverter}}">
            <!-- view 3 contents -->
        </Grid>
        <Grid Visibility="{Binding IsDialogVisible, 
            Converter={StaticResource BoolToVisibilityConverter}}">
            <!-- dialog contents contents -->
        </Grid>
    </Grid>
    

    【讨论】:

    • 这对于许多不同的视图来说变得非常复杂,而且由于您必须手动管理视图可见性,所以工作量要大得多。我发现使用DataTemplates 来定义每个ViewModel 的绘制方式,在ContentControl 中显示CurrentView 属性,然后将CurrentView 设置为当前的ViewModel 会更有效。我有一个例子on my blog如果你有兴趣
    猜你喜欢
    • 2014-01-30
    • 2011-02-20
    • 2012-07-22
    • 1970-01-01
    • 1970-01-01
    • 2021-01-09
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    相关资源
    最近更新 更多