【问题标题】:C# WPF Navigation Between Pages (Views)页面(视图)之间的 C# WPF 导航
【发布时间】: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.StackOverflowException 告终。

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

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

【问题讨论】:

    标签: c# wpf navigation


    【解决方案1】:

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

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

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

    用法

    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>
    

    实施

    1. 创建页面控件。这可以是 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>
    
    1. 创建页面模型

    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));
          }
        }
    
    1. 创建页面标识符的枚举(以消除 XAML 和 C# 中的魔术字符串)

    PageName.cs

        public enum PageName
        {
          Undefined = 0, WelcomePage, LoginPage
        }
    
    1. 创建将管理页面及其导航的MainViewModel

    MainViewModel.cs
    RelayCommand 的实现可以在
    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 定义为一个静态类,其方法只需更改当前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;
        }
    }
    

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-29
      • 2021-12-08
      • 1970-01-01
      • 1970-01-01
      • 2020-05-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多