这里我有一个用MVVM结构制作的WPF应用程序。我对C#WPF相当陌生,对这个概念也不熟悉。我试图通过按下按钮,通过一个视图中的函数切换到另一个视图。
下面是应用程序的外观
按下登录按钮后,将触发一个功能,该功能将验证输入,如果有效,则切换到另一个视图。
文件结构
如何切换视图?下面是一些代码供参考。
- 主窗口. xaml**
<Window x:Class="QuizAppV2.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:local="clr-namespace:QuizAppV2"
xmlns:viewModel="clr-namespace:QuizAppV2.MVVM.ViewModel"
mc:Ignorable="d"
Height="600" Width="920"
WindowStartupLocation="CenterScreen"
WindowStyle="None"
ResizeMode="NoResize"
Background="Transparent"
AllowsTransparency="True">
<Window.DataContext>
<viewModel:MainViewModel/>
</Window.DataContext>
<Border Background="#272537"
CornerRadius="20">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="75"/>
<RowDefinition/>
<RowDefinition Height="25"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Online Quiz"
Grid.Column="1"
FontSize="20"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<StackPanel Grid.Column="2"
Margin="30,20"
Orientation="Horizontal"
HorizontalAlignment="Right"
VerticalAlignment="Top">
<Button Content="–"
Background="#00CA4E"
Style="{StaticResource UserControls}"
Click="Minimise"/>
<Button Content="▢"
Background="#FFBD44"
Style="{StaticResource UserControls}"
Click="Restore"/>
<Button Content="X"
Background="#FF605C"
Style="{StaticResource UserControls}"
Click="Exit"/>
</StackPanel>
</Grid>
<ContentControl Grid.Column="1"
Grid.Row="1"
Margin="20,10,20,50"
Content="{Binding CurrentView}"/>
</Grid>
</Border>
</Window>
- 主视图模型. cs**
using QuizAppV2.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QuizAppV2.MVVM.ViewModel
{
class MainViewModel : ObservableObject
{
public RelayCommand LoginViewCommand { get; set; }
public RelayCommand SubjectSelectionViewCommand { get; set; }
public RelayCommand QuizViewCommand { get; set; }
public RelayCommand ResultViewCommand { get; set; }
public LoginViewModel LoginVM { get; set; }
public SubjectSelectionViewModel SubjectSelectVM { get; set; }
public QuizViewModel QuizVM { get; set; }
public ResultViewModel ResultVM { get; set; }
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set
{
_currentView = value;
onPropertyChanged();
}
}
public MainViewModel()
{
LoginVM = new LoginViewModel();
SubjectSelectVM = new SubjectSelectionViewModel();
QuizVM = new QuizViewModel();
ResultVM = new ResultViewModel();
CurrentView = SubjectSelectVM;
LoginViewCommand = new RelayCommand(o =>
{
CurrentView = LoginVM;
});
SubjectSelectionViewCommand = new RelayCommand(o =>
{
CurrentView = SubjectSelectVM;
});
}
}
}
- 登录视图. xaml**
using QuizAppV2.MVVM.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace QuizAppV2.MVVM.View
{
/// <summary>
/// Interaction logic for LoginView.xaml
/// </summary>
public partial class LoginView : UserControl
{
public LoginView()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (UsrId.Text == "" || UsrName.Text == "")
{
UsrIDErrMsg.Visibility = Visibility.Visible;
UsrNameErrMsg.Visibility = Visibility.Visible;
}
else
{
UsrIDErrMsg.Visibility = Visibility.Hidden;
UsrNameErrMsg.Visibility = Visibility.Hidden;
MainWindow.currentUser = new Student(UsrId.Text, UsrName.Text);
}
}
}
}
谢谢
4条答案
按热度按时间r9f1avp51#
我建议使用“数据模板”。在主窗口中放置以下资源:
等等与其他... WPF正在为您做所有的工作,它检查“当前视图”属性,并选择如何查看它根据适当的数据模板。
wnvonmuf2#
导航是一个棘手的主题,有几种方法可以做到这一点,但由于您是WPF的新手,我尝试概述一个简单的技术,沿着您提供的示例的路线,要求是必须从一个页面转到另一个页面,一个简单的想法是交换内容。我的意思是,当用户单击**“登录”**时,我们验证用户并将LoginPage与其他一些页面交换。在您的情况下,一个测验页面,当用户选择任何选项,我们交换了视图与下一个视图等等。
我已经编写了一个简单的解决方案与 shell 机制。本质上,我们创建了一个空壳在主窗口(即它没有用户界面),我们加载页面到这个空壳使用导航服务/助手。我已经用了一个单例类在这里只是为了简单,有3个主要的方法,
寄存器 shell :这必须是将发生交换的窗口,理想情况下需要设置一次。
加载视图:用新视图替换旧视图的方法,我使用了用户控件,因为在WPF中大多数子视图都可以是用户控件。
加载带有自定义数据的视图:与上面类似,但具有更大的灵活性,因为它允许您提供额外的数据。
下面是登录页面的外观,其中重要的一行是**NavigationService.Instance.LoadView<_4OptionQuizPage>()**这实际上是将用户发送到_4OptionQuizPage。
在_4OptionQuizPage中,我们可以有这样的内容,这是大量业务逻辑可能驻留的地方,这里有4个按钮,其中2个显示消息框,但按钮1将您返回LoginPage,按钮2将使用不同的数据重新加载同一页面(即,将用户发送到下一个问题)
最后,您的MainWindow将注册shell并将用户发送到LoginPage,并且它的XAML文件中不应包含任何内容
MainWindow.xaml应该是空的,本质上是一个用于其他所有内容的shell。
bxfogqkk3#
这个例子演示了两种导航方法。通常很有用,因为你说要从登录开始,但在用户登录之前不显示任何菜单等。然后,一旦他们登录,你想要某种菜单或视图列表,他们可以导航到这保持静态。
我的主窗口纯粹是一个 shell 来包含一切。
其标记为:
此示例首先使用视图模型进行所有导航。视图模型被模板化到UI中。
后面的代码中还有更多内容。
messenger注册将使用dependency属性切换出窗口的datacontext。消息只是一个类,它有一个属性来传递Type
回调ParentViewModelChanged接受一个类型,示例化它并在窗口上设置datacontext。
通常,您对保留窗口或父级视图的状态不感兴趣。您已经登录。如果您想再次登录,则需要重新开始并输入用户名和密码。
入口点有点不寻常,因为我处理应用程序启动并依赖于依赖属性回调。
而不是一个主窗口充满菜单等,我当然什么也没有得到。
我有一个LoginUC是你在启动时看到的第一件事。这只是说明性的。
我们将从用户那里获取输入,并在真实的应用中导航之前进行验证。我们只是对这里的导航感兴趣,因此此版本只有一个导航到MainViewModel的按钮:
我的LoginViewModel有一个命令、标题和任务。
基础父视图模型
接口
这个接口的目的是给予我们一个通用的方法让任何视图模型获取它所需要的数据。通过设置datacontext,然后启动一个后台线程来获取数据,视图将快速出现,然后填充它所需要的任何数据。如果获取数据需要一段时间,那么至少视图是“向上”的,并且在任务继续工作的同时快速可见。
在一个更完整的例子中,我们会在一个基础视图模型中设置IsBusy,它会从true开始,然后被更改为false,这将在视图中驱动一个“throbber”或busing指示器。
资源字典使用以下数据类型将视图模型数据模板与用户控件相关联:
合并到app.xaml中
登录后,窗口的全部内容将被替换。datacontext从LoginViewModel更改为MainViewModel,然后模板化到MainUC:
当然,您可能希望拥有更多视图,并选择一个视图进行初始显示,该视图将在Initiate中设置。
主UC:
在视图中,你会在左列中看到一个按钮列表,它允许在右列中导航。当然,保留MainUC。
它可以是一个菜单或者一个选项卡控件,而不是一个列表框。
单击按钮调用MainViewModel中的命令,并将ChildViewModel的示例作为参数传递。
然后使用它示例化视图模型、设置CurrentChildViewmodel并缓存示例。
当然,CurrentChildViewmodel本身将被模板化到MainUC内的用户控件中。
这是一个相当简单的方法,在真实的世界中,你可能需要依赖注入、工厂等等,但这已经是解决堆栈溢出问题的相当多的代码了。
剩下的视图模型和视图只是简单的实现来证明它是可行的
以及
nuypyhwy4#
允许视图模型参与页面导航的方法有很多种。
通常,参与导航的每个类都必须能够访问您的导航API。
例如,您可以将导航逻辑移动到专用类
NavigationService
,并提供对每个类的共享引用,这些类应该能够导航到不同的视图。或者(也是推荐的),您可以使用在
MainWindow
上处理的路由命令,然后MainWindow
将命令委托给MainViewModel
。在这个场景中,每个按钮都必须以
CommandParameter
的形式传递目的地,这个解决方案允许特定的视图模型不直接参与导航,你不需要用导航细节污染你的视图模型类。以下示例显示如何使用
RoutedCommand
从QuizView
导航到ResultView
。主视图模型.cs
MainViewModel
是唯一知道如何导航和了解相关细节的视图模型类。这在保持视图模型类的实现简单的同时实现了可扩展性。
通常,要启用数据验证,请让视图模型实现
INotifyDataErrorInfo
。然后,您可以在允许离开页面之前查询
INotifyDataErrorInfo.HasErrors
属性。主窗口.xaml.cs
主窗口.xaml
要实际呈现视图(例如自定义控件),您需要定义一个隐式
DataTemplate
(不带x:Key
指令),它将关联的视图模型类定义为DataType
。然后ContentControl
将自动选择与ContentControl.Content
属性值类型匹配的正确视图。如果一个视图需要导航,它必须使用静态路由命令(在
MainWindow
中定义和处理),并将目标视图模型的Type
作为CommandParameter
传递。这样,导航就不会污染视图模型,并停留在视图中。
测验视图.xaml
结果视图.xaml
因为视图模型类通常不直接参与导航,
它们不必实现任何相关命令或依赖于任何导航服务。
导航完全由
MainWindow
和它的MainViewModel
控制。对于可选的数据验证,让他们实现
INotifyDataErrorInfo
。虚拟机测验.cs
结果虚拟机.cs