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

Paw*_*wel 3 c# navigation wpf

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

到目前为止我得到了:

class MainWindowNavigation : MainWindow
{
    public MainWindow mainWindow;

   public void ChangePage(Page page)
    {
        mainWindow.Content = page;
    }
}
Run Code Online (Sandbox Code Playgroud)

主窗口本身:

public MainWindow()
    {
        InitializeComponent();
        MainWindowNavigation mainWindow = new MainWindowNavigation();
        mainWindow.ChangePage(new Pages.MainWindowPage());
    }
Run Code Online (Sandbox Code Playgroud)

不幸的是,这最终导致了 System.StackOverflowException。

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

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

Bio*_*ode 14

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

任务很简单。为每个视图创建一个UserControl(或),例如,及其相应的视图模型和。DataTemplateWelcomePageLoginPageWelcomePageViewModelLoginPageViewModel

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

用法

主窗口.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>
Run Code Online (Sandbox Code Playgroud)

执行

  1. 创建单独的页面控件(承载页面内容的控件)。这可以是Control,UserControlPage简单的DataTemplate:

欢迎页面.xaml

    <UserControl>
      <StackPanel>
        <TextBlock Text="{Binding PageTitle}" />
        <TextBlock Text="{Binding Message}" />
      </StackPanel>
    </UserControl>
Run Code Online (Sandbox Code Playgroud)

登录页面.xaml

    <UserControl>
      <StackPanel>
        <TextBlock Text="{Binding PageTitle}" />
        <TextBox Text="{Binding UserName}" />
      </StackPanel>
    </UserControl>
Run Code Online (Sandbox Code Playgroud)
  1. 创建页面模型:

IPage.cs

    interface IPage : INotifyPropertyChanged
    {
      string PageTitel { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)

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));
      }
    }
Run Code Online (Sandbox Code Playgroud)

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

页面名称.cs

    public enum PageName
    {
      Undefined = 0, WelcomePage, LoginPage
    }
Run Code Online (Sandbox Code Playgroud)
  1. 创建MainViewModel将管理页面及其导航的:

MainViewModel.cs
的实现RelayCommand可以在 Microsoft Docs 中找到
:模式 - 使用模型-视图-视图模型设计模式的 WPF 应用程序 -中继命令逻辑

    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));
    }
Run Code Online (Sandbox Code Playgroud)

  • 每次我必须使用 WPF 做一些事情时,我都会一遍又一遍地回到这个问题。这是一个非常好的起点教程。 (4认同)