C# 如何在WPF应用程序中的页面之间切换?
我正在尝试创建一个WPF应用程序,它显示一个登录视图,成功登录后,将显示第一、第二和第三页(类似于向导)。包括登录视图在内的每个“页面”都有其各自的C# 如何在WPF应用程序中的页面之间切换?,c#,wpf,xaml,C#,Wpf,Xaml,我正在尝试创建一个WPF应用程序,它显示一个登录视图,成功登录后,将显示第一、第二和第三页(类似于向导)。包括登录视图在内的每个“页面”都有其各自的ViewModel。我有一个main window.xaml,其中包含四个UserControls,其中一个在任何给定状态下都可见 我在处理可见性的编排方面遇到了问题。对我来说最有意义的是,MainWindowViewModel是负责跟踪当前可见的UserControl的工具,但我似乎无法让代码正常工作 我将只显示main窗口和LoginView的相
ViewModel
。我有一个main window.xaml
,其中包含四个UserControls
,其中一个在任何给定状态下都可见
我在处理可见性的编排方面遇到了问题。对我来说最有意义的是,MainWindowViewModel
是负责跟踪当前可见的UserControl
的工具,但我似乎无法让代码正常工作
我将只显示main窗口
和LoginView
的相关文件,以简化操作
main window.xaml
<Grid>
<local:LoginView Visibility="{Not sure what to bind to here}" />
<local:PageOne Visibility="{Not sure what to bind to here}" />
<local:PageTwo Visibility="{Not sure what to bind to here}" />
<local:PageThree Visibility="{Not sure what to bind to here}" />
</Grid>
<UserControl x:Class="MyProject.View.LoginView"
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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyProject.View"
mc:Ignorable="d"
d:DesignHeight="800" d:DesignWidth="1200">
<Grid>
<!-- UI Layout stuff here -->
</Grid>
</UserControl>
<Window>
<Window.DataContext>
<MainViewModel x:Key="MainViewModel" />
</Window.DataContext>
<Window.Resources>
<!--
The templates for the view of each page model.
Can be moved to dedicated files.
-->
<DataTemplate DataType="{x:Type LoginViewModel}">
<Border Background="Coral">
<!-- UserControl -->
<local:LoginView />
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type PageOneViewModel}">
<Border Background="Red">
<local:PageOne />
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type PageTwoViewModel}">
<Border Background="DeepSkyBlue">
<TextBox Text="{Binding PageTitle}" />
</Border>
</DataTemplate>
</Window.Resources>
<StackPanel>
<Button Content="Load Login Page"
Command="{Binding SelectPageFromIndexCommand}"
CommandParameter="0" />
<Button Content="Load Page One"
Command="{Binding SelectPageFromIndexCommand}"
CommandParameter="1" />
<Button Content="Load Next Page"
Command="{Binding SelectNextPageCommand}" />
<!-- The actual page control -->
<ContentControl Content="{Binding SelectedPage}" />
</StackPanel>
</Window>
MainWindowViewModel.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
public partial class Login : UserControl
{
public Login()
{
InitializeComponent();
DataContext = new LoginViewModel();
}
}
public class LoginViewModel : BaseViewModel
{
public ICommand ConnectCommand { get; }
public ICommand WindowClosingCommand { get; }
public LoginViewModel()
{
ConnectCommand = new ConnectCommand(this);
WindowClosingCommand = new WindowClosingCommand(this);
}
public string UserName { get; set; }
}
class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
this.Pages = new ObservableCollection<IPageViewModel>()
{
new LoginViewModel(),
new PageOneViewModel(),
new PageTwoViewModel()
};
// Show startup page
this.SelectedPage = this.Pages.First();
}
// Define the Execute and CanExecute delegates for the command
// and pass them to the constructor
public ICommand SelectPageFromIndexCommand => new SelectPageCommand(
param => this.SelectedPage = this.Pages.ElementAt(int.Parse(param as string)),
param => int.TryParse(param as string, out int index));
// Define the Execute and CanExecute delegates for the command
// and pass them to the constructor
public ICommand SelectNextPageCommand => new SelectPageCommand(
param => this.SelectedPage = this.Pages.ElementAt(this.Pages.IndexOf(this.SelectedPage) + 1),
param => this.Pages.IndexOf(this.SelectedPage) + 1 < this.Pages.Count);
private IPageViewModel selectedPage;
public IPageViewModel SelectedPage
{
get => this.selectedPage;
set
{
if (object.Equals(value, this.selectedPage))
{
return;
}
this.selectedPage = value;
OnPropertyChanged();
}
}
private ObservableCollection<IPageViewModel> pages;
public ObservableCollection<IPageViewModel> Pages
{
get => this.pages;
set
{
if (object.Equals(value, this.pages))
{
return;
}
this.pages = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class SelectPageCommand : ICommand
{
public SelectPageCommand(Action<object> executeDelegate, Predicate<object> canExecuteDelegate)
{
this.ExecuteDelegate = executeDelegate;
this.CanExecuteDelegate = canExecuteDelegate;
}
private Predicate<object> CanExecuteDelegate { get; }
private Action<object> ExecuteDelegate { get; }
#region Implementation of ICommand
public bool CanExecute(object parameter) => this.CanExecuteDelegate?.Invoke(parameter) ?? false;
public void Execute(object parameter) => this.ExecuteDelegate?.Invoke(parameter);
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
#endregion
}
// Base type for all pages
interface IPageViewModel : INotifyPropertyChanged
{
public string PageTitle { get; set; }
}
// BaseViewModel implementation.
// Consider to introduce dedicated abstract class Page which implements IPageViewModel
class LoginViewModel : BaseViewModel
{
// Implementation
}
// BaseViewModel implementation.
// Consider to introduce dedicated abstract class Page which implements IPageViewModel
class PageOneViewModel : IPageViewModel
{
// Implementation
}
// BaseViewModel implementation.
// Consider to introduce dedicated abstract class Page which implements IPageViewModel
class PageTwoViewModel : IPageViewModel
{
// Implementation
}
公共类MainWindowViewModel:BaseViewModel
{
公共ICommand WindowClosingCommand{get;}
public MainWindowViewModel()
{
WindowClosingCommand = new WindowClosingCommand(this);
}
}
LoginView.xaml
<Grid>
<local:LoginView Visibility="{Not sure what to bind to here}" />
<local:PageOne Visibility="{Not sure what to bind to here}" />
<local:PageTwo Visibility="{Not sure what to bind to here}" />
<local:PageThree Visibility="{Not sure what to bind to here}" />
</Grid>
<UserControl x:Class="MyProject.View.LoginView"
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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyProject.View"
mc:Ignorable="d"
d:DesignHeight="800" d:DesignWidth="1200">
<Grid>
<!-- UI Layout stuff here -->
</Grid>
</UserControl>
<Window>
<Window.DataContext>
<MainViewModel x:Key="MainViewModel" />
</Window.DataContext>
<Window.Resources>
<!--
The templates for the view of each page model.
Can be moved to dedicated files.
-->
<DataTemplate DataType="{x:Type LoginViewModel}">
<Border Background="Coral">
<!-- UserControl -->
<local:LoginView />
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type PageOneViewModel}">
<Border Background="Red">
<local:PageOne />
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type PageTwoViewModel}">
<Border Background="DeepSkyBlue">
<TextBox Text="{Binding PageTitle}" />
</Border>
</DataTemplate>
</Window.Resources>
<StackPanel>
<Button Content="Load Login Page"
Command="{Binding SelectPageFromIndexCommand}"
CommandParameter="0" />
<Button Content="Load Page One"
Command="{Binding SelectPageFromIndexCommand}"
CommandParameter="1" />
<Button Content="Load Next Page"
Command="{Binding SelectNextPageCommand}" />
<!-- The actual page control -->
<ContentControl Content="{Binding SelectedPage}" />
</StackPanel>
</Window>
LoginViewModel.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
public partial class Login : UserControl
{
public Login()
{
InitializeComponent();
DataContext = new LoginViewModel();
}
}
public class LoginViewModel : BaseViewModel
{
public ICommand ConnectCommand { get; }
public ICommand WindowClosingCommand { get; }
public LoginViewModel()
{
ConnectCommand = new ConnectCommand(this);
WindowClosingCommand = new WindowClosingCommand(this);
}
public string UserName { get; set; }
}
class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
this.Pages = new ObservableCollection<IPageViewModel>()
{
new LoginViewModel(),
new PageOneViewModel(),
new PageTwoViewModel()
};
// Show startup page
this.SelectedPage = this.Pages.First();
}
// Define the Execute and CanExecute delegates for the command
// and pass them to the constructor
public ICommand SelectPageFromIndexCommand => new SelectPageCommand(
param => this.SelectedPage = this.Pages.ElementAt(int.Parse(param as string)),
param => int.TryParse(param as string, out int index));
// Define the Execute and CanExecute delegates for the command
// and pass them to the constructor
public ICommand SelectNextPageCommand => new SelectPageCommand(
param => this.SelectedPage = this.Pages.ElementAt(this.Pages.IndexOf(this.SelectedPage) + 1),
param => this.Pages.IndexOf(this.SelectedPage) + 1 < this.Pages.Count);
private IPageViewModel selectedPage;
public IPageViewModel SelectedPage
{
get => this.selectedPage;
set
{
if (object.Equals(value, this.selectedPage))
{
return;
}
this.selectedPage = value;
OnPropertyChanged();
}
}
private ObservableCollection<IPageViewModel> pages;
public ObservableCollection<IPageViewModel> Pages
{
get => this.pages;
set
{
if (object.Equals(value, this.pages))
{
return;
}
this.pages = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class SelectPageCommand : ICommand
{
public SelectPageCommand(Action<object> executeDelegate, Predicate<object> canExecuteDelegate)
{
this.ExecuteDelegate = executeDelegate;
this.CanExecuteDelegate = canExecuteDelegate;
}
private Predicate<object> CanExecuteDelegate { get; }
private Action<object> ExecuteDelegate { get; }
#region Implementation of ICommand
public bool CanExecute(object parameter) => this.CanExecuteDelegate?.Invoke(parameter) ?? false;
public void Execute(object parameter) => this.ExecuteDelegate?.Invoke(parameter);
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
#endregion
}
// Base type for all pages
interface IPageViewModel : INotifyPropertyChanged
{
public string PageTitle { get; set; }
}
// BaseViewModel implementation.
// Consider to introduce dedicated abstract class Page which implements IPageViewModel
class LoginViewModel : BaseViewModel
{
// Implementation
}
// BaseViewModel implementation.
// Consider to introduce dedicated abstract class Page which implements IPageViewModel
class PageOneViewModel : IPageViewModel
{
// Implementation
}
// BaseViewModel implementation.
// Consider to introduce dedicated abstract class Page which implements IPageViewModel
class PageTwoViewModel : IPageViewModel
{
// Implementation
}
正如您所看到的,我希望避免在代码隐藏文件.xaml.cs
中放入大量逻辑,因为这是最佳实践,我有一个ViewModel
文件,它的.xaml
文件
public PageType CurrentPage;
public enum PageType
{
Login, PageOne, PageTwo, PageThree
}
public Visibility LoginVisibility
{
get { (CurrentPage == PageType.Login) ? Visibility.Visible : Visibility.Collapsed }
}
// Repeat for each of the other three pages
然后根据是否在每页上单击了“下一步”或“上一步”按钮,我会正确设置CurrentPage
字段
但是,如果我们回到我的MainWindow.xaml
,我不能只做:
<local:LoginView Visibility="{Binding LoginVisibility}" />
因为LoginVisibility
不存在于LoginViewModel
中,而这正是用户控件的数据上下文。将该字段放在那里是不对的,因为所有ViewModels
都需要知道自己的可见性状态,并以某种方式将其传达到main窗口
基本上,我对如何在应用程序中的页面之间切换感到困惑和不确定。任何帮助或指导都将不胜感激。您可以在主窗口资源中创建数据模板,并将适当的数据模板绑定到控件模板,而不是绑定可见性(在您希望显示它的网格内)基于枚举更改 下面是一个粗略的想法 在mainwindow.xaml中
<Window.Resources>
<ResourceDictionary>
<DataTemplate x:Key="DTLoginView">
<local:LoginView />
</DataTemplate>
<DataTemplate x:Key="DTPageOne">
<local:PageOne />
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
在主窗口的XAML中。(在其中有网格)
如果您查看上面的xaml代码,我将datatrigger绑定的值设置为“0”“1”,因为枚举应该有0,1,2,3等等。但是,您也可以直接将枚举绑定为值。进行一些搜索,您可以轻松找到答案
当前页面的属性(枚举值)应该由某些逻辑(由您实现)设置。一旦设置完成,它将自动触发对xaml的通知
希望这能对您有所帮助。与使用
框架相比,最简单、最轻量级的方法是为每个页面创建一个视图模型。然后创建一个包含所有页面并管理其选择的主视图模型。ContentControl
将使用分配给eContentControl.ContentTemplate
属性,或者在多页场景中,通过只定义DataTemplate.DataType
而不定义键
属性,将DataTemplateSelector
分配给ContentControl.ContentTemplateSelector
或隐式模板:
景色
main window.xaml
<Grid>
<local:LoginView Visibility="{Not sure what to bind to here}" />
<local:PageOne Visibility="{Not sure what to bind to here}" />
<local:PageTwo Visibility="{Not sure what to bind to here}" />
<local:PageThree Visibility="{Not sure what to bind to here}" />
</Grid>
<UserControl x:Class="MyProject.View.LoginView"
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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyProject.View"
mc:Ignorable="d"
d:DesignHeight="800" d:DesignWidth="1200">
<Grid>
<!-- UI Layout stuff here -->
</Grid>
</UserControl>
<Window>
<Window.DataContext>
<MainViewModel x:Key="MainViewModel" />
</Window.DataContext>
<Window.Resources>
<!--
The templates for the view of each page model.
Can be moved to dedicated files.
-->
<DataTemplate DataType="{x:Type LoginViewModel}">
<Border Background="Coral">
<!-- UserControl -->
<local:LoginView />
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type PageOneViewModel}">
<Border Background="Red">
<local:PageOne />
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type PageTwoViewModel}">
<Border Background="DeepSkyBlue">
<TextBox Text="{Binding PageTitle}" />
</Border>
</DataTemplate>
</Window.Resources>
<StackPanel>
<Button Content="Load Login Page"
Command="{Binding SelectPageFromIndexCommand}"
CommandParameter="0" />
<Button Content="Load Page One"
Command="{Binding SelectPageFromIndexCommand}"
CommandParameter="1" />
<Button Content="Load Next Page"
Command="{Binding SelectNextPageCommand}" />
<!-- The actual page control -->
<ContentControl Content="{Binding SelectedPage}" />
</StackPanel>
</Window>
LoginViewModel.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
public partial class Login : UserControl
{
public Login()
{
InitializeComponent();
DataContext = new LoginViewModel();
}
}
public class LoginViewModel : BaseViewModel
{
public ICommand ConnectCommand { get; }
public ICommand WindowClosingCommand { get; }
public LoginViewModel()
{
ConnectCommand = new ConnectCommand(this);
WindowClosingCommand = new WindowClosingCommand(this);
}
public string UserName { get; set; }
}
class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
this.Pages = new ObservableCollection<IPageViewModel>()
{
new LoginViewModel(),
new PageOneViewModel(),
new PageTwoViewModel()
};
// Show startup page
this.SelectedPage = this.Pages.First();
}
// Define the Execute and CanExecute delegates for the command
// and pass them to the constructor
public ICommand SelectPageFromIndexCommand => new SelectPageCommand(
param => this.SelectedPage = this.Pages.ElementAt(int.Parse(param as string)),
param => int.TryParse(param as string, out int index));
// Define the Execute and CanExecute delegates for the command
// and pass them to the constructor
public ICommand SelectNextPageCommand => new SelectPageCommand(
param => this.SelectedPage = this.Pages.ElementAt(this.Pages.IndexOf(this.SelectedPage) + 1),
param => this.Pages.IndexOf(this.SelectedPage) + 1 < this.Pages.Count);
private IPageViewModel selectedPage;
public IPageViewModel SelectedPage
{
get => this.selectedPage;
set
{
if (object.Equals(value, this.selectedPage))
{
return;
}
this.selectedPage = value;
OnPropertyChanged();
}
}
private ObservableCollection<IPageViewModel> pages;
public ObservableCollection<IPageViewModel> Pages
{
get => this.pages;
set
{
if (object.Equals(value, this.pages))
{
return;
}
this.pages = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class SelectPageCommand : ICommand
{
public SelectPageCommand(Action<object> executeDelegate, Predicate<object> canExecuteDelegate)
{
this.ExecuteDelegate = executeDelegate;
this.CanExecuteDelegate = canExecuteDelegate;
}
private Predicate<object> CanExecuteDelegate { get; }
private Action<object> ExecuteDelegate { get; }
#region Implementation of ICommand
public bool CanExecute(object parameter) => this.CanExecuteDelegate?.Invoke(parameter) ?? false;
public void Execute(object parameter) => this.ExecuteDelegate?.Invoke(parameter);
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
#endregion
}
// Base type for all pages
interface IPageViewModel : INotifyPropertyChanged
{
public string PageTitle { get; set; }
}
// BaseViewModel implementation.
// Consider to introduce dedicated abstract class Page which implements IPageViewModel
class LoginViewModel : BaseViewModel
{
// Implementation
}
// BaseViewModel implementation.
// Consider to introduce dedicated abstract class Page which implements IPageViewModel
class PageOneViewModel : IPageViewModel
{
// Implementation
}
// BaseViewModel implementation.
// Consider to introduce dedicated abstract class Page which implements IPageViewModel
class PageTwoViewModel : IPageViewModel
{
// Implementation
}
PageOneViewModel.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
public partial class Login : UserControl
{
public Login()
{
InitializeComponent();
DataContext = new LoginViewModel();
}
}
public class LoginViewModel : BaseViewModel
{
public ICommand ConnectCommand { get; }
public ICommand WindowClosingCommand { get; }
public LoginViewModel()
{
ConnectCommand = new ConnectCommand(this);
WindowClosingCommand = new WindowClosingCommand(this);
}
public string UserName { get; set; }
}
class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
this.Pages = new ObservableCollection<IPageViewModel>()
{
new LoginViewModel(),
new PageOneViewModel(),
new PageTwoViewModel()
};
// Show startup page
this.SelectedPage = this.Pages.First();
}
// Define the Execute and CanExecute delegates for the command
// and pass them to the constructor
public ICommand SelectPageFromIndexCommand => new SelectPageCommand(
param => this.SelectedPage = this.Pages.ElementAt(int.Parse(param as string)),
param => int.TryParse(param as string, out int index));
// Define the Execute and CanExecute delegates for the command
// and pass them to the constructor
public ICommand SelectNextPageCommand => new SelectPageCommand(
param => this.SelectedPage = this.Pages.ElementAt(this.Pages.IndexOf(this.SelectedPage) + 1),
param => this.Pages.IndexOf(this.SelectedPage) + 1 < this.Pages.Count);
private IPageViewModel selectedPage;
public IPageViewModel SelectedPage
{
get => this.selectedPage;
set
{
if (object.Equals(value, this.selectedPage))
{
return;
}
this.selectedPage = value;
OnPropertyChanged();
}
}
private ObservableCollection<IPageViewModel> pages;
public ObservableCollection<IPageViewModel> Pages
{
get => this.pages;
set
{
if (object.Equals(value, this.pages))
{
return;
}
this.pages = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class SelectPageCommand : ICommand
{
public SelectPageCommand(Action<object> executeDelegate, Predicate<object> canExecuteDelegate)
{
this.ExecuteDelegate = executeDelegate;
this.CanExecuteDelegate = canExecuteDelegate;
}
private Predicate<object> CanExecuteDelegate { get; }
private Action<object> ExecuteDelegate { get; }
#region Implementation of ICommand
public bool CanExecute(object parameter) => this.CanExecuteDelegate?.Invoke(parameter) ?? false;
public void Execute(object parameter) => this.ExecuteDelegate?.Invoke(parameter);
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
#endregion
}
// Base type for all pages
interface IPageViewModel : INotifyPropertyChanged
{
public string PageTitle { get; set; }
}
// BaseViewModel implementation.
// Consider to introduce dedicated abstract class Page which implements IPageViewModel
class LoginViewModel : BaseViewModel
{
// Implementation
}
// BaseViewModel implementation.
// Consider to introduce dedicated abstract class Page which implements IPageViewModel
class PageOneViewModel : IPageViewModel
{
// Implementation
}
// BaseViewModel implementation.
// Consider to introduce dedicated abstract class Page which implements IPageViewModel
class PageTwoViewModel : IPageViewModel
{
// Implementation
}
PageTwoViewModel.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
public partial class Login : UserControl
{
public Login()
{
InitializeComponent();
DataContext = new LoginViewModel();
}
}
public class LoginViewModel : BaseViewModel
{
public ICommand ConnectCommand { get; }
public ICommand WindowClosingCommand { get; }
public LoginViewModel()
{
ConnectCommand = new ConnectCommand(this);
WindowClosingCommand = new WindowClosingCommand(this);
}
public string UserName { get; set; }
}
class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
this.Pages = new ObservableCollection<IPageViewModel>()
{
new LoginViewModel(),
new PageOneViewModel(),
new PageTwoViewModel()
};
// Show startup page
this.SelectedPage = this.Pages.First();
}
// Define the Execute and CanExecute delegates for the command
// and pass them to the constructor
public ICommand SelectPageFromIndexCommand => new SelectPageCommand(
param => this.SelectedPage = this.Pages.ElementAt(int.Parse(param as string)),
param => int.TryParse(param as string, out int index));
// Define the Execute and CanExecute delegates for the command
// and pass them to the constructor
public ICommand SelectNextPageCommand => new SelectPageCommand(
param => this.SelectedPage = this.Pages.ElementAt(this.Pages.IndexOf(this.SelectedPage) + 1),
param => this.Pages.IndexOf(this.SelectedPage) + 1 < this.Pages.Count);
private IPageViewModel selectedPage;
public IPageViewModel SelectedPage
{
get => this.selectedPage;
set
{
if (object.Equals(value, this.selectedPage))
{
return;
}
this.selectedPage = value;
OnPropertyChanged();
}
}
private ObservableCollection<IPageViewModel> pages;
public ObservableCollection<IPageViewModel> Pages
{
get => this.pages;
set
{
if (object.Equals(value, this.pages))
{
return;
}
this.pages = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class SelectPageCommand : ICommand
{
public SelectPageCommand(Action<object> executeDelegate, Predicate<object> canExecuteDelegate)
{
this.ExecuteDelegate = executeDelegate;
this.CanExecuteDelegate = canExecuteDelegate;
}
private Predicate<object> CanExecuteDelegate { get; }
private Action<object> ExecuteDelegate { get; }
#region Implementation of ICommand
public bool CanExecute(object parameter) => this.CanExecuteDelegate?.Invoke(parameter) ?? false;
public void Execute(object parameter) => this.ExecuteDelegate?.Invoke(parameter);
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
#endregion
}
// Base type for all pages
interface IPageViewModel : INotifyPropertyChanged
{
public string PageTitle { get; set; }
}
// BaseViewModel implementation.
// Consider to introduce dedicated abstract class Page which implements IPageViewModel
class LoginViewModel : BaseViewModel
{
// Implementation
}
// BaseViewModel implementation.
// Consider to introduce dedicated abstract class Page which implements IPageViewModel
class PageOneViewModel : IPageViewModel
{
// Implementation
}
// BaseViewModel implementation.
// Consider to introduce dedicated abstract class Page which implements IPageViewModel
class PageTwoViewModel : IPageViewModel
{
// Implementation
}
在VMMS中使用“代码>内容控制< /代码>。在MaVIM上创建属性以存储当前VM。一旦将内容控件绑定到当前视图模型,就不需要EnUM。也不需要处理可视性。通常我会先推荐VIEWMIDE。向导是我考虑的少数需求之一。一个框架中的g个页面。您可以轻松地在一个框架内前后导航。每个页面都保留其视图状态。在di容器或资源或中介对象中放置对viewmodels的引用,并使页面从中获取其datacontext。如果需要重置,请再次实例化页面并替换这些实例。或者您可以uld首先进入viewmodel。你有用户控件。你有viewmodels。你所需要做的就是告诉它哪个uc用于哪个vm,谢谢你的评论。我想我在想你在这里要做什么。但是,页面模型(例如PageA.cs
和PageB.cs
)与用户控件中的UI有什么关系(PageOne.xaml
)我设计的?我调整了代码示例。您现在可以复制和粘贴代码,它将运行。只需添加视图模型的实现,并添加视图实现,以便可以实例化DataTemplate
中定义的对象。基本思想是将页面视图包装到DataTemplate
它的DataType
设置为作为数据源的模型类型,即包装视图的DataContext
。我还添加了一个SelectNextPageCommand
,并在视图中添加了一个相应的按钮
,以显示其基本原理和简单性。您现在可以通过索引和下一页进行选择。只需在e所有页面模型都派生自一个公共类型。Thia common type是包含所有页面的集合的通用参数。还要确保公共基类型的每个实现