【问题标题】:Change view in wpf single page application depending upon child view model根据子视图模型更改 wpf 单页应用程序中的视图
【发布时间】:2015-12-16 08:45:39
【问题描述】:

我在 wpf 中编写了一个应用程序并使用 mvvm 模式。 我尝试使用 ContentControl 和 DataTemplate 实现单页应用程序。 在我的应用程序中,我想根据子视图模型切换视图,如下所示: 我有一个带有内容控件的主窗口,它的内容绑定到 MainViewModel 中的一个属性:

public BaseViewModel CurrentViewModel { get; set; }

在我写的构造函数中:

CurrentViewModel = new LoginViewModel();

在 LoginViewModel 中,我有一个获取名称和密码并检查详细信息是否正确的函数。 如果可以的话,我想把MainViewModel中的CurrentViewModel设置为NavigationViewModel。

但是当我查找示例时,我发现只能直接在主视图模型中切换视图模型。 怎么办???

【问题讨论】:

  • 你在使用 mvvmlight 吗?
  • 我还没决定,你推荐我吗?可以帮我听听吗?
  • 我会发布示例,请稍等。
  • 使用两个用户控件。
  • 我没有写完整的代码

标签: c# wpf mvvm


【解决方案1】:

如果我正确理解您的问题,您是在询问登录完成后如何从LoginViewModel 中更改CurrentViewModel

你不应该这样做。 LoginViewModel 应该担心登录,并且不应该知道它的使用位置或应用程序的任何其他部分。

MainViewModel 是拥有子 VM 并协调应用程序流的那个,所以应该是 MainViewModel 进行切换。

由于您想在登录成功后切换视图,因此您需要LoginViewModel 告诉您登录已成功。两种明显的方法是:

  1. LoginViewModel 公开 LoginComplete 事件,MainViewModel 订阅该事件,或
  2. MainViewModelLoginComplete Action 提供给 LoginViewModel 构造函数,当登录完成时 LoginViewModel 会调用该构造函数。

.

public class MainViewModel 
{
    //INPC omitted for brevity
    public object CurrentViewModel { get; private set; }

    public void MainViewModel()
    {
        this.CurrentViewModel = new LoginViewModel(LoginComplete);
    }

    private void LoginComplete()
    {
        this.CurrentViewModel = new NavigationViewModel();
    }
}

public class LoginViewModel 
{
    private Action loginCompleteAction;

    public void LoginViewModel(Action loginCompleteAction)
    {
        this.loginCompleteAction = loginCompleteAction;
    }

    private void UserHasLoggedIn()
    {
        this.loginCompleteAction();
    }
}

【讨论】:

    【解决方案2】:

    使用 MvvmLight 框架你可以构建这样的怪物。

    MainWindow.xaml

    DataContext="{Binding Source={StaticResource Locator}, Path=Main}"

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Button Content="BaseView" Height="25" Width="80" Command="{Binding SwitchToBaseCommand}"/>
            <Button Grid.Row="1" Content="NavigationView" Height="25" Width="100" Command="{Binding SwitchToNavigationCommand}"/>
        </Grid>
        <ContentControl Grid.Column="1" Content="{Binding CurrentViewModel}"/>
    </Grid>
    

    BaseView.xaml

    DataContext="{Binding Source={StaticResource Locator}, Path=BaseViewModel}"

    <Grid>
        <Label Content="{Binding Message}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
    

    NavigationView.xaml

    DataContext="{Binding Source={StaticResource Locator}, Path=NaviationViewModel}"

    <Grid>
        <Label Content="{Binding Message}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
    

    App.xaml

    xmlns:Views="clr-namespace:WpfApplication.Views" xmlns:ViewModels="clr-namespace:WpfApplication.ViewModel"

      <Application.Resources>
        <ResourceDictionary>
            <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:WpfApplication233.ViewModel" />
    
            <DataTemplate DataType="{x:Type ViewModels:BaseViewModel}">
                <Views:BaseView/>
            </DataTemplate>
    
            <DataTemplate DataType="{x:Type ViewModels:NavigationViewModel}">
                <Views:NavigationView/>
            </DataTemplate>
    
        </ResourceDictionary>
    </Application.Resources>
    

    ViewModelLocator.cs

    public class ViewModelLocator
    {
        /// <summary>
        /// Initializes a new instance of the ViewModelLocator class.
        /// </summary>
        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
            SimpleIoc.Default.Register<MainViewModel>();
            SimpleIoc.Default.Register<BaseViewModel>();
            SimpleIoc.Default.Register<NavigationViewModel>();
        }
    
        public MainViewModel Main
        {
            get
            {
                return ServiceLocator.Current.GetInstance<MainViewModel>();
            }
        }
    
        public BaseViewModel BaseViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<BaseViewModel>();
            }
        }
    
        public NavigationViewModel NavigationViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<NavigationViewModel>();
            }
        }
    
        public static void Cleanup()
        {
            // TODO Clear the ViewModels
        }
    }
    

    MainViewModel.cs

    public class MainViewModel : ViewModelBase
    {
        private ViewModelBase currentViewModel;
        public ViewModelBase CurrentViewModel
        {
            get
            {
                return this.currentViewModel;
            }
    
            set
            {
                this.currentViewModel = value;
                this.RaisePropertyChanged("CurrentViewModel");
            }
        }
        public RelayCommand SwitchToBaseCommand
        {
            get
            {
                return new RelayCommand(() => SwitchToBase());
            }
        }
    
        public RelayCommand SwitchToNavigationCommand
        {
            get
            {
                return new RelayCommand(() => SwitchToNavigation());
            }
        }
    
        public void SwitchToBase()
        {
            CurrentViewModel = ServiceLocator.Current.GetInstance<BaseViewModel>();
        }
    
        public void SwitchToNavigation()
        {
            CurrentViewModel = ServiceLocator.Current.GetInstance<NavigationViewModel>();
        }
    
    }
    

    BaseViewModel.cs

    public class BaseViewModel :ViewModelBase
    {
        public string Message {get; set;}
        public BaseViewModel()
        {
            Message = "Message from BaseViewModel View Model";
        }
    }
    

    NavigationViewModel.cs

        public class NavigationViewModel:ViewModelBase
    {
        public string Message { get; set; }
        public NavigationViewModel()
        {
            Message = "Message from Navigation View Model";
        }
    }
    

    【讨论】:

      【解决方案3】:

      如果要根据ViewModel动态切换Views,使用DataTemplates比较合适:

      <Window>
         <Window.Resources>
            <DataTemplate DataType="{x:Type ViewModelA}">
               <localControls:ViewAUserControl/>
            </DataTemplate>
            <DataTemplate DataType="{x:Type ViewModelB}">
               <localControls:ViewBUserControl/>
            </DataTemplate>
         <Window.Resources>
        <ContentPresenter Content="{Binding CurrentView}"/>
      </Window>
      

      如果Window.DataContextViewModelA 的实例,则将显示ViewA,而Window.DataContextViewModelB 的实例,则将显示ViewB

      我见过和读过的最好的例子是Rachel LimSee the example.

      【讨论】:

      • 我没有写完整的代码,我想知道如何更改当前视图,例如 ViewModelA
      猜你喜欢
      • 2019-02-20
      • 2017-05-04
      • 1970-01-01
      • 2013-12-25
      • 1970-01-01
      • 2020-07-30
      • 1970-01-01
      • 1970-01-01
      • 2021-01-17
      相关资源
      最近更新 更多