👔 💄 🖖 🚧 WIP MVVM album detail

pull/1/head
Alexis Drai 2 years ago
parent 4201f8f766
commit c36734dbd7

@ -3,6 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net7.0-android;net7.0-ios;net7.0-maccatalyst</TargetFrameworks> <TargetFrameworks>net7.0-android;net7.0-ios;net7.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net7.0-windows10.0.19041.0</TargetFrameworks> <TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net7.0-windows10.0.19041.0</TargetFrameworks>
<Nullable>enable</Nullable>
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET --> <!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
<!-- <TargetFrameworks>$(TargetFrameworks);net7.0-tizen</TargetFrameworks> --> <!-- <TargetFrameworks>$(TargetFrameworks);net7.0-tizen</TargetFrameworks> -->
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
@ -52,9 +53,14 @@
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<MauiXaml Update="Views\AlbumPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Pages\" /> <Folder Include="Pages\" />
<Folder Include="Views\" />
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -2,10 +2,10 @@
public partial class App : Application public partial class App : Application
{ {
public App() public App()
{ {
InitializeComponent(); InitializeComponent();
MainPage = new AppShell(); MainPage = new AppShell();
} }
} }

@ -2,8 +2,8 @@
public partial class AppShell : Shell public partial class AppShell : Shell
{ {
public AppShell() public AppShell()
{ {
InitializeComponent(); InitializeComponent();
} }
} }

@ -3,39 +3,10 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="AD_MAUI.MainPage"> x:Class="AD_MAUI.MainPage">
<ScrollView> <Button
<VerticalStackLayout x:Name="RdmBtn"
Spacing="25" Text="DO IT"
Padding="30,0" Clicked="OnRdmBtnClicked"
VerticalOptions="Center"> />
<Image
Source="dotnet_bot.png"
SemanticProperties.Description="Cute dot net bot waving hi to you!"
HeightRequest="200"
HorizontalOptions="Center" />
<Label
Text="Hello, World!"
SemanticProperties.HeadingLevel="Level1"
FontSize="32"
HorizontalOptions="Center" />
<Label
Text="Welcome to .NET Multi-platform App UI"
SemanticProperties.HeadingLevel="Level2"
SemanticProperties.Description="Welcome to dot net Multi platform App U I"
FontSize="18"
HorizontalOptions="Center" />
<Button
x:Name="CounterBtn"
Text="Click me"
SemanticProperties.Hint="Counts the number of times you click"
Clicked="OnCounterClicked"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ScrollView>
</ContentPage> </ContentPage>

@ -1,24 +1,20 @@
namespace AD_MAUI; using AD_MAUI.ViewModel;
using AD_MAUI.Views;
namespace AD_MAUI;
public partial class MainPage : ContentPage public partial class MainPage : ContentPage
{ {
int count = 0; public MainPage()
{
public MainPage() InitializeComponent();
{ }
InitializeComponent();
}
private void OnCounterClicked(object sender, EventArgs e)
{
count++;
if (count == 1) // TODO use commands (or navigation methods?) in VMApp instead
CounterBtn.Text = $"Clicked {count} time"; private void OnRdmBtnClicked(object sender, EventArgs e)
else {
CounterBtn.Text = $"Clicked {count} times"; Navigation.PushAsync(new AlbumPage(new AlbumViewModel(null)));
}
SemanticScreenReader.Announce(CounterBtn.Text);
}
} }

@ -4,21 +4,21 @@ namespace AD_MAUI;
public static class MauiProgram public static class MauiProgram
{ {
public static MauiApp CreateMauiApp() public static MauiApp CreateMauiApp()
{ {
var builder = MauiApp.CreateBuilder(); var builder = MauiApp.CreateBuilder();
builder builder
.UseMauiApp<App>() .UseMauiApp<App>()
.ConfigureFonts(fonts => .ConfigureFonts(fonts =>
{ {
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
}); });
#if DEBUG #if DEBUG
builder.Logging.AddDebug(); builder.Logging.AddDebug();
#endif #endif
return builder.Build(); return builder.Build();
} }
} }

@ -0,0 +1,12 @@
namespace AD_MAUI.Model
{
public class Album
{
public int Id { get; set; }
public string Title { get; set; }
public string Artist { get; set; }
public string CoverImage { get; set; }
public List<Song> Songs { get; set; }
}
}

@ -0,0 +1,9 @@
namespace AD_MAUI.Model
{
public class Song
{
public int Id { get; set; }
public string Title { get; set; }
public int Duration { get; set; }
}
}

@ -1,6 +1,5 @@
using Android.App; using Android.App;
using Android.Content.PM; using Android.Content.PM;
using Android.OS;
namespace AD_MAUI; namespace AD_MAUI;

@ -6,10 +6,10 @@ namespace AD_MAUI;
[Application] [Application]
public class MainApplication : MauiApplication public class MainApplication : MauiApplication
{ {
public MainApplication(IntPtr handle, JniHandleOwnership ownership) public MainApplication(IntPtr handle, JniHandleOwnership ownership)
: base(handle, ownership) : base(handle, ownership)
{ {
} }
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
} }

