Change view in wpf single page application depending upon child view model

瘦欲@ 提交于 2021-02-08 10:30:54

问题


I write an application in wpf and using mvvm pattern. I try to implement a single page application using a ContentControl and DataTemplate. In my application I want to switch a view depending on the child view model like this: I have a mainwindow with a content control that its content is binding to a property in MainViewModel:

public BaseViewModel CurrentViewModel { get; set; }

in the consturctor I wrote:

CurrentViewModel = new LoginViewModel();

in LoginViewModel I have a function that gets name and password and check if the details are correct. if it's ok, I want to set CurrentViewModel in MainViewModel to NavigationViewModel.

but when I look for examples I find only switch the viewmodel directly in the main view model. how can I do it???


回答1:


If I understand your question correctly, you're asking how to change CurrentViewModel from within LoginViewModel after login completes?

You shouldn't be doing this. LoginViewModel should worry about logging in, and should not know anything about where it's being used, or any other part of the app.

MainViewModel is the one that owns the child VM and orchestrates the flow of the application, so it is MainViewModel that should be doing the switching.

Since you want to switch view after login completes OK, you need LoginViewModel to tell you that login has completed ok. The two obvious ways of doing that are:

  1. LoginViewModel exposes a LoginComplete event, which MainViewModel subscribes to, or
  2. MainViewModel supplies a LoginComplete Action to the LoginViewModel constructor, which LoginViewModel calls when login is complete.

.

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:


Using MvvmLight framework you can build such monster.

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=NavigationViewModel}"

<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:


It is appropriate to use DataTemplates if you want to dynamically switch Views depending on the ViewModel:

<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>

If Window.DataContext is an instance of ViewModelA, then ViewA will be displayed and Window.DataContext is an instance of ViewModelB, then ViewB will be displayed.

The best example I've ever seen and read it is made by Rachel Lim. See the example.



来源:https://stackoverflow.com/questions/34307404/change-view-in-wpf-single-page-application-depending-upon-child-view-model

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!