You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinUI 3中如何将嵌入页面绑定到主窗口ViewModel

如何让Frame导航的页面共享主窗口ViewModel(避免为每个页面单独创建VM)

我正在开发一个欢迎界面,主窗口通过Frame控件导航展示5个页面。希望将页面中的部分控件通过x:Bind绑定到主窗口的ViewModel,不想为这5个页面分别创建ViewModel,请问该如何实现?或者如何将主窗口的ViewModel对象传递给这5个页面?


现有代码参考

主窗口 WelcomeScreen.xaml

<Window
    x:Class="WelcomeScreen.WelcomeScreen"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WelcomeScreen"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="">
    <Grid ColumnDefinitions="*,*,*" RowDefinitions="*" >
        <StackPanel Grid.Column="0" Grid.ColumnSpan="3" Orientation="Vertical" >
            <Frame x:Name="NavigationFrame" />
            <Line Stroke="LightGray" X1="0" Y1="0" X2="1200" Y2="0" StrokeThickness="2" Margin="12,0,12,0"/>
            <RelativePanel>
                <CheckBox x:Name="DoNotShowAaginCheckBox" Content="Don't show this again" FontFamily="{StaticResource VeneerFont}" Checked="{x:Bind ViewModel.OnChecked}" Unchecked="{x:Bind ViewModel.OnChecked}" Visibility="{x:Bind ViewModel.IsCheckBoxVisible,Mode=OneWay}" Margin="12,12,0,0" RelativePanel.AlignLeftWithPanel="True" />

            <Button x:Name="BackButton" Width="100" Style="{StaticResource AccentButtonStyle}" FontFamily="{StaticResource VeneerFont}" Click="{x:Bind ViewModel.BackButton_Click}" RelativePanel.AlignLeftWithPanel="True" Content="Back" Visibility="{x:Bind ViewModel.IsBackButtonVisible,Mode=OneWay}"  Margin="12,12,12,0"/>

            <PipsPager x:Name="PipsPager" Margin="0,15,0,0" NumberOfPages="{x:Bind ViewModel.WelcomeScreenPageList.Count}" SelectedPageIndex="{x:Bind ViewModel.CurrentPageIndex, Mode=TwoWay}" RelativePanel.AlignHorizontalCenterWithPanel="True" SelectedIndexChanged="Pager_SelectedIndexChanged" />

            <Button x:Name="NextButton" Width="100" Style="{StaticResource AccentButtonStyle}" FontFamily="{StaticResource VeneerFont}" Click="{x:Bind ViewModel.NextButton_Click}" Content="{x:Bind ViewModel.NextButtonText,Mode=OneWay}" RelativePanel.AlignRightWithPanel="True" Margin="0,12,12,0"/>
        </RelativePanel>
    </StackPanel>
</Grid>

主窗口 WelcomeScreen.xaml.cs

public sealed partial class WelcomeScreen : Window
{
    internal WelcomeScreenPageViewModel ViewModel { get; set; }

    public WelcomeScreen(object viewModel) : this()
    {
        if (viewModel == null || viewModel.GetType() != typeof(WelcomeScreenPageViewModel))
            return;

        ViewModel = viewModel as WelcomeScreenPageViewModel;
    }

    public WelcomeScreen()
    {
        this.InitializeComponent();
        this.InitializeControls();
    }

    public void Pager_SelectedIndexChanged(object sender,PipsPagerSelectedIndexChangedEventArgs e)
    {
        bool isForward = false;
        if (ViewModel.CurrentPageIndex > ViewModel.PreviousPageIndex)
           isForward = true;

        ViewModel.PreviousPageIndex = ViewModel.CurrentPageIndex;

        Type pagetype =   Type.GetType(ViewModel.WelcomeScreenPageList[ViewModel.CurrentPageIndex]);

        if (isForward)
        {
            NavigationFrame.Navigate(pagetype,
                       null,
                       new SlideNavigationTransitionInfo()
                       { Effect = SlideNavigationTransitionEffect.FromRight });
        }
        else
        {
            NavigationFrame.Navigate(pagetype,
                       null,
                       new SlideNavigationTransitionInfo()
                       { Effect = SlideNavigationTransitionEffect.FromLeft });
        }

        ViewModel.UpdateControls();
    }
}

页面 WelcomeScreenPage1.xaml

<Page
    x:Class="WelcomeScreen.WelcomeScreenPage1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WelcomeScreen"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource SystemControlBackgroundChromeWhiteBrush}">

    <Grid ColumnDefinitions="*" RowDefinitions="*,*" >
      HorizontalAlignment="Right" Grid.Row="0" Margin="0,5,5,0"/>
        <StackPanel Orientation="Horizontal" Grid.Row="1">
            <TextBlock x:Name="PageText" Text="{x:Bind WelcomeScreenPageViewModel.xxx? , Mode=OneWay}""/>
        </StackPanel>
    </Grid>
</Page>

页面 WelcomeScreenPage1.xaml.cs

public sealed partial class WelcomeScreenPage1 : Page
{
    public WelcomeScreenPage1()
    {
        this.InitializeComponent();
    }
}

解决方案

方案一:通过导航参数传递ViewModel

在主窗口导航页面时,将主ViewModel作为参数传递给页面,页面接收后作为绑定源使用。

  1. 修改主窗口的导航代码,传递ViewModel:
// 替换原Navigate中的null为ViewModel
if (isForward)
{
    NavigationFrame.Navigate(pagetype,
               ViewModel, // 传递主窗口ViewModel
               new SlideNavigationTransitionInfo()
               { Effect = SlideNavigationTransitionEffect.FromRight });
}
else
{
    NavigationFrame.Navigate(pagetype,
               ViewModel, // 传递主窗口ViewModel
               new SlideNavigationTransitionInfo()
               { Effect = SlideNavigationTransitionEffect.FromLeft });
}
  1. 在页面中接收并保存ViewModel:
    修改WelcomeScreenPage1.xaml.cs,重写OnNavigatedTo方法获取参数:
public sealed partial class WelcomeScreenPage1 : Page
{
    public WelcomeScreenPageViewModel ViewModel { get; set; }

    public WelcomeScreenPage1()
    {
        this.InitializeComponent();
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);
        // 接收传递的ViewModel
        if (e.Parameter is WelcomeScreenPageViewModel vm)
        {
            ViewModel = vm;
            this.Bindings.Update(); // 通知UI更新绑定
        }
    }
}
  1. 修改页面XAML的绑定:
<TextBlock x:Name="PageText" Text="{x:Bind ViewModel.xxx, Mode=OneWay}"/>

方案二:直接绑定到主窗口的ViewModel

通过Window.Current.Content获取主窗口实例,直接在页面XAML中绑定到主窗口的ViewModel属性,无需修改页面后台代码。

修改页面XAML的绑定表达式:

<TextBlock x:Name="PageText" 
           Text="{x:Bind ((local:WelcomeScreen)Window.Current.Content).ViewModel.xxx, Mode=OneWay}"/>

两种方案对比

  • 方案一:耦合度低,页面可自主处理ViewModel,适合需要对ViewModel做额外逻辑处理的场景。
  • 方案二:代码更简洁,无需修改页面后台,但依赖主窗口实例的存在,耦合度稍高,适合简单绑定场景。

内容的提问来源于stack exchange,提问作者Ming

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 22:17:35