从 MVVM 中的 UserControl 内部导航 ContentControl

Navigate ContentControl from inside a UserControl in MVVM

我试图在不使用第三方框架的情况下创建一种 ShellView,我正在尝试通过它来实现。 ContentControls 和 UserControls。

只要从 UserControl 外部触发命令,我就可以 navigate/switch ContentControl 中显示的 UserControl,但是当我将代码移到 UserControl 中的按钮内时,什么也没有发生。

目前我有一个 MainWindow.XAML,我有一个 ContentControl。

此 ContentControl 托管 LoginWindowUserControl 或 UserWindowUserControl - 我希望能够从其中一个 UserControl 切换 UserControl。

我的 MainWindow.XAML 看起来像这样:

<Window x:Class="ModelHealthApplication.MainWindow"
    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:vms="clr-namespace:ModelHealthApplication.ViewModels"
    xmlns:ia="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:views="clr-namespace:ModelHealthApplication.Views.UserControls"
    xmlns:local="clr-namespace:ModelHealthApplication"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Window.Resources>
    <DataTemplate DataType="{x:Type vms:CurrentWindowUserStateViewModel}">
        <views:UserView />
    </DataTemplate>
    <DataTemplate DataType="{x:Type vms:LoginWindowViewModel}">
        <views:LoginView />
    </DataTemplate>
    <vms:NavigationViewModel x:Key="nVm" />
</Window.Resources>
<ia:Interaction.Triggers>
    <ia:EventTrigger EventName="Loaded">
        <ia:InvokeCommandAction Command="{Binding Source={StaticResource nVm}, Path=OpenLoginWindowCommand}" />
    </ia:EventTrigger>
</ia:Interaction.Triggers>
<Grid DataContext="{StaticResource nVm}">
    <DockPanel>
        <Button Content="Test" DockPanel.Dock="Left" Command="{Binding OpenUserWindowStateCommand}" />
        <ContentControl x:Name="WindowUserState" Content="{Binding CurrentWindowUserState}" />
    </DockPanel>
</Grid>

我的 UserWindowUserControl 如下所示:

<UserControl x:Class="ModelHealthApplication.Views.UserControls.UserView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:vms="clr-namespace:ModelHealthApplication.ViewModels"
             xmlns:views="clr-namespace:ModelHealthApplication.Views.UserControls"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ModelHealthApplication.Views.UserControls"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <DataTemplate DataType="{x:Type vms:MyModelsViewModel}">
            <views:MyModelsView/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type vms:MyAccountViewModel}">
            <views:MyAccountView />
        </DataTemplate>
        <vms:NavigationViewModel x:Key="nVm" />
    </UserControl.Resources>
    <Grid>
        <DockPanel DataContext="{StaticResource nVm}">
            <Grid DockPanel.Dock="Top" Background="{StaticResource MainBlue}" Height="25">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <StackPanel Orientation="Horizontal" Grid.Column="0">
                    <Button Content="My Models" 
                        HorizontalAlignment="Center" 
                        Command="{Binding OpenMyModelsCommand}"
                        Style="{StaticResource NavButtonStyle}"/>

                    <Button Content="My Account" 
                        HorizontalAlignment="Center" 
                        Command="{Binding OpenMyAccountCommand}"
                        Style="{StaticResource NavButtonStyle}"
                        />
                </StackPanel>

                <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Right" Grid.Column="1" Margin="0, 0, 10, 0">
                    <TextBlock  VerticalAlignment="Center" Foreground="White">
                    <Run Text="Logged in as:" FontWeight="Bold"/>
                    <Run Text="{Binding LoggedInAs}" d:Text="TestUser" />
                    </TextBlock>
                    <TextBlock Margin="20, 0 ,0 ,0" Text="Log Out"  VerticalAlignment="Center" TextDecorations="Underline" Foreground="{StaticResource ComplenetarySecondOrange}" Background="{DynamicResource MainBlue}" Cursor="Hand">
                        <TextBlock.InputBindings>
                            <MouseBinding Command="{Binding OpenLoginWindowCommand}" MouseAction="LeftClick" />
                        </TextBlock.InputBindings>
                    </TextBlock>
                </StackPanel>
            </Grid>
            <ContentControl x:Name="Pages" Content="{Binding SelectedViewModel}" />
        </DockPanel>
    </Grid>
</UserControl>

当我按下“注销”时 button/textblock 我想导航回登录 window,但我只能通过 UserControl 之外的按钮执行此操作。

我已经阅读了其他几个与此类似的 post,但我还没有找到适合我需要的解决方案 - 我尝试使用 RelativeSource 但没有成功 - 也许有人可以看到我在做什么错了。

我正在使用“NavigationViewModel”来保存我提到的命令,这些命令在 UserControls 之外工作正常:

using ModelHealthApplication.Commands;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace ModelHealthApplication.ViewModels
{
    public class NavigationViewModel : INotifyPropertyChanged
    {
        public ICommand OpenMyModelsCommand { get; set; }
        public ICommand OpenMyAccountCommand { get; set; }
        public ICommand OpenUserWindowStateCommand { get; set; }
        public ICommand OpenLoginWindowCommand { get; set; }

        private object currentWindowUserState;
        public object CurrentWindowUserState
        {
            get { return currentWindowUserState; }
            set 
            { 
                currentWindowUserState = value;
                OnPropertyChanged("CurrentWindowUserState");
            }
        }

        private object selectedViewModel;
        public object SelectedViewModel
        {
            get { return selectedViewModel; }
            set 
            { 
                selectedViewModel = value;
                OnPropertyChanged("SelectedViewModel");
            }
        }

        public NavigationViewModel()
        {
            OpenMyModelsCommand = new OpenMyModelsCommand(this);
            OpenMyAccountCommand = new OpenAccountCommand(this);
            OpenUserWindowStateCommand = new OpenUserWindowStateCommand(this);
            OpenLoginWindowCommand = new OpenLoginWindowCommand(this);
        }

        public void OpenUserWindowState(object obj)
        {
            CurrentWindowUserState = new CurrentWindowUserStateViewModel();
        }

        public void OpenLoginWindow(object obj)
        {
            CurrentWindowUserState = new LoginWindowViewModel();
        }

        public void OpenMyModels(object obj)
        {
            SelectedViewModel = new MyModelsViewModel();
        }

        public void OpenMyAccount(object obj)
        {
            SelectedViewModel = new MyAccountViewModel();
        }


        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propName)
        {
            if (PropertyChanged != null)

            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }
    }
}

最终通过 Youtube 上 SingletonSean 的精彩教程解决了这个问题。

如果有人遇到此问题并遇到同样的问题,请在此处发布。

Singleton Sean MVVM Navigatoin