C# WPF 页面间导航(视图)
C# WPF Navigation Between Pages (Views)
我正在尝试创建一个 class & 方法,它可以在任何 window 和页面上使用,以更改 MainWindow window 上显示的当前页面。
到目前为止我得到了:
class MainWindowNavigation : MainWindow
{
public MainWindow mainWindow;
public void ChangePage(Page page)
{
mainWindow.Content = page;
}
}
主要window本身:
public MainWindow()
{
InitializeComponent();
MainWindowNavigation mainWindow = new MainWindowNavigation();
mainWindow.ChangePage(new Pages.MainWindowPage());
}
不幸的是,这以 System.WhosebugException 结束。
创建这个的主要原因是我希望能够从当前显示在 mainWindow.Content 中的页面更改 mainWindow.Content。
我已经看过 MVVM,但我认为它不值得用于像这样的小型应用程序,因为我想要它做的就是在打开时显示一个欢迎页面,然后在旁边几乎没有纽扣。一旦按下 mainWindow.Content 正确更改为用户可以输入登录详细信息的页面,然后在登录页面上按下按钮,我想在成功验证登录后将 mainWindow.Content 更改为另一个页面输入了详细信息。
使用 MVVM 绝对没问题,因为它将简化您的需求的实现。 WPF 是为与 MVVM 模式一起使用而构建的,这意味着大量使用数据绑定和数据模板。
任务很简单。为每个视图创建一个 UserControl
(或 DataTemplate
),例如 WelcomePage
和 LoginPage
及其对应的视图模型 WelcomePageViewModel
和 LoginPageViewModel
。
A ContentControl
将显示页面。
主要技巧是,当使用隐式 DataTemplate
(没有定义 x:Key
的模板资源)时,XAML 解析器将自动查找并应用正确的模板,其中 DataType
匹配 ContentControl
的当前内容类型。这使得导航非常简单,因为您只需从页面模型集合中 select 当前页面,并通过数据绑定将此页面设置为 [=24= 的 Content
属性 ] 或 ContentPresenter
:
用法
MainWindow.xaml
<Window>
<Window.DataContext>
<MainViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type WelcomePageviewModel}">
<WelcomPage />
</DataTemplate>
<DataTemplate DataType="{x:Type LoginPageviewModel}">
<LoginPage />
</DataTemplate>
</Window.Resources>
<StackPanel>
<!-- Page navigation -->
<StackPanel Orientation="Horizontal">
<Button Content="Show Login Screen"
Command="{Binding SelectPageCommand}"
CommandParameter="{x:Static PageName.LoginPage}" />
<Button Content="Show Welcome Screen"
Command="{Binding SelectPageCommand}"
CommandParameter="{x:Static PageName.WelcomePage}" />
</StackPanel>
<!--
Host of SelectedPage.
Automatically displays the DataTemplate that matches the current data type
-->
<ContentControl Content="{Binding SelectedPage}" />
<StackPanel>
</Window>
实施
- 创建页面控件。这可以是
Control
、UserControl
、Page
或简单的 DataTemplate
WelcomePage.xaml
<UserControl>
<StackPanel>
<TextBlock Text="{Binding PageTitle}" />
<TextBlock Text="{Binding Message}" />
</StackPanel>
</UserControl>
LoginPage.xaml
<UserControl>
<StackPanel>
<TextBlock Text="{Binding PageTitle}" />
<TextBox Text="{Binding UserName}" />
</StackPanel>
</UserControl>
- 创建页面模型
IPage.cs
interface IPage : INotifyPropertyChanged
{
string PageTitel { get; set; }
}
WelcomePageViewModel.cs
class WelcomePageViewModel : IPage
{
private string pageTitle;
public string PageTitle
{
get => this.pageTitle;
set
{
this.pageTitle = value;
OnPropertyChanged();
}
}
private string message;
public string Message
{
get => this.message;
set
{
this.message = value;
OnPropertyChanged();
}
}
public WelcomePageViewModel()
{
this.PageTitle = "Welcome";
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
LoginPageViewModel.cs
class LoginPageViewModel : IPage
{
private string pageTitle;
public string PageTitle
{
get => this.pageTitle;
set
{
this.pageTitle = value;
OnPropertyChanged();
}
}
private string userName;
public string UserName
{
get => this.userName;
set
{
this.userName = value;
OnPropertyChanged();
}
}
public LoginPageViewModel()
{
this.PageTitle = "Login";
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
- 创建页面标识符枚举(以消除 XAML 和 C# 中的魔法字符串)
PageName.cs
public enum PageName
{
Undefined = 0, WelcomePage, LoginPage
}
- 创建
MainViewModel
来管理页面及其导航
MainViewModel.cs
RelayCommand
的实现可以在
找到
Microsoft 文档:模式 - 使用模型-视图-视图模型设计模式的 WPF 应用程序 - Relaying Command Logic
class MainViewModel
{
public ICommand SelectPageCommand => new RelayCommand(SelectPage);
private Dictionary<PageName, IPage> Pages { get; }
private IPage selectedPage;
public IPage SelectedPage
{
get => this.selectedPage;
set
{
this.selectedPage = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
this.Pages = new Dictionary<PageName, IPage>
{
{ PageName.WelcomePage, new WelcomePageViewModel() },
{ PageName.LoginPage, new LoginPageViewModel() }
};
this.SelectedPage = this.Pages.First().Value;
}
public void SelectPage(object param)
{
if (param is PageName pageName
&& this.Pages.TryGetValue(pageName, out IPage selectedPage))
{
this.SelectedPage = selectedPage;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
=> this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
您可能想将 MainWindowNavigation
定义为静态 class,方法是简单地更改当前 MainWindow
:
的 Content
static class MainWindowNavigation
{
public static void ChangePage(Page page)
{
var mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
if (mainWindow != null)
mainWindow.Content = page;
}
}
然后您可以从任何 class 调用该方法,而无需引用 MainWindow
:
MainWindowNavigation.ChangePage(new Pages.MainWindowPage());
我正在尝试创建一个 class & 方法,它可以在任何 window 和页面上使用,以更改 MainWindow window 上显示的当前页面。
到目前为止我得到了:
class MainWindowNavigation : MainWindow
{
public MainWindow mainWindow;
public void ChangePage(Page page)
{
mainWindow.Content = page;
}
}
主要window本身:
public MainWindow()
{
InitializeComponent();
MainWindowNavigation mainWindow = new MainWindowNavigation();
mainWindow.ChangePage(new Pages.MainWindowPage());
}
不幸的是,这以 System.WhosebugException 结束。
创建这个的主要原因是我希望能够从当前显示在 mainWindow.Content 中的页面更改 mainWindow.Content。
我已经看过 MVVM,但我认为它不值得用于像这样的小型应用程序,因为我想要它做的就是在打开时显示一个欢迎页面,然后在旁边几乎没有纽扣。一旦按下 mainWindow.Content 正确更改为用户可以输入登录详细信息的页面,然后在登录页面上按下按钮,我想在成功验证登录后将 mainWindow.Content 更改为另一个页面输入了详细信息。
使用 MVVM 绝对没问题,因为它将简化您的需求的实现。 WPF 是为与 MVVM 模式一起使用而构建的,这意味着大量使用数据绑定和数据模板。
任务很简单。为每个视图创建一个 UserControl
(或 DataTemplate
),例如 WelcomePage
和 LoginPage
及其对应的视图模型 WelcomePageViewModel
和 LoginPageViewModel
。
A ContentControl
将显示页面。
主要技巧是,当使用隐式 DataTemplate
(没有定义 x:Key
的模板资源)时,XAML 解析器将自动查找并应用正确的模板,其中 DataType
匹配 ContentControl
的当前内容类型。这使得导航非常简单,因为您只需从页面模型集合中 select 当前页面,并通过数据绑定将此页面设置为 [=24= 的 Content
属性 ] 或 ContentPresenter
:
用法
MainWindow.xaml
<Window>
<Window.DataContext>
<MainViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type WelcomePageviewModel}">
<WelcomPage />
</DataTemplate>
<DataTemplate DataType="{x:Type LoginPageviewModel}">
<LoginPage />
</DataTemplate>
</Window.Resources>
<StackPanel>
<!-- Page navigation -->
<StackPanel Orientation="Horizontal">
<Button Content="Show Login Screen"
Command="{Binding SelectPageCommand}"
CommandParameter="{x:Static PageName.LoginPage}" />
<Button Content="Show Welcome Screen"
Command="{Binding SelectPageCommand}"
CommandParameter="{x:Static PageName.WelcomePage}" />
</StackPanel>
<!--
Host of SelectedPage.
Automatically displays the DataTemplate that matches the current data type
-->
<ContentControl Content="{Binding SelectedPage}" />
<StackPanel>
</Window>
实施
- 创建页面控件。这可以是
Control
、UserControl
、Page
或简单的DataTemplate
WelcomePage.xaml
<UserControl>
<StackPanel>
<TextBlock Text="{Binding PageTitle}" />
<TextBlock Text="{Binding Message}" />
</StackPanel>
</UserControl>
LoginPage.xaml
<UserControl>
<StackPanel>
<TextBlock Text="{Binding PageTitle}" />
<TextBox Text="{Binding UserName}" />
</StackPanel>
</UserControl>
- 创建页面模型
IPage.cs
interface IPage : INotifyPropertyChanged
{
string PageTitel { get; set; }
}
WelcomePageViewModel.cs
class WelcomePageViewModel : IPage
{
private string pageTitle;
public string PageTitle
{
get => this.pageTitle;
set
{
this.pageTitle = value;
OnPropertyChanged();
}
}
private string message;
public string Message
{
get => this.message;
set
{
this.message = value;
OnPropertyChanged();
}
}
public WelcomePageViewModel()
{
this.PageTitle = "Welcome";
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
LoginPageViewModel.cs
class LoginPageViewModel : IPage
{
private string pageTitle;
public string PageTitle
{
get => this.pageTitle;
set
{
this.pageTitle = value;
OnPropertyChanged();
}
}
private string userName;
public string UserName
{
get => this.userName;
set
{
this.userName = value;
OnPropertyChanged();
}
}
public LoginPageViewModel()
{
this.PageTitle = "Login";
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
- 创建页面标识符枚举(以消除 XAML 和 C# 中的魔法字符串)
PageName.cs
public enum PageName
{
Undefined = 0, WelcomePage, LoginPage
}
- 创建
MainViewModel
来管理页面及其导航
MainViewModel.cs
RelayCommand
的实现可以在
找到
Microsoft 文档:模式 - 使用模型-视图-视图模型设计模式的 WPF 应用程序 - Relaying Command Logic
class MainViewModel
{
public ICommand SelectPageCommand => new RelayCommand(SelectPage);
private Dictionary<PageName, IPage> Pages { get; }
private IPage selectedPage;
public IPage SelectedPage
{
get => this.selectedPage;
set
{
this.selectedPage = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
this.Pages = new Dictionary<PageName, IPage>
{
{ PageName.WelcomePage, new WelcomePageViewModel() },
{ PageName.LoginPage, new LoginPageViewModel() }
};
this.SelectedPage = this.Pages.First().Value;
}
public void SelectPage(object param)
{
if (param is PageName pageName
&& this.Pages.TryGetValue(pageName, out IPage selectedPage))
{
this.SelectedPage = selectedPage;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
=> this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
您可能想将 MainWindowNavigation
定义为静态 class,方法是简单地更改当前 MainWindow
:
Content
static class MainWindowNavigation
{
public static void ChangePage(Page page)
{
var mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
if (mainWindow != null)
mainWindow.Content = page;
}
}
然后您可以从任何 class 调用该方法,而无需引用 MainWindow
:
MainWindowNavigation.ChangePage(new Pages.MainWindowPage());