Reputation: 173
I'm building a radio app, which contain NavigationView
with one Page (for now), which would be ExploreCountriesPage
, but I have problem achieving this.
The MainWindow.xaml
is the main window which really should deal the navigation and should show the content of child pages in the frame
element.
Unfortunately, when I'm trying to display the ExploreCountriesPage
in the frame of MainWindow.xaml I get Object reference not set to an instance of an object error.
Ultimately, what I want to achieve is to show a page inside a frame of main window and fetch the data as soon as page shows.
How can I achieve this? What am I doing wrong?
public partial class App : Application
{
public IServiceProvider Container { get; private set; }
public App()
{
this.InitializeComponent();
}
private IServiceProvider RegisterServices()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<IRadioBrowserClient>(new RadioBrowserClient(apiUrl: "de1.api.radio-browser.info"));
serviceCollection.AddSingleton<ExploreCountriesViewModel>();
return serviceCollection.BuildServiceProvider();
}
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
Container = RegisterServices();
m_window = new MainWindow();
m_window.Activate();
}
private Window m_window;
}
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
ContentFrame.Navigate(typeof(ExploreCountriesPage)); // <-- this line causes the error
}
}
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="Rad.io.Client.WinUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Rad.io.Client.WinUI"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:views="using:Rad.io.Client.WinUI.Views">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid x:Name="AppTitleBar">
<Image Source="Images/WindowIcon.png"
HorizontalAlignment="Left"
Width="16" Height="16"
Margin="8,0"/>
<TextBlock x:Name="AppTitleTextBlock" Text="Rad.io"
TextWrapping="NoWrap"
Style="{StaticResource CaptionTextBlockStyle}"
VerticalAlignment="Center"
Margin="28,0,0,0"/>
</Grid>
<NavigationView x:Name="NavigationView"
SelectionChanged="NavigationView_SelectionChanged"
Header="Explore"
Grid.Row="1"
IsBackButtonVisible="Collapsed"
IsSettingsVisible="True">
<NavigationView.MenuItems>
<NavigationViewItem Icon="Globe" Content="Explore" Tag="Explore" />
<NavigationViewItem Icon="Library" Content="Library" Tag="SamplePage2" />
<NavigationViewItem Icon="Play" Content="Now Playing" Tag="SamplePage3" />
</NavigationView.MenuItems>
<Frame x:Name="ContentFrame"/>
</NavigationView>
</Grid>
</Window>
namespace Rad.io.Client.WinUI.ViewModels
{
public class ExploreCountriesViewModel : INotifyPropertyChanged
{
private readonly IRadioBrowserClient radioBrowserClient;
private List<NameAndCount> _countries;
private List<NameAndCount> filteredCountries;
private string entryQuery;
public List<NameAndCount> Countries
{
get => _countries;
set
{
_countries = value;
RaisePropertyChanged();
}
}
public List<NameAndCount> FilteredCountries
{
get
{
if (EntryQuery is null) return Countries;
return Countries.Where(value => value.Name.Contains(EntryQuery, StringComparison.OrdinalIgnoreCase)).ToList();
}
}
public string EntryQuery
{
get => entryQuery;
set
{
entryQuery = value;
RaisePropertyChanged();
}
}
public ExploreCountriesViewModel(IRadioBrowserClient radioBrowserClient)
{
this.radioBrowserClient = radioBrowserClient;
InitializeDataAsync();
}
public async Task InitializeDataAsync()
{
try
{
Countries = await radioBrowserClient.Lists.GetCountriesAsync();
foreach (var result in Countries)
{
Debug.WriteLine(result.Name);
}
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public sealed partial class ExploreCountriesPage : Page
{
public ExploreCountriesViewModel CountriesViewModel { get; set; }
public ExploreCountriesPage(ExploreCountriesViewModel exploreCountriesViewModel)
{
this.InitializeComponent();
this.CountriesViewModel = exploreCountriesViewModel;
DataContext = CountriesViewModel;
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
await CountriesViewModel.InitializeDataAsync();
}
}
<Page
x:Class="Rad.io.Client.WinUI.Views.ExploreCountriesPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Rad.io.Client.WinUI.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<TextBlock Text="Content"
Style="{ThemeResource TitleTextBlockStyle}"
Margin="32,0,0,0"/>
<ListView BorderThickness="1"
ItemsSource="{x:Bind CountriesViewModel.FilteredCountries}"
BorderBrush="{ThemeResource SystemControlForegroundBaseMediumLowBrush}"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"/>
</StackPanel>
</Page>
Upvotes: 1
Views: 934
Reputation: 13213
I guess you get the error because the service provider it's not responsible of instantiating ExploreCountiresPage.
ContentFrame.Navigate(typeof(ExploreCountriesPage));
the navigation above needs a parameterless constructor.
One way to do what you are trying to do is using something like this, and get the ViewModel inside the constructor.
Upvotes: 2