@ -5,5 +5,5 @@ namespace AD_MAUI;
[Register("AppDelegate")] [Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate public class AppDelegate : MauiUIApplicationDelegate
{ {
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
} }

@ -1,15 +1,14 @@
using ObjCRuntime; using UIKit;
using UIKit;
namespace AD_MAUI; namespace AD_MAUI;
public class Program public class Program
{ {
// This is the main entry point of the application. // This is the main entry point of the application.
static void Main(string[] args) static void Main(string[] args)
{ {
// if you want to use a different Application Delegate class from "AppDelegate" // if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here. // you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate)); UIApplication.Main(args, null, typeof(AppDelegate));
} }
} }

@ -1,6 +1,4 @@
using Microsoft.UI.Xaml; // To learn more about WinUI, the WinUI project structure,
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info. // and more about our project templates, see: http://aka.ms/winui-project-info.
namespace AD_MAUI.WinUI; namespace AD_MAUI.WinUI;
@ -10,15 +8,15 @@ namespace AD_MAUI.WinUI;
/// </summary> /// </summary>
public partial class App : MauiWinUIApplication public partial class App : MauiWinUIApplication
{ {
/// <summary> /// <summary>
/// Initializes the singleton application object. This is the first line of authored code /// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain(). /// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary> /// </summary>
public App() public App()
{ {
this.InitializeComponent(); this.InitializeComponent();
} }
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
} }

@ -5,5 +5,5 @@ namespace AD_MAUI;
[Register("AppDelegate")] [Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate public class AppDelegate : MauiUIApplicationDelegate
{ {
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
} }

@ -1,15 +1,14 @@
using ObjCRuntime; using UIKit;
using UIKit;
namespace AD_MAUI; namespace AD_MAUI;
public class Program public class Program
{ {
// This is the main entry point of the application. // This is the main entry point of the application.
static void Main(string[] args) static void Main(string[] args)
{ {
// if you want to use a different Application Delegate class from "AppDelegate" // if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here. // you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate)); UIApplication.Main(args, null, typeof(AppDelegate));
} }
} }

@ -0,0 +1,86 @@
using AD_MAUI.Model;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace AD_MAUI.ViewModel
{
// TODO make sure not to notify change if not necessary!
public class AlbumViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private readonly Album album = new();
public int Id { get => album.Id; }
public string Title
{
get => album.Title;
set
{
if (album.Title != value)
{
album.Title = value;
OnPropertyChanged();
}
}
}
public string Artist
{
get => album.Artist;
set
{
if (album.Artist != value)
{
album.Artist = value;
OnPropertyChanged();
}
}
}
public string CoverImage
{
get => album.CoverImage;
set
{
if (album.CoverImage != value)
{
album.CoverImage = value;
OnPropertyChanged();
}
}
}
public ReadOnlyObservableCollection<SongViewModel> Songs { get => new(songs); }
private readonly ObservableCollection<SongViewModel> songs;
public AlbumViewModel(Album? album)
{
// Mocked data for testing
this.album = album ?? new Album
{
Id = 1,
Title = "Test Album",
Artist = "Test Artist",
CoverImage = "dotnet_bot.png", // make this a path?
Songs = new List<Song>
{
new Song { Id = 1, Title = "Test Song 1", Duration = 210 },
new Song { Id = 2, Title = "Test Song 2", Duration = 260 },
new Song { Id = 3, Title = "Test Song 3", Duration = 817 },
}
};
songs = new ObservableCollection<SongViewModel>(this.album.Songs.Select(song => new SongViewModel(song)));
}
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

@ -0,0 +1,51 @@
using AD_MAUI.Model;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace AD_MAUI.ViewModel
{
// TODO make sure not to notify change if not necessary!
public class SongViewModel : INotifyPropertyChanged
{
private readonly Song song;
public event PropertyChangedEventHandler? PropertyChanged;
public string Title
{
get => song.Title;
set
{
if (song.Title != value)
{
song.Title = value;
OnPropertyChanged();
}
}
}
public int Duration
{
get => song.Duration;
set
{
if (song.Duration != value)
{
song.Duration = value;
OnPropertyChanged();
}
}
}
public SongViewModel(Song? song)
{
this.song = song ?? new();
}
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:AD_MAUI.ViewModel"
x:Class="AD_MAUI.Views.AlbumPage"
x:DataType="local:AlbumViewModel">
<ScrollView>
<StackLayout>
<Image Source="{Binding CoverImage}" Aspect="AspectFit" />
<Label Text="{Binding Title}" FontSize="Large" />
<Label Text="{Binding Artist}" FontSize="Medium" />
<ListView ItemsSource="{Binding Songs}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:SongViewModel">
<TextCell
Text="{Binding Title}"
Detail="{Binding Duration, StringFormat='\{0\} second(s)'}"
/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ScrollView>
</ContentPage>

@ -0,0 +1,17 @@
using AD_MAUI.ViewModel;
namespace AD_MAUI.Views
{
public partial class AlbumPage : ContentPage
{
private readonly AlbumViewModel viewModel;
public AlbumPage(AlbumViewModel? albumViewModel)
{
InitializeComponent();
viewModel = albumViewModel ?? new AlbumViewModel(null);
BindingContext = viewModel;
}
}
}
Loading…
Cancel
Save