基于MVVM模式的WinUI3框架页面间数据共享方案咨询
WinUI3页面间共享选中影片数据的正确实现方式
当前代码存在的核心问题:MediaCenterPage和PlayerPage各自在XAML中实例化了MediaModel,导致两个页面的DataContext是独立的对象实例,无法共享选中数据;同时用静态字段SelectedMovie传递数据不符合MVVM规范,还可能引发内存泄漏风险。以下是三种可行的解决方式:
方案一:导航时直接传递选中对象
利用WinUI的Frame导航传参能力,直接将选中的MediaObject实例传给PlayerPage,是最直接的实现方式。
修改MediaCenterPage的导航逻辑
// MediaCenterPage.xaml.cs private void MediaCenterItemView_PointerPressed(object sender, PointerRoutedEventArgs e) { if (MediaCenterItemView.SelectedItem is MediaObject selectedMovie) { Frame.Navigate(typeof(PlayerPage), selectedMovie); } }
PlayerPage接收并使用参数
// PlayerPage.xaml.cs public sealed partial class PlayerPage : Page { private MediaObject _selectedMovie; public PlayerPage() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); _selectedMovie = e.Parameter as MediaObject; if (_selectedMovie != null) { MediaPlayer.Source = _selectedMovie.Movie; } } private void BackButton_Click(object sender, RoutedEventArgs e) { MainPage.MainFrame.Navigate(typeof(MediaCenterPage)); } }
方案二:使用单一共享的ViewModel实例
创建全局唯一的MediaModel实例,让两个页面绑定同一个ViewModel,选中数据的变更会自动同步。
1. 创建全局ViewModel实例
在App.xaml.cs中定义全局共享实例:
// App.xaml.cs using WinUI3Demo.ViewModels; namespace WinUI3Demo { public partial class App : Application { public static MediaModel SharedMediaModel { get; } = new MediaModel(); // ...其他原有代码 } }
2. 修改页面的DataContext绑定
MediaCenterPage.xaml
替换原有的<Page.DataContext>:
<Page x:Class="WinUI3Demo.Pages.MediaCenter.MediaCenterPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:WinUI3Demo" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" mc:Ignorable="d"> <Page.DataContext> <Binding Source="{x:Bind local:App.SharedMediaModel}" /> </Page.DataContext> <!-- 原有页面内容 --> </Page>
PlayerPage.xaml同理修改:
<Page x:Class="WinUI3Demo.Pages.MediaCenter.PlayerPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:WinUI3Demo" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" mc:Ignorable="d"> <Page.DataContext> <Binding Source="{x:Bind local:App.SharedMediaModel}" /> </Page.DataContext> <!-- 原有页面内容 --> </Page>
3. 更新选中逻辑到ViewModel
// MediaCenterPage.xaml.cs private void ItemsView_SelectionChanged(ItemsView sender, ItemsViewSelectionChangedEventArgs args) { if (MediaCenterItemView.SelectedItem is MediaObject selectedMovie) { HeaderTextBlock.Text = selectedMovie.Title; ContentTextBlock.Text = selectedMovie.Description; App.SharedMediaModel.SelectedMovie = selectedMovie; } }
4. PlayerPage绑定选中项的媒体源
修改PlayerPage的MediaPlayerElement绑定:
<MediaPlayerElement x:Name="MediaPlayer" AreTransportControlsEnabled="True" AutoPlay="True" Source="{x:Bind SelectedMovie.Movie, Mode=OneWay}" />
方案三:使用消息传递(适合复杂场景)
如果页面层级复杂或不想依赖全局实例/导航参数,可以用Microsoft.Toolkit.Mvvm的消息传递功能实现解耦。
1. 安装NuGet包
安装Microsoft.Toolkit.Mvvm包。
2. 定义消息类
// ViewModels/Messages/MediaSelectedMessage.cs namespace WinUI3Demo.ViewModels.Messages { public class MediaSelectedMessage { public MediaObject SelectedMovie { get; } public MediaSelectedMessage(MediaObject selectedMovie) { SelectedMovie = selectedMovie; } } }
3. MediaCenterPage发送消息
// MediaCenterPage.xaml.cs using Microsoft.Toolkit.Mvvm.Messaging; using WinUI3Demo.ViewModels.Messages; private void MediaCenterItemView_PointerPressed(object sender, PointerRoutedEventArgs e) { if (MediaCenterItemView.SelectedItem is MediaObject selectedMovie) { WeakReferenceMessenger.Default.Send(new MediaSelectedMessage(selectedMovie)); Frame.Navigate(typeof(PlayerPage)); } }
4. PlayerPage接收消息
// PlayerPage.xaml.cs using Microsoft.Toolkit.Mvvm.Messaging; using WinUI3Demo.ViewModels.Messages; public sealed partial class PlayerPage : Page { private MediaObject _selectedMovie; private IDisposable _messageSubscription; public PlayerPage() { this.InitializeComponent(); _messageSubscription = WeakReferenceMessenger.Default.Register<MediaSelectedMessage>(this, (recipient, message) => { _selectedMovie = message.SelectedMovie; MediaPlayer.Source = _selectedMovie.Movie; }); } protected override void OnNavigatedFrom(NavigationEventArgs e) { base.OnNavigatedFrom(e); _messageSubscription?.Dispose(); } private void BackButton_Click(object sender, RoutedEventArgs e) { MainPage.MainFrame.Navigate(typeof(MediaCenterPage)); } }
内容的提问来源于stack exchange,提问作者miniHessel
相关产品推荐
相关产品推荐

