【问题标题】:How to change main window view from a pop up window如何从弹出窗口更改主窗口视图
【发布时间】:2016-07-13 01:48:34
【问题描述】:

我正在尝试学习 MVVM,但我发现它是一场噩梦,试图了解如何使用 MVVM 在应用程序中的视图之间正确导航。经过一段时间的研究和尝试了解不同的技术,我遇到了来自Rachel Lim's blog 的方法。该技术为应用程序本身使用 ViewModel 并跟踪应用程序状态,例如当前页面。我觉得这对于我的应用程序来说是一个很好的方法。

现在开始解决我的问题..

我想要达到的目标

我想要一个应用程序,它有一个主应用程序视图,它将一个 LoginView 和一个 HomeView 存储为 DataTemplates,并有一个内容控件,它将 LoginView 设置为应用程序启动时显示的视图。 LoginView 将有一个按钮,按下该按钮将打开另一个具有按钮的窗口。当按下弹出窗口中的按钮时,我想将主应用程序窗口中的视图从 LoginView 更改为 HomeView。

我目前所拥有的

我设置了可以正常工作的 ApplicationView。

<Window x:Class="WPF_Navigation_Practice.Views.ApplicationView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:ignore="http://www.galasoft.ch/ignore"
        xmlns:vm="clr-namespace:WPF_Navigation_Practice.ViewModels"
        xmlns:views="clr-namespace:WPF_Navigation_Practice.Views"
        mc:Ignorable="d ignore"
        DataContext="{StaticResource ApplicationViewModel}">

    <Window.Resources>
        <DataTemplate DataType="{x:Type vm:LoginViewModel}">
            <views:LoginView />
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:HomeViewModel}">
            <views:HomeView />
        </DataTemplate>
    </Window.Resources>

    <Grid>
        <ContentControl Content="{Binding CurrentPageViewModel}" />
    </Grid>
</Window>

并已按如下方式设置 ApplicationViewModel。将当前页面设置为 LoginViewModel。

using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using WPF_Navigation_Practice.Interfaces;

namespace WPF_Navigation_Practice.ViewModels
{
    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class ApplicationViewModel : ViewModelBase
    {
        #region Fields

        private ICommand _changePageCommand;

        private IPageViewModel _currentPageViewModel;
        private List<IPageViewModel> _pageViewModels;

        #endregion

        public ApplicationViewModel()
        {
            // Add available pages
            PageViewModels.Add(new LoginViewModel());
            PageViewModels.Add(new HomeViewModel());
            PageViewModels.Add(new CodeViewModel());


            // Set starting page
            CurrentPageViewModel = PageViewModels[0];
        }

        #region Properties / Commands

        public ICommand ChangePageCommand
        {
            get
            {
                if (_changePageCommand == null)
                {
                    _changePageCommand = new RelayCommand<object>(
                        p => ChangeViewModel((IPageViewModel)p),
                        p => p is IPageViewModel);
                }

                return _changePageCommand;
            }
        }

        public List<IPageViewModel> PageViewModels
        {
            get
            {
                if (_pageViewModels == null)
                    _pageViewModels = new List<IPageViewModel>();

                return _pageViewModels;
            }
        }

        public IPageViewModel CurrentPageViewModel
        {
            get
            {
                return _currentPageViewModel;
            }
            set
            {
                if (_currentPageViewModel != value)
                {
                    _currentPageViewModel = value;
                    RaisePropertyChanged("CurrentPageViewModel");
                }
            }
        }


        #endregion

        #region Methods

        private void ChangeViewModel(IPageViewModel viewModel)
        {
            if (!PageViewModels.Contains(viewModel))
                PageViewModels.Add(viewModel);

            CurrentPageViewModel = PageViewModels
                .FirstOrDefault(vm => vm == viewModel);
        }

        #endregion
    }
}

