页面(视图)之间的 C# WPF 导航

Posted

技术标签:

【中文标题】页面(视图)之间的 C# WPF 导航【英文标题】:C# WPF Navigation Between Pages (Views) 【发布时间】:2020-08-02 20:23:50 【问题描述】:

我正在尝试创建一个可以在任何窗口和页面上使用的类和方法,以更改 MainWindow 窗口上显示的当前页面。

到目前为止,我得到了:

class MainWindowNavigation : MainWindow

    public MainWindow mainWindow;

   public void ChangePage(Page page)
    
        mainWindow.Content = page;
    

主窗口本身:

public MainWindow()
    
        InitializeComponent();
        MainWindowNavigation mainWindow = new MainWindowNavigation();
        mainWindow.ChangePage(new Pages.MainWindowPage());
    

不幸的是,这以 System.***Exception 告终。

创建它的主要原因是我希望能够从当前显示在 mainWindow.Content 中的页面更改 mainWindow.Content。

我已经审查过 MVVM,但我认为它不值得将它用于像这样的小型应用程序,因为我想要它做的只是在打开时显示一个欢迎页面,然后在侧面会有几个按钮。一旦按下 mainWindow.Content 正确更改为用户可以输入登录详细信息的页面,然后在登录页面上按下按钮,我想在成功验证输入的登录详细信息后将 mainWindow.Content 更改为不同的页面。

【问题讨论】:

【参考方案1】:

使用 MVVM 绝对没问题,因为它将简化您的需求的实现。 WPF 是为与 MVVM 模式一起使用而构建的,这意味着大量使用数据绑定和数据模板。

任务很简单。为每个视图创建一个UserControl(或DataTemplate),例如WelcomePageLoginPage及其对应的视图模型WelcomePageViewModelLoginPageViewModel

ContentControl 将显示页面。 主要技巧是,当使用隐式 DataTemplate(未定义 x:Key 的模板资源)时,XAML 解析器将自动查找并应用正确的模板,其中 DataType 匹配 @ 的当前内容类型987654332@。这使导航变得非常简单,因为您只需从页面模型集合中选择当前页面并通过数据绑定将此页面设置为ContentControlContentPresenterContent 属性:

用法

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>

实施

    创建页面控件。这可以是 ControlUserControlPage 或只是 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.csRelayCommand 的实现可以在Microsoft Docs:模式 - 具有模型-视图-视图模型设计模式的 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));
    

【讨论】:

每次我必须使用 WPF 做某事时,我都会一遍又一遍地回到这个问题上。这是一个非常好的起点教程。【参考方案2】:

您可能希望将MainWindowNavigation 定义为一个静态类,其方法只需更改当前MainWindowContent

static class MainWindowNavigation

    public static void ChangePage(Page page)
    
        var mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
        if (mainWindow != null)
            mainWindow.Content = page;
    

然后您可以从任何类调用该方法,而无需引用MainWindow

MainWindowNavigation.ChangePage(new Pages.MainWindowPage());

【讨论】:

以上是关于页面(视图)之间的 C# WPF 导航的主要内容,如果未能解决你的问题,请参考以下文章

页面之间导航 C# UWP

用于 WPF 导航的窗口、页面和用户控件?

在 Xamarin.forms 中的多个页面之间导航?

在页面 flutter,streambuilder,listview 之间导航时列表视图位置丢失

WPF线程安​​全和页面之间的通信

UINavigation的外观设置和页面之间的跳转