当我运行应用程序时,它将显示我的主应用程序窗口,该窗口显示 loginView,它是一个 UserControl,并设置为带有 ContentPresenter 的 currentPageViewModel。

当点击 LoginView UserControl 中的按钮时,它将打开另一个窗口。根据下图。

这是该窗口的 XAML。

<Window x:Class="WPF_Navigation_Practice.Views.CodeView"
    x:Name="CodeWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:ignore="http://www.galasoft.ch/ignore"
    xmlns:z="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:viewModels="clr-namespace:WPF_Navigation_Practice.ViewModels"
    mc:Ignorable="d ignore"
    d:DesignWidth="623.224" d:DesignHeight="381.269"
    DataContext="{Binding CodeViewModel, Source={StaticResource ApplicationViewModel}}">

<Grid>
    <Button Content="Ok" 
            HorizontalAlignment="Left" 
            Margin="235,166,0,0" 
            VerticalAlignment="Top"
            Width="138" 
            FontSize="20" 
            Height="67"/>
    <Label Content="Second Window" HorizontalAlignment="Left" Margin="166,56,0,0" VerticalAlignment="Top" FontSize="36"/>
</Grid>

我的问题

我想要实现的是,当单击 secondView 窗口中的“确定”按钮时,我想将 ApplicationView 窗口中的 currentPageViewModel 从 LoginView 更改为显示 HomeView,但我对如何实现这一点感到困惑.任何帮助将不胜感激。

【问题讨论】:

  • 我可能会给登录视图一个主视图订阅的 LoggedIn 事件。然后,当它出现时,主视图可以告诉应用程序视图模型有人登录,方法是在其上设置一个属性(it == app vm),调用一个方法,等等。应该有一个视图模型来统治他们所有人,谁来决定用户登录时要做什么——我假设那是应用程序视图模型。
  • MVVM 确实不适合拥有多个窗口(包括对话框等)。如果可能,使用单个窗口,并插入不同的用户控件(每个都被视为一个视图)。
  • 将主窗口作为参数传递给弹出窗口的构造函数并使用主窗口的属性从那里设置视图的任何问题。

标签: c# wpf xaml mvvm


【解决方案1】:

我看到您已经在使用 MVVMLight。这里有一个 Messenger 课程可以为您提供帮助。在 ApplicationViewModel 构造函数中注册到 messenger,并在处理 CodeViewModel 中的按钮单击的代码中使用 Send 发送消息。在您传递注册的操作中,根据需要更改视图模型。

http://www.mvvmlight.net/help/WP8/html/9fb9c53a-943a-11d7-9517-c550440c3664.htmUse MVVM Light's Messenger to Pass Values Between View Model

我没有 MVVMLight 为您编写示例代码。我从头开始编写了一个 ViewModelMessenger,我的是这样的:

public static void Register(string actionName, object registerer, Action<object, object> action)
{
    var actionKey = new Tuple<string, object>(actionName, registerer);
    if (!RegisteredActions.ContainsKey(actionKey))
    {
        RegisteredActions.Add(actionKey, action);
    }
    else
    {
        RegisteredActions[actionKey] = action;
    }
}

像这样使用:

VMMessenger.Register("ChangeViewModel",this,ChangeViewModelAction)

public static void SendMessage(string messageName, object message, object sender)
{
    var actionKeys = RegisteredActions.Keys.ToList();
    foreach (Tuple<string, object> actionKey in actionKeys)
    {
        if (actionKey.Item1 == messageName)
        {
            Action<object, object> action;
            if (RegisteredActions.TryGetValue(actionKey, out action))
            {
                action?.Invoke(message, sender);
            }
        }
    }
}

像这样使用:

VMMessenger.SendMessage("ChangeViewModel","HomeViewModel",this);

ChangeViewModelAction 中,您可以检查 ViewModel 名称并将 CurrentPageViewModel 更改为具有匹配名称的名称。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-02
    • 1970-01-01
    • 1970-01-01
    • 2016-05-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多