Add chapter 6 samples

This commit is contained in:
Alvin Ashcraft
2023-07-02 12:49:25 -04:00
parent 370d8a52b8
commit 7d9fe9fb0f
62 changed files with 2566 additions and 0 deletions
@@ -0,0 +1,43 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33530.505
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyMediaCollection", "MyMediaCollection\MyMediaCollection.csproj", "{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|ARM64.ActiveCfg = Debug|ARM64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|ARM64.Build.0 = Debug|ARM64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|ARM64.Deploy.0 = Debug|ARM64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|x64.ActiveCfg = Debug|x64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|x64.Build.0 = Debug|x64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|x64.Deploy.0 = Debug|x64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|x86.ActiveCfg = Debug|x86
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|x86.Build.0 = Debug|x86
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|x86.Deploy.0 = Debug|x86
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|ARM64.ActiveCfg = Release|ARM64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|ARM64.Build.0 = Release|ARM64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|ARM64.Deploy.0 = Release|ARM64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|x64.ActiveCfg = Release|x64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|x64.Build.0 = Release|x64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|x64.Deploy.0 = Release|x64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|x86.ActiveCfg = Release|x86
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|x86.Build.0 = Release|x86
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|x86.Deploy.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C9277197-C949-4948-80CD-0A685EE6DCBB}
EndGlobalSection
EndGlobal
@@ -0,0 +1,18 @@
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
<!-- Licensed under the MIT License. -->
<Application
x:Class="MyMediaCollection.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyMediaCollection">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<!-- Other app resources here -->
</ResourceDictionary>
</Application.Resources>
</Application>
@@ -0,0 +1,72 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using MyMediaCollection.Interfaces;
using MyMediaCollection.Services;
using MyMediaCollection.ViewModels;
using MyMediaCollection.Views;
using System;
using System.Net;
using System.Threading.Tasks;
namespace MyMediaCollection
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : Application
{
public static IHost HostContainer { get; private set; }
/// <summary>
/// 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().
/// </summary>
public App()
{
this.InitializeComponent();
}
/// <summary>
/// Invoked when the application is launched.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected override async void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
m_window = new MainWindow();
var rootFrame = new Frame();
await RegisterComponentsAsync(rootFrame);
rootFrame.NavigationFailed += RootFrame_NavigationFailed;
rootFrame.Navigate(typeof(MainPage), args);
m_window.Content = rootFrame;
m_window.Activate();
}
private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception($"Error loading page {e.SourcePageType.FullName}");
}
private Window m_window;
private async Task RegisterComponentsAsync(Frame rootFrame)
{
var navigationService = new NavigationService(rootFrame);
navigationService.Configure(nameof(MainPage), typeof(MainPage));
navigationService.Configure(nameof(ItemDetailsPage), typeof(ItemDetailsPage));
var dataService = new SqliteDataService();
await dataService.InitializeDataAsync();
HostContainer = Host.CreateDefaultBuilder()
.ConfigureServices(services =>
{
services.AddSingleton<INavigationService>(navigationService);
services.AddSingleton<IDataService>(dataService);
services.AddTransient<MainViewModel>();
services.AddTransient<ItemDetailsViewModel>();
}).Build();
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1,9 @@
namespace MyMediaCollection.Enums
{
public enum ItemType
{
Music,
Video,
Book
}
}
@@ -0,0 +1,8 @@
namespace MyMediaCollection.Enums
{
public enum LocationType
{
InCollection,
Loaned
}
}
@@ -0,0 +1,22 @@
using MyMediaCollection.Enums;
using MyMediaCollection.Model;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MyMediaCollection.Interfaces
{
public interface IDataService
{
Task<IList<MediaItem>> GetItemsAsync();
Task<MediaItem> GetItemAsync(int id);
Task<int> AddItemAsync(MediaItem item);
Task UpdateItemAsync(MediaItem item);
Task DeleteItemAsync(MediaItem item);
IList<ItemType> GetItemTypes();
Medium GetMedium(string name);
IList<Medium> GetMediums();
IList<Medium> GetMediums(ItemType itemType);
IList<LocationType> GetLocationTypes();
Task InitializeDataAsync();
}
}
@@ -0,0 +1,12 @@
using System;
namespace MyMediaCollection.Interfaces
{
public interface INavigationService
{
string CurrentPage { get; }
void NavigateTo(string page);
void NavigateTo(string page, object parameter);
void GoBack();
}
}
@@ -0,0 +1,7 @@
namespace MyMediaCollection.Interfaces
{
public interface IValidatable
{
void Validate(string memberName, object value);
}
}
@@ -0,0 +1,10 @@
<Window
x:Class="MyMediaCollection.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyMediaCollection"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:model="using:MyMediaCollection.Model"
mc:Ignorable="d">
</Window>
@@ -0,0 +1,15 @@
using Microsoft.UI.Xaml;
namespace MyMediaCollection
{
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
}
}
}
@@ -0,0 +1,17 @@
using Dapper.Contrib.Extensions;
using MyMediaCollection.Enums;
namespace MyMediaCollection.Model
{
public class MediaItem
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public ItemType MediaType { get; set; }
[Computed]
public Medium MediumInfo { get; set; }
public LocationType Location { get; set; }
public int MediumId => MediumInfo.Id;
}
}
@@ -0,0 +1,13 @@
using Dapper.Contrib.Extensions;
using MyMediaCollection.Enums;
namespace MyMediaCollection.Model
{
public class Medium
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public ItemType MediaType { get; set; }
}
}
@@ -0,0 +1,68 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>MyMediaCollection</RootNamespace>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Platforms>x86;x64;ARM64</Platforms>
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
<PublishProfile>win10-$(Platform).pubxml</PublishProfile>
<UseWinUI>true</UseWinUI>
<EnableMsixTooling>true</EnableMsixTooling>
<SupportedOSPlatformVersion>10.0.18362.0</SupportedOSPlatformVersion>
</PropertyGroup>
<ItemGroup>
<None Remove="Views\ItemDetailsPage.xaml" />
<None Remove="Views\MainPage.xaml" />
</ItemGroup>
<ItemGroup>
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.0" />
<PackageReference Include="Dapper" Version="2.0.143" />
<PackageReference Include="Dapper.Contrib" Version="2.0.78" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.8" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221109.1" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />
<Manifest Include="$(ApplicationManifest)" />
</ItemGroup>
<!--
Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
Tools extension to be activated for this project even if the Windows App SDK Nuget
package has not yet been restored.
-->
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
<ProjectCapability Include="Msix" />
</ItemGroup>
<ItemGroup>
<Page Update="Views\MainPage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Views\ItemDetailsPage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<!--
Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution
Explorer "Package and Publish" context menu entry to be enabled for this project even if
the Windows App SDK Nuget package has not yet been restored.
-->
<PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
<HasPackageAndPublishMenu>true</HasPackageAndPublishMenu>
</PropertyGroup>
</Project>
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
<Identity
Name="9457558b-77d0-43f9-b1e4-e6aa9aba05d6"
Publisher="CN=alash"
Version="1.0.0.0" />
<Properties>
<DisplayName>MyMediaCollection</DisplayName>
<PublisherDisplayName>alash</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="MyMediaCollection"
Description="MyMediaCollection"
BackgroundColor="transparent"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>
@@ -0,0 +1,10 @@
{
"profiles": {
"MyMediaCollection (Package)": {
"commandName": "MsixPackage"
},
"MyMediaCollection (Unpackaged)": {
"commandName": "Project"
}
}
}
@@ -0,0 +1,175 @@
using MyMediaCollection.Enums;
using MyMediaCollection.Interfaces;
using MyMediaCollection.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyMediaCollection.Services
{
public class DataService : IDataService
{
private IList<MediaItem> _items;
private IList<ItemType> _itemTypes;
private IList<Medium> _mediums;
private IList<LocationType> _locationTypes;
public async Task InitializeDataAsync()
{
PopulateItemTypes();
PopulateMediums();
PopulateLocationTypes();
PopulateItems();
await Task.Delay(1);
}
private void PopulateItems()
{
var cd = new MediaItem
{
Id = 1,
Name = "Classical Favorites",
MediaType = ItemType.Music,
MediumInfo = _mediums.FirstOrDefault(m => m.Name == "CD"),
Location = LocationType.InCollection
};
var book = new MediaItem
{
Id = 2,
Name = "Classic Fairy Tales",
MediaType = ItemType.Book,
MediumInfo = _mediums.FirstOrDefault(m => m.Name == "Hardcover"),
Location = LocationType.InCollection
};
var bluRay = new MediaItem
{
Id = 3,
Name = "The Mummy",
MediaType = ItemType.Video,
MediumInfo = _mediums.FirstOrDefault(m => m.Name == "Blu Ray"),
Location = LocationType.InCollection
};
_items = new List<MediaItem>
{
cd,
book,
bluRay
};
}
private void PopulateMediums()
{
var cd = new Medium { Id = 1, MediaType = ItemType.Music, Name = "CD" };
var vinyl = new Medium { Id = 2, MediaType = ItemType.Music, Name = "Vinyl" };
var hardcover = new Medium { Id = 3, MediaType = ItemType.Book, Name = "Hardcover" };
var paperback = new Medium { Id = 4, MediaType = ItemType.Book, Name = "Paperback" };
var dvd = new Medium { Id = 5, MediaType = ItemType.Video, Name = "DVD" };
var bluRay = new Medium { Id = 6, MediaType = ItemType.Video, Name = "Blu Ray" };
_mediums = new List<Medium>
{
cd,
vinyl,
hardcover,
paperback,
dvd,
bluRay
};
}
private void PopulateItemTypes()
{
_itemTypes = new List<ItemType>
{
ItemType.Book,
ItemType.Music,
ItemType.Video
};
}
private void PopulateLocationTypes()
{
_locationTypes = new List<LocationType>
{
LocationType.InCollection,
LocationType.Loaned
};
}
public async Task<int> AddItemAsync(MediaItem item)
{
item.Id = _items.Max(i => i.Id) + 1;
_items.Add(item);
await Task.Delay(1);
return item.Id;
}
public async Task<MediaItem> GetItemAsync(int id)
{
await Task.Delay(1);
return _items.FirstOrDefault(i => i.Id == id);
}
public async Task<IList<MediaItem>> GetItemsAsync()
{
await Task.Delay(1);
return _items;
}
public IList<ItemType> GetItemTypes()
{
return _itemTypes;
}
public IList<Medium> GetMediums()
{
return _mediums;
}
public IList<Medium> GetMediums(ItemType itemType)
{
return _mediums
.Where(m => m.MediaType == itemType)
.ToList();
}
public IList<LocationType> GetLocationTypes()
{
return _locationTypes;
}
public async Task UpdateItemAsync(MediaItem item)
{
var idx = -1;
var matchedItem =
(from x in _items
let ind = idx++
where x.Id == item.Id
select ind).FirstOrDefault();
if (idx == -1)
{
throw new Exception("Unable to update item. Item not found in collection.");
}
_items[idx] = item;
await Task.Delay(1);
}
public async Task DeleteItemAsync(MediaItem item)
{
await Task.Delay(1);
throw new NotImplementedException();
}
public Medium GetMedium(string name)
{
return _mediums.FirstOrDefault(m => m.Name == name);
}
}
}
@@ -0,0 +1,84 @@
using Microsoft.UI.Xaml.Controls;
using MyMediaCollection.Interfaces;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace MyMediaCollection.Services
{
public class NavigationService : INavigationService
{
public NavigationService(Frame rootFrame)
{
AppFrame = rootFrame;
}
private readonly IDictionary<string, Type> _pages = new ConcurrentDictionary<string, Type>();
public const string RootPage = "(Root)";
public const string UnknownPage = "(Unknown)";
private static Frame AppFrame;
public void Configure(string page, Type type)
{
if (_pages.Values.Any(v => v == type))
{
throw new ArgumentException($"The {type.Name} view has already been registered under another name.");
}
_pages[page] = type;
}
/// <summary>
/// Gets the name of the currently displayed page.
/// </summary>
public string CurrentPage
{
get
{
var frame = AppFrame;
if (frame.BackStackDepth == 0)
return RootPage;
if (frame.Content == null)
return UnknownPage;
var type = frame.Content.GetType();
if (_pages.Values.All(v => v != type))
return UnknownPage;
var item = _pages.Single(i => i.Value == type);
return item.Key;
}
}
public void NavigateTo(string page)
{
NavigateTo(page, null);
}
public void NavigateTo(string page, object parameter)
{
if (!_pages.ContainsKey(page))
{
throw new ArgumentException($"Unable to find a page registered with the name {page}.");
}
AppFrame.Navigate(_pages[page], parameter);
}
public void GoBack()
{
if (AppFrame?.CanGoBack == true)
{
AppFrame.GoBack();
}
}
}
}
@@ -0,0 +1,300 @@
using Dapper;
using Dapper.Contrib.Extensions;
using Microsoft.Data.Sqlite;
using MyMediaCollection.Enums;
using MyMediaCollection.Interfaces;
using MyMediaCollection.Model;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Windows.Storage;
namespace MyMediaCollection.Services
{
public class SqliteDataService : IDataService
{
private IList<ItemType> _itemTypes;
private IList<Medium> _mediums;
private IList<LocationType> _locationTypes;
private const string DbName = "mediaCollectionData.db";
private async Task<SqliteConnection> GetOpenConnectionAsync()
{
await ApplicationData.Current.LocalFolder.CreateFileAsync(DbName, CreationCollisionOption.OpenIfExists).AsTask();
string dbPath = Path.Combine(ApplicationData.Current.LocalFolder.Path, DbName);
var cn = new SqliteConnection($"Filename={dbPath}");
cn.Open();
return cn;
}
private async Task CreateMediumTableAsync(SqliteConnection db)
{
string tableCommand = @"CREATE TABLE IF NOT
EXISTS Mediums (Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name NVARCHAR(30) NOT NULL,
MediumType INTEGER NOT NULL)";
using var createTable = new SqliteCommand(tableCommand, db);
await createTable.ExecuteNonQueryAsync();
}
private async Task CreateMediaItemTableAsync(SqliteConnection db)
{
string tableCommand = @"CREATE TABLE IF NOT
EXISTS MediaItems (Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name NVARCHAR(1000) NOT NULL,
ItemType INTEGER NOT NULL,
MediumId INTEGER NOT NULL,
LocationType INTEGER,
CONSTRAINT fk_mediums
FOREIGN KEY(MediumId)
REFERENCES Mediums(Id))";
using var createTable = new SqliteCommand(tableCommand, db);
await createTable.ExecuteNonQueryAsync();
}
private async Task InsertMediumAsync(SqliteConnection db, Medium medium)
{
//// IMPLEMENTATION WITHOUT DAPPER
//using var insertCommand = new SqliteCommand
//{
// Connection = db,
// CommandText = "INSERT INTO Mediums VALUES (NULL, @Name, @MediumType);"
//};
//insertCommand.Parameters.AddWithValue("@Name", medium.Name);
//insertCommand.Parameters.AddWithValue("@MediumType", (int)medium.MediaType);
//await insertCommand.ExecuteNonQueryAsync();
var newIds = await db.QueryAsync<long>(
$@"INSERT INTO Mediums
({nameof(medium.Name)}, MediumType)
VALUES
(@{nameof(medium.Name)}, @{nameof(medium.MediaType)});
SELECT last_insert_rowid()", medium);
medium.Id = (int)newIds.First();
}
private async Task<IList<Medium>> GetAllMediumsAsync(SqliteConnection db)
{
//// IMPLEMENTATION WITHOUT DAPPER
//IList<Medium> mediums = new List<Medium>();
//using var selectCommand = new SqliteCommand("SELECT Id, Name, MediumType FROM Mediums", db);
//using SqliteDataReader query = await selectCommand.ExecuteReaderAsync();
//while (query.Read())
//{
// var medium = new Medium
// {
// Id = query.GetInt32(0),
// Name = query.GetString(1),
// MediaType = (ItemType)query.GetInt32(2)
// };
// mediums.Add(medium);
//}
//return mediums;
var mediums =
await db.QueryAsync<Medium>(@"SELECT Id,
Name,
MediumType AS MediaType
FROM Mediums");
return mediums.ToList();
}
private async Task<List<MediaItem>> GetAllMediaItemsAsync(SqliteConnection db)
{
var itemsResult = await db.QueryAsync<MediaItem, Medium, MediaItem>
(
@"SELECT
[MediaItems].[Id],
[MediaItems].[Name],
[MediaItems].[ItemType] AS MediaType,
[MediaItems].[LocationType] AS Location,
[Mediums].[Id],
[Mediums].[Name],
[Mediums].[MediumType] AS MediaType
FROM
[MediaItems]
JOIN
[Mediums]
ON
[Mediums].[Id] = [MediaItems].[MediumId]",
(item, medium) =>
{
item.MediumInfo = medium;
return item;
}
);
return itemsResult.ToList();
}
private async Task<int> InsertMediaItemAsync(SqliteConnection db, MediaItem item)
{
var newIds = await db.QueryAsync<long>(
@"INSERT INTO MediaItems
(Name, ItemType, MediumId, LocationType)
VALUES
(@Name, @MediaType, @MediumId, @Location);
SELECT last_insert_rowid()", item);
return (int)newIds.First();
}
private async Task UpdateMediaItemAsync(SqliteConnection db, MediaItem item)
{
await db.QueryAsync(
@"UPDATE MediaItems
SET Name = @Name,
ItemType = @MediaType,
MediumId = @MediumId,
LocationType = @Location
WHERE Id = @Id;", item);
}
private async Task DeleteMediaItemAsync(SqliteConnection db, int id)
{
await db.DeleteAsync<MediaItem>(new MediaItem { Id = id });
}
private async Task PopulateMediumsAsync(SqliteConnection db)
{
_mediums = await GetAllMediumsAsync(db);
if (_mediums.Count == 0)
{
var cd = new Medium { Id = 1, MediaType = ItemType.Music, Name = "CD" };
var vinyl = new Medium { Id = 2, MediaType = ItemType.Music, Name = "Vinyl" };
var hardcover = new Medium { Id = 3, MediaType = ItemType.Book, Name = "Hardcover" };
var paperback = new Medium { Id = 4, MediaType = ItemType.Book, Name = "Paperback" };
var dvd = new Medium { Id = 5, MediaType = ItemType.Video, Name = "DVD" };
var bluRay = new Medium { Id = 6, MediaType = ItemType.Video, Name = "Blu Ray" };
var mediums = new List<Medium>
{
cd,
vinyl,
hardcover,
paperback,
dvd,
bluRay
};
foreach (var medium in mediums)
{
await InsertMediumAsync(db, medium);
}
_mediums = await GetAllMediumsAsync(db);
}
}
private void PopulateItemTypes()
{
_itemTypes = new List<ItemType>
{
ItemType.Book,
ItemType.Music,
ItemType.Video
};
}
private void PopulateLocationTypes()
{
_locationTypes = new List<LocationType>
{
LocationType.InCollection,
LocationType.Loaned
};
}
public async Task InitializeDataAsync()
{
using (var db = await GetOpenConnectionAsync())
{
await CreateMediumTableAsync(db);
await CreateMediaItemTableAsync(db);
PopulateItemTypes();
await PopulateMediumsAsync(db);
PopulateLocationTypes();
}
}
public async Task<int> AddItemAsync(MediaItem item)
{
using var db = await GetOpenConnectionAsync();
return await InsertMediaItemAsync(db, item);
}
public async Task<MediaItem> GetItemAsync(int id)
{
IList<MediaItem> mediaItems;
using var db = await GetOpenConnectionAsync();
mediaItems = await GetAllMediaItemsAsync(db);
//Filter the list to get the item for our Id
return mediaItems.FirstOrDefault(i => i.Id == id);
}
public async Task<IList<MediaItem>> GetItemsAsync()
{
using var db = await GetOpenConnectionAsync();
return await GetAllMediaItemsAsync(db);
}
public IList<ItemType> GetItemTypes()
{
return _itemTypes;
}
public IList<LocationType> GetLocationTypes()
{
return _locationTypes;
}
public Medium GetMedium(string name)
{
return _mediums.FirstOrDefault(m => m.Name == name);
}
public Medium GetMedium(int id)
{
return _mediums.FirstOrDefault(m => m.Id == id);
}
public IList<Medium> GetMediums()
{
return _mediums;
}
public IList<Medium> GetMediums(ItemType itemType)
{
return _mediums
.Where(m => m.MediaType == itemType)
.ToList();
}
public async Task UpdateItemAsync(MediaItem item)
{
using var db = await GetOpenConnectionAsync();
await UpdateMediaItemAsync(db, item);
}
public async Task DeleteItemAsync(MediaItem item)
{
using var db = await GetOpenConnectionAsync();
await DeleteMediaItemAsync(db, item.Id);
}
}
}
@@ -0,0 +1,142 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MyMediaCollection.Enums;
using MyMediaCollection.Interfaces;
using MyMediaCollection.Model;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace MyMediaCollection.ViewModels
{
public partial class ItemDetailsViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<string> locationTypes = new();
[ObservableProperty]
private ObservableCollection<string> mediums = new();
[ObservableProperty]
private ObservableCollection<string> itemTypes = new();
private int _itemId;
[ObservableProperty]
private string itemName;
[ObservableProperty]
private string selectedMedium;
[ObservableProperty]
private string selectedItemType;
[ObservableProperty]
private string selectedLocation;
[ObservableProperty]
private bool isDirty;
private int _selectedItemId = -1;
protected INavigationService _navigationService;
protected IDataService _dataService;
public ItemDetailsViewModel(INavigationService navigationService, IDataService dataService)
{
_navigationService = navigationService;
_dataService = dataService;
PopulateLists();
}
public void InitializeItemDetailData(int itemId)
{
_selectedItemId = itemId;
IsDirty = false;
}
private void PopulateLists()
{
ItemTypes.Clear();
foreach (string iType in Enum.GetNames(typeof(ItemType)))
ItemTypes.Add(iType);
LocationTypes.Clear();
foreach (string lType in Enum.GetNames(typeof(LocationType)))
LocationTypes.Add(lType);
Mediums = new ObservableCollection<string>();
}
private async Task SaveAsync()
{
MediaItem item;
if (_itemId > 0)
{
item = await _dataService.GetItemAsync(_itemId);
item.Name = ItemName;
item.Location = (LocationType)Enum.Parse(typeof(LocationType), SelectedLocation);
item.MediaType = (ItemType)Enum.Parse(typeof(ItemType), SelectedItemType);
item.MediumInfo = _dataService.GetMedium(SelectedMedium);
await _dataService.UpdateItemAsync(item);
}
else
{
item = new MediaItem
{
Name = ItemName,
Location = (LocationType)Enum.Parse(typeof(LocationType), SelectedLocation),
MediaType = (ItemType)Enum.Parse(typeof(ItemType), SelectedItemType),
MediumInfo = _dataService.GetMedium(SelectedMedium)
};
await _dataService.AddItemAsync(item);
}
}
public async Task SaveItemAndContinueAsync()
{
await SaveAsync();
_itemId = 0;
ItemName = string.Empty;
SelectedMedium = null;
SelectedLocation = null;
SelectedItemType = null;
IsDirty = false;
}
public async Task SaveItemAndReturnAsync()
{
await SaveAsync();
_navigationService.GoBack();
}
partial void OnItemNameChanged(string value)
{
IsDirty = true;
}
partial void OnSelectedMediumChanged(string value)
{
IsDirty = true;
}
partial void OnSelectedItemTypeChanged(string value)
{
IsDirty = true;
Mediums.Clear();
if (!string.IsNullOrWhiteSpace(value))
{
foreach (string med in _dataService.GetMediums((ItemType)Enum.Parse(typeof(ItemType), SelectedItemType)).Select(m => m.Name))
Mediums.Add(med);
}
}
partial void OnSelectedLocationChanged(string value)
{
IsDirty = true;
}
[RelayCommand]
private void Cancel()
{
_navigationService.GoBack();
}
}
}
@@ -0,0 +1,102 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.UI.Xaml.Input;
using MyMediaCollection.Interfaces;
using MyMediaCollection.Model;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
namespace MyMediaCollection.ViewModels
{
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
private string selectedMedium;
[ObservableProperty]
private ObservableCollection<MediaItem> items = new ObservableCollection<MediaItem>();
private ObservableCollection<MediaItem> allItems;
[ObservableProperty]
private ObservableCollection<string> mediums;
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(DeleteCommand))]
private MediaItem selectedMediaItem;
private INavigationService _navigationService;
private IDataService _dataService;
private const string AllMediums = "All";
public MainViewModel(INavigationService navigationService, IDataService dataService)
{
_navigationService = navigationService;
_dataService = dataService;
PopulateDataAsync();
}
public async Task PopulateDataAsync()
{
Items.Clear();
foreach (var item in await _dataService.GetItemsAsync())
{
Items.Add(item);
}
allItems = new ObservableCollection<MediaItem>(Items);
Mediums = new ObservableCollection<string>
{
AllMediums
};
foreach (var itemType in _dataService.GetItemTypes())
{
Mediums.Add(itemType.ToString());
}
SelectedMedium = Mediums[0];
}
partial void OnSelectedMediumChanged(string value)
{
Items.Clear();
foreach (var item in allItems)
{
if (string.IsNullOrWhiteSpace(value)
|| value == "All"
|| value == item.MediaType.ToString())
{
Items.Add(item);
}
}
}
[RelayCommand]
private void AddEdit()
{
var selectedItemId = -1;
if (SelectedMediaItem != null)
{
selectedItemId = SelectedMediaItem.Id;
}
_navigationService.NavigateTo("ItemDetailsPage", selectedItemId);
}
public void ListViewDoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
AddEdit();
}
[RelayCommand(CanExecute = nameof(CanDeleteItem))]
private async Task DeleteAsync()
{
await _dataService.DeleteItemAsync(SelectedMediaItem);
allItems.Remove(SelectedMediaItem);
Items.Remove(SelectedMediaItem);
}
private bool CanDeleteItem() => SelectedMediaItem != null;
}
}
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<Page
x:Class="MyMediaCollection.Views.ItemDetailsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyMediaCollection.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}">
<Page.Resources>
<Style x:Key="AttributeTitleStyle" TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="AttributeValueStyle" TargetType="TextBox">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="8"/>
</Style>
<Style x:Key="AttributeComboxValueStyle" TargetType="ComboBox">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="8"/>
</Style>
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Item Details" FontSize="18" Margin="8"/>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Name:" Style="{StaticResource AttributeTitleStyle}"/>
<TextBox Grid.Column="1" Style="{StaticResource AttributeValueStyle}" Text="{x:Bind ViewModel.ItemName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="Media Type:" Grid.Row="1" Style="{StaticResource AttributeTitleStyle}"/>
<ComboBox Grid.Row="1" Grid.Column="1" Style="{StaticResource AttributeComboxValueStyle}" ItemsSource="{x:Bind ViewModel.ItemTypes}" SelectedValue="{x:Bind ViewModel.SelectedItemType, Mode=TwoWay}"/>
<TextBlock Text="Medium:" Grid.Row="2" Style="{StaticResource AttributeTitleStyle}"/>
<ComboBox Grid.Row="2" Grid.Column="1" Style="{StaticResource AttributeComboxValueStyle}" ItemsSource="{x:Bind ViewModel.Mediums}" SelectedValue="{x:Bind ViewModel.SelectedMedium, Mode=TwoWay}"/>
<TextBlock Text="Location:" Grid.Row="3" Style="{StaticResource AttributeTitleStyle}"/>
<ComboBox Grid.Row="3" Grid.Column="1" Style="{StaticResource AttributeComboxValueStyle}" ItemsSource="{x:Bind ViewModel.LocationTypes}" SelectedValue="{x:Bind ViewModel.SelectedLocation, Mode=TwoWay}"/>
</Grid>
<StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Right">
<SplitButton x:Name="SaveButton"
Content="Save and Return"
Margin="8,8,0,8"
Click="{x:Bind ViewModel.SaveItemAndReturnAsync}"
IsEnabled="{x:Bind ViewModel.IsDirty, Mode=OneWay}">
<SplitButton.Flyout>
<Flyout>
<StackPanel>
<Button Content="Save and Create New"
Click="{x:Bind ViewModel.SaveItemAndContinueAsync}"
IsEnabled="{x:Bind ViewModel.IsDirty, Mode=OneWay}"
Background="Transparent"/>
<Button Content="Save and Return"
Click="{x:Bind ViewModel.SaveItemAndReturnAsync}"
IsEnabled="{x:Bind ViewModel.IsDirty, Mode=OneWay}"
Background="Transparent"/>
</StackPanel>
</Flyout>
</SplitButton.Flyout>
<SplitButton.Resources>
<TeachingTip x:Name="SavingTip"
Target="{x:Bind SaveButton}"
Title="Save and create new"
Subtitle="Use the dropdown button option to save your item and create another.">
</TeachingTip>
</SplitButton.Resources>
</SplitButton>
<Button Content="Cancel" Margin="8" Command="{x:Bind ViewModel.CancelCommand}"/>
</StackPanel>
</Grid>
</Page>
@@ -0,0 +1,49 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using MyMediaCollection.ViewModels;
namespace MyMediaCollection.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class ItemDetailsPage : Page
{
public ItemDetailsPage()
{
ViewModel = App.HostContainer.Services.GetService<ItemDetailsViewModel>();
this.InitializeComponent();
Windows.Storage.ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
// Load the user setting
string haveExplainedSaveSetting = localSettings.Values[nameof(SavingTip)] as string;
// If the user has not seen the save tip, display it
if (!bool.TryParse(haveExplainedSaveSetting, out bool result) || !result)
{
SavingTip.IsOpen = true;
// Save the teaching tip setting
localSettings.Values[nameof(SavingTip)] = "true";
}
}
public ItemDetailsViewModel ViewModel;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var itemId = (int)e.Parameter;
if (itemId > 0)
{
ViewModel.InitializeItemDetailData(itemId);
}
}
}
}
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<Page
x:Class="MyMediaCollection.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyMediaCollection.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="using:MyMediaCollection.Model"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Media Collection" Margin="4" FontWeight="Bold" VerticalAlignment="Center"/>
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock Text="Media Type:" Margin="4" FontWeight="Bold" VerticalAlignment="Center"/>
<ComboBox ItemsSource="{x:Bind ViewModel.Mediums, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedMedium, Mode=TwoWay}" MinWidth="120" Margin="0,2,6,4"/>
</StackPanel>
</Grid>
<ListView Grid.Row="1" ItemsSource="{x:Bind ViewModel.Items}"
SelectedItem="{x:Bind ViewModel.SelectedMediaItem, Mode=TwoWay}"
DoubleTapped="{x:Bind ViewModel.ListViewDoubleTapped}">
<ListView.HeaderTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border BorderBrush="BlueViolet" BorderThickness="0,0,0,1">
<TextBlock Text="Medium" Margin="5,0,0,0" FontWeight="Bold"/>
</Border>
<Border Grid.Column="1" BorderBrush="BlueViolet" BorderThickness="0,0,0,1">
<TextBlock Text="Title" Margin="5,0,0,0" FontWeight="Bold"/>
</Border>
</Grid>
</DataTemplate>
</ListView.HeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:MediaItem">
<Grid IsHitTestVisible="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{x:Bind Path=MediumInfo.Name}"/>
<TextBlock Grid.Column="1" Text="{x:Bind Path=Name}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Grid.Row="2"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button Command="{x:Bind ViewModel.AddEditCommand}"
Content="Add/Edit Item"
Margin="8,8,0,8"/>
<Button Command="{x:Bind ViewModel.DeleteCommand}"
Content="Delete Item"
Margin="8"/>
</StackPanel>
</Grid>
</Page>
@@ -0,0 +1,20 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
using MyMediaCollection.ViewModels;
namespace MyMediaCollection.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
ViewModel = App.HostContainer.Services.GetService<MainViewModel>();
this.InitializeComponent();
}
public MainViewModel ViewModel;
}
}
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyMediaCollection.app"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!--The ID below informs the system that this application is compatible with OS features first introduced in Windows 8.
For more info see https://docs.microsoft.com/windows/win32/sysinfo/targeting-your-application-at-windows-8-1
It is also necessary to support features in unpackaged applications, for example the custom titlebar implementation.-->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect:
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update
-->
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
</windowsSettings>
</application>
</assembly>
+43
View File
@@ -0,0 +1,43 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33530.505
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyMediaCollection", "MyMediaCollection\MyMediaCollection.csproj", "{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|ARM64.ActiveCfg = Debug|ARM64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|ARM64.Build.0 = Debug|ARM64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|ARM64.Deploy.0 = Debug|ARM64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|x64.ActiveCfg = Debug|x64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|x64.Build.0 = Debug|x64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|x64.Deploy.0 = Debug|x64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|x86.ActiveCfg = Debug|x86
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|x86.Build.0 = Debug|x86
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Debug|x86.Deploy.0 = Debug|x86
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|ARM64.ActiveCfg = Release|ARM64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|ARM64.Build.0 = Release|ARM64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|ARM64.Deploy.0 = Release|ARM64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|x64.ActiveCfg = Release|x64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|x64.Build.0 = Release|x64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|x64.Deploy.0 = Release|x64
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|x86.ActiveCfg = Release|x86
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|x86.Build.0 = Release|x86
{972D5C0D-86E6-4A2F-A6FD-8D4FE3380707}.Release|x86.Deploy.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C9277197-C949-4948-80CD-0A685EE6DCBB}
EndGlobalSection
EndGlobal
@@ -0,0 +1,18 @@
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
<!-- Licensed under the MIT License. -->
<Application
x:Class="MyMediaCollection.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyMediaCollection">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<!-- Other app resources here -->
</ResourceDictionary>
</Application.Resources>
</Application>
@@ -0,0 +1,70 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using MyMediaCollection.Interfaces;
using MyMediaCollection.Services;
using MyMediaCollection.ViewModels;
using MyMediaCollection.Views;
using System;
using System.Net;
using System.Threading.Tasks;
namespace MyMediaCollection
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : Application
{
public static IHost HostContainer { get; private set; }
/// <summary>
/// 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().
/// </summary>
public App()
{
this.InitializeComponent();
}
/// <summary>
/// Invoked when the application is launched.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
m_window = new MainWindow();
var rootFrame = new Frame();
RegisterComponents(rootFrame);
rootFrame.NavigationFailed += RootFrame_NavigationFailed;
rootFrame.Navigate(typeof(MainPage), args);
m_window.Content = rootFrame;
m_window.Activate();
}
private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception($"Error loading page {e.SourcePageType.FullName}");
}
private Window m_window;
private void RegisterComponents(Frame rootFrame)
{
var navigationService = new NavigationService(rootFrame);
navigationService.Configure(nameof(MainPage), typeof(MainPage));
navigationService.Configure(nameof(ItemDetailsPage), typeof(ItemDetailsPage));
HostContainer = Host.CreateDefaultBuilder()
.ConfigureServices(services =>
{
services.AddSingleton<INavigationService>(navigationService);
services.AddSingleton<IDataService, DataService>();
services.AddTransient<MainViewModel>();
services.AddTransient<ItemDetailsViewModel>();
}).Build();
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1,9 @@
namespace MyMediaCollection.Enums
{
public enum ItemType
{
Music,
Video,
Book
}
}
@@ -0,0 +1,8 @@
namespace MyMediaCollection.Enums
{
public enum LocationType
{
InCollection,
Loaned
}
}
@@ -0,0 +1,19 @@
using MyMediaCollection.Enums;
using MyMediaCollection.Model;
using System.Collections.Generic;
namespace MyMediaCollection.Interfaces
{
public interface IDataService
{
IList<MediaItem> GetItems();
MediaItem GetItem(int id);
int AddItem(MediaItem item);
void UpdateItem(MediaItem item);
IList<ItemType> GetItemTypes();
Medium GetMedium(string name);
IList<Medium> GetMediums();
IList<Medium> GetMediums(ItemType itemType);
IList<LocationType> GetLocationTypes();
}
}
@@ -0,0 +1,12 @@
using System;
namespace MyMediaCollection.Interfaces
{
public interface INavigationService
{
string CurrentPage { get; }
void NavigateTo(string page);
void NavigateTo(string page, object parameter);
void GoBack();
}
}
@@ -0,0 +1,10 @@
<Window
x:Class="MyMediaCollection.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyMediaCollection"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:model="using:MyMediaCollection.Model"
mc:Ignorable="d">
</Window>
@@ -0,0 +1,15 @@
using Microsoft.UI.Xaml;
namespace MyMediaCollection
{
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
}
}
}
@@ -0,0 +1,13 @@
using MyMediaCollection.Enums;
namespace MyMediaCollection.Model
{
public class MediaItem
{
public int Id { get; set; }
public string Name { get; set; }
public ItemType MediaType { get; set; }
public Medium MediumInfo { get; set; }
public LocationType Location { get; set; }
}
}
@@ -0,0 +1,11 @@
using MyMediaCollection.Enums;
namespace MyMediaCollection.Model
{
public class Medium
{
public int Id { get; set; }
public string Name { get; set; }
public ItemType MediaType { get; set; }
}
}
@@ -0,0 +1,65 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>MyMediaCollection</RootNamespace>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Platforms>x86;x64;ARM64</Platforms>
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
<PublishProfile>win10-$(Platform).pubxml</PublishProfile>
<UseWinUI>true</UseWinUI>
<EnableMsixTooling>true</EnableMsixTooling>
<SupportedOSPlatformVersion>10.0.18362.0</SupportedOSPlatformVersion>
</PropertyGroup>
<ItemGroup>
<None Remove="Views\ItemDetailsPage.xaml" />
<None Remove="Views\MainPage.xaml" />
</ItemGroup>
<ItemGroup>
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221109.1" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />
<Manifest Include="$(ApplicationManifest)" />
</ItemGroup>
<!--
Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
Tools extension to be activated for this project even if the Windows App SDK Nuget
package has not yet been restored.
-->
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
<ProjectCapability Include="Msix" />
</ItemGroup>
<ItemGroup>
<Page Update="Views\MainPage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Views\ItemDetailsPage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<!--
Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution
Explorer "Package and Publish" context menu entry to be enabled for this project even if
the Windows App SDK Nuget package has not yet been restored.
-->
<PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
<HasPackageAndPublishMenu>true</HasPackageAndPublishMenu>
</PropertyGroup>
</Project>
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
<Identity
Name="9457558b-77d0-43f9-b1e4-e6aa9aba05d6"
Publisher="CN=alash"
Version="1.0.0.0" />
<Properties>
<DisplayName>MyMediaCollection</DisplayName>
<PublisherDisplayName>alash</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="MyMediaCollection"
Description="MyMediaCollection"
BackgroundColor="transparent"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>
@@ -0,0 +1,10 @@
{
"profiles": {
"MyMediaCollection (Package)": {
"commandName": "MsixPackage"
},
"MyMediaCollection (Unpackaged)": {
"commandName": "Project"
}
}
}
@@ -0,0 +1,163 @@
using MyMediaCollection.Enums;
using MyMediaCollection.Interfaces;
using MyMediaCollection.Model;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MyMediaCollection.Services
{
public class DataService : IDataService
{
private IList<MediaItem> _items;
private IList<ItemType> _itemTypes;
private IList<Medium> _mediums;
private IList<LocationType> _locationTypes;
public DataService()
{
PopulateItemTypes();
PopulateMediums();
PopulateLocationTypes();
PopulateItems();
}
private void PopulateItems()
{
var cd = new MediaItem
{
Id = 1,
Name = "Classical Favorites",
MediaType = ItemType.Music,
MediumInfo = _mediums.FirstOrDefault(m => m.Name == "CD"),
Location = LocationType.InCollection
};
var book = new MediaItem
{
Id = 2,
Name = "Classic Fairy Tales",
MediaType = ItemType.Book,
MediumInfo = _mediums.FirstOrDefault(m => m.Name == "Hardcover"),
Location = LocationType.InCollection
};
var bluRay = new MediaItem
{
Id = 3,
Name = "The Mummy",
MediaType = ItemType.Video,
MediumInfo = _mediums.FirstOrDefault(m => m.Name == "Blu Ray"),
Location = LocationType.InCollection
};
_items = new List<MediaItem>
{
cd,
book,
bluRay
};
}
private void PopulateMediums()
{
var cd = new Medium { Id = 1, MediaType = ItemType.Music, Name = "CD" };
var vinyl = new Medium { Id = 2, MediaType = ItemType.Music, Name = "Vinyl" };
var hardcover = new Medium { Id = 3, MediaType = ItemType.Book, Name = "Hardcover" };
var paperback = new Medium { Id = 4, MediaType = ItemType.Book, Name = "Paperback" };
var dvd = new Medium { Id = 5, MediaType = ItemType.Video, Name = "DVD" };
var bluRay = new Medium { Id = 6, MediaType = ItemType.Video, Name = "Blu Ray" };
_mediums = new List<Medium>
{
cd,
vinyl,
hardcover,
paperback,
dvd,
bluRay
};
}
private void PopulateItemTypes()
{
_itemTypes = new List<ItemType>
{
ItemType.Book,
ItemType.Music,
ItemType.Video
};
}
private void PopulateLocationTypes()
{
_locationTypes = new List<LocationType>
{
LocationType.InCollection,
LocationType.Loaned
};
}
public int AddItem(MediaItem item)
{
item.Id = _items.Max(i => i.Id) + 1;
_items.Add(item);
return item.Id;
}
public MediaItem GetItem(int id)
{
return _items.FirstOrDefault(i => i.Id == id);
}
public IList<MediaItem> GetItems()
{
return _items;
}
public IList<ItemType> GetItemTypes()
{
return _itemTypes;
}
public IList<Medium> GetMediums()
{
return _mediums;
}
public IList<Medium> GetMediums(ItemType itemType)
{
return _mediums
.Where(m => m.MediaType == itemType)
.ToList();
}
public IList<LocationType> GetLocationTypes()
{
return _locationTypes;
}
public void UpdateItem(MediaItem item)
{
var idx = -1;
var matchedItem =
(from x in _items
let ind = idx++
where x.Id == item.Id
select ind).FirstOrDefault();
if (idx == -1)
{
throw new Exception("Unable to update item. Item not found in collection.");
}
_items[idx] = item;
}
public Medium GetMedium(string name)
{
return _mediums.FirstOrDefault(m => m.Name == name);
}
}
}
@@ -0,0 +1,84 @@
using Microsoft.UI.Xaml.Controls;
using MyMediaCollection.Interfaces;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace MyMediaCollection.Services
{
public class NavigationService : INavigationService
{
public NavigationService(Frame rootFrame)
{
AppFrame = rootFrame;
}
private readonly IDictionary<string, Type> _pages = new ConcurrentDictionary<string, Type>();
public const string RootPage = "(Root)";
public const string UnknownPage = "(Unknown)";
private static Frame AppFrame;
public void Configure(string page, Type type)
{
if (_pages.Values.Any(v => v == type))
{
throw new ArgumentException($"The {type.Name} view has already been registered under another name.");
}
_pages[page] = type;
}
/// <summary>
/// Gets the name of the currently displayed page.
/// </summary>
public string CurrentPage
{
get
{
var frame = AppFrame;
if (frame.BackStackDepth == 0)
return RootPage;
if (frame.Content == null)
return UnknownPage;
var type = frame.Content.GetType();
if (_pages.Values.All(v => v != type))
return UnknownPage;
var item = _pages.Single(i => i.Value == type);
return item.Key;
}
}
public void NavigateTo(string page)
{
NavigateTo(page, null);
}
public void NavigateTo(string page, object parameter)
{
if (!_pages.ContainsKey(page))
{
throw new ArgumentException($"Unable to find a page registered with the name {page}.");
}
AppFrame.Navigate(_pages[page], parameter);
}
public void GoBack()
{
if (AppFrame?.CanGoBack == true)
{
AppFrame.GoBack();
}
}
}
}
@@ -0,0 +1,161 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MyMediaCollection.Enums;
using MyMediaCollection.Interfaces;
using MyMediaCollection.Model;
using System;
using System.Collections.ObjectModel;
using System.Linq;
namespace MyMediaCollection.ViewModels
{
public partial class ItemDetailsViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<string> locationTypes = new();
[ObservableProperty]
private ObservableCollection<string> mediums = new();
[ObservableProperty]
private ObservableCollection<string> itemTypes = new();
private int _itemId;
[ObservableProperty]
private string itemName;
[ObservableProperty]
private string selectedMedium;
[ObservableProperty]
private string selectedItemType;
[ObservableProperty]
private string selectedLocation;
[ObservableProperty]
private bool isDirty;
private int _selectedItemId = -1;
protected INavigationService _navigationService;
protected IDataService _dataService;
public ItemDetailsViewModel(INavigationService navigationService, IDataService dataService)
{
_navigationService = navigationService;
_dataService = dataService;
PopulateLists();
}
public void InitializeItemDetailData(int itemId)
{
_selectedItemId = itemId;
PopulateExistingItem(_dataService);
IsDirty = false;
}
private void PopulateExistingItem(IDataService dataService)
{
if (_selectedItemId > 0)
{
var item = _dataService.GetItem(_selectedItemId);
Mediums.Clear();
foreach (string medium in dataService.GetMediums(item.MediaType).Select(m => m.Name))
Mediums.Add(medium);
_itemId = item.Id;
ItemName = item.Name;
SelectedMedium = item.MediumInfo.Name;
SelectedLocation = item.Location.ToString();
SelectedItemType = item.MediaType.ToString();
}
}
private void PopulateLists()
{
ItemTypes.Clear();
foreach (string iType in Enum.GetNames(typeof(ItemType)))
ItemTypes.Add(iType);
LocationTypes.Clear();
foreach (string lType in Enum.GetNames(typeof(LocationType)))
LocationTypes.Add(lType);
Mediums = new ObservableCollection<string>();
}
private void Save()
{
MediaItem item;
if (_itemId > 0)
{
item = _dataService.GetItem(_itemId);
item.Name = ItemName;
item.Location = (LocationType)Enum.Parse(typeof(LocationType), SelectedLocation);
item.MediaType = (ItemType)Enum.Parse(typeof(ItemType), SelectedItemType);
item.MediumInfo = _dataService.GetMedium(SelectedMedium);
_dataService.UpdateItem(item);
}
else
{
item = new MediaItem
{
Name = ItemName,
Location = (LocationType)Enum.Parse(typeof(LocationType), SelectedLocation),
MediaType = (ItemType)Enum.Parse(typeof(ItemType), SelectedItemType),
MediumInfo = _dataService.GetMedium(SelectedMedium)
};
_dataService.AddItem(item);
}
}
public void SaveItemAndContinue()
{
Save();
_itemId = 0;
ItemName = string.Empty;
SelectedMedium = null;
SelectedLocation = null;
SelectedItemType = null;
IsDirty = false;
}
public void SaveItemAndReturn()
{
Save();
_navigationService.GoBack();
}
partial void OnItemNameChanged(string value)
{
IsDirty = true;
}
partial void OnSelectedMediumChanged(string value)
{
IsDirty = true;
}
partial void OnSelectedItemTypeChanged(string value)
{
IsDirty = true;
Mediums.Clear();
if (!string.IsNullOrWhiteSpace(value))
{
foreach (string med in _dataService.GetMediums((ItemType)Enum.Parse(typeof(ItemType), SelectedItemType)).Select(m => m.Name))
Mediums.Add(med);
}
}
partial void OnSelectedLocationChanged(string value)
{
IsDirty = true;
}
[RelayCommand]
private void Cancel()
{
_navigationService.GoBack();
}
}
}
@@ -0,0 +1,100 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.UI.Xaml.Input;
using MyMediaCollection.Interfaces;
using MyMediaCollection.Model;
using System.Collections.ObjectModel;
namespace MyMediaCollection.ViewModels
{
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
private string selectedMedium;
[ObservableProperty]
private ObservableCollection<MediaItem> items = new ObservableCollection<MediaItem>();
private ObservableCollection<MediaItem> allItems;
[ObservableProperty]
private ObservableCollection<string> mediums;
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(DeleteCommand))]
private MediaItem selectedMediaItem;
private INavigationService _navigationService;
private IDataService _dataService;
private const string AllMediums = "All";
public MainViewModel(INavigationService navigationService, IDataService dataService)
{
_navigationService = navigationService;
_dataService = dataService;
PopulateData();
}
public void PopulateData()
{
Items.Clear();
foreach (var item in _dataService.GetItems())
{
Items.Add(item);
}
allItems = new ObservableCollection<MediaItem>(Items);
Mediums = new ObservableCollection<string>
{
AllMediums
};
foreach (var itemType in _dataService.GetItemTypes())
{
Mediums.Add(itemType.ToString());
}
SelectedMedium = Mediums[0];
}
partial void OnSelectedMediumChanged(string value)
{
Items.Clear();
foreach (var item in allItems)
{
if (string.IsNullOrWhiteSpace(value)
|| value == "All"
|| value == item.MediaType.ToString())
{
Items.Add(item);
}
}
}
[RelayCommand]
private void AddEdit()
{
var selectedItemId = -1;
if (SelectedMediaItem != null)
{
selectedItemId = SelectedMediaItem.Id;
}
_navigationService.NavigateTo("ItemDetailsPage", selectedItemId);
}
public void ListViewDoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
AddEdit();
}
[RelayCommand(CanExecute = nameof(CanDeleteItem))]
private void Delete()
{
allItems.Remove(SelectedMediaItem);
Items.Remove(SelectedMediaItem);
}
private bool CanDeleteItem() => SelectedMediaItem != null;
}
}
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<Page
x:Class="MyMediaCollection.Views.ItemDetailsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyMediaCollection.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}">
<Page.Resources>
<Style x:Key="AttributeTitleStyle" TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="AttributeValueStyle" TargetType="TextBox">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="8"/>
</Style>
<Style x:Key="AttributeComboxValueStyle" TargetType="ComboBox">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="8"/>
</Style>
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Item Details" FontSize="18" Margin="8"/>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Name:" Style="{StaticResource AttributeTitleStyle}"/>
<TextBox Grid.Column="1" Style="{StaticResource AttributeValueStyle}" Text="{x:Bind ViewModel.ItemName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="Media Type:" Grid.Row="1" Style="{StaticResource AttributeTitleStyle}"/>
<ComboBox Grid.Row="1" Grid.Column="1" Style="{StaticResource AttributeComboxValueStyle}" ItemsSource="{x:Bind ViewModel.ItemTypes}" SelectedValue="{x:Bind ViewModel.SelectedItemType, Mode=TwoWay}"/>
<TextBlock Text="Medium:" Grid.Row="2" Style="{StaticResource AttributeTitleStyle}"/>
<ComboBox Grid.Row="2" Grid.Column="1" Style="{StaticResource AttributeComboxValueStyle}" ItemsSource="{x:Bind ViewModel.Mediums}" SelectedValue="{x:Bind ViewModel.SelectedMedium, Mode=TwoWay}"/>
<TextBlock Text="Location:" Grid.Row="3" Style="{StaticResource AttributeTitleStyle}"/>
<ComboBox Grid.Row="3" Grid.Column="1" Style="{StaticResource AttributeComboxValueStyle}" ItemsSource="{x:Bind ViewModel.LocationTypes}" SelectedValue="{x:Bind ViewModel.SelectedLocation, Mode=TwoWay}"/>
</Grid>
<StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Right">
<SplitButton x:Name="SaveButton"
Content="Save and Return"
Margin="8,8,0,8"
Click="{x:Bind ViewModel.SaveItemAndReturn}"
IsEnabled="{x:Bind ViewModel.IsDirty, Mode=OneWay}">
<SplitButton.Flyout>
<Flyout>
<StackPanel>
<Button Content="Save and Create New"
Click="{x:Bind ViewModel.SaveItemAndContinue}"
IsEnabled="{x:Bind ViewModel.IsDirty, Mode=OneWay}"
Background="Transparent"/>
<Button Content="Save and Return"
Click="{x:Bind ViewModel.SaveItemAndReturn}"
IsEnabled="{x:Bind ViewModel.IsDirty, Mode=OneWay}"
Background="Transparent"/>
</StackPanel>
</Flyout>
</SplitButton.Flyout>
<SplitButton.Resources>
<TeachingTip x:Name="SavingTip"
Target="{x:Bind SaveButton}"
Title="Save and create new"
Subtitle="Use the dropdown button option to save your item and create another.">
</TeachingTip>
</SplitButton.Resources>
</SplitButton>
<Button Content="Cancel" Margin="8" Command="{x:Bind ViewModel.CancelCommand}"/>
</StackPanel>
</Grid>
</Page>
@@ -0,0 +1,49 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using MyMediaCollection.ViewModels;
namespace MyMediaCollection.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class ItemDetailsPage : Page
{
public ItemDetailsPage()
{
ViewModel = App.HostContainer.Services.GetService<ItemDetailsViewModel>();
this.InitializeComponent();
Windows.Storage.ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
// Load the user setting
string haveExplainedSaveSetting = localSettings.Values[nameof(SavingTip)] as string;
// If the user has not seen the save tip, display it
if (!bool.TryParse(haveExplainedSaveSetting, out bool result) || !result)
{
SavingTip.IsOpen = true;
// Save the teaching tip setting
localSettings.Values[nameof(SavingTip)] = "true";
}
}
public ItemDetailsViewModel ViewModel;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var itemId = (int)e.Parameter;
if (itemId > 0)
{
ViewModel.InitializeItemDetailData(itemId);
}
}
}
}
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<Page
x:Class="MyMediaCollection.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyMediaCollection.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="using:MyMediaCollection.Model"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Media Collection" Margin="4" FontWeight="Bold" VerticalAlignment="Center"/>
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock Text="Media Type:" Margin="4" FontWeight="Bold" VerticalAlignment="Center"/>
<ComboBox ItemsSource="{x:Bind ViewModel.Mediums}" SelectedItem="{x:Bind ViewModel.SelectedMedium, Mode=TwoWay}" MinWidth="120" Margin="0,2,6,4"/>
</StackPanel>
</Grid>
<ListView Grid.Row="1" ItemsSource="{x:Bind ViewModel.Items}"
SelectedItem="{x:Bind ViewModel.SelectedMediaItem, Mode=TwoWay}"
DoubleTapped="{x:Bind ViewModel.ListViewDoubleTapped}">
<ListView.HeaderTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border BorderBrush="BlueViolet" BorderThickness="0,0,0,1">
<TextBlock Text="Medium" Margin="5,0,0,0" FontWeight="Bold"/>
</Border>
<Border Grid.Column="1" BorderBrush="BlueViolet" BorderThickness="0,0,0,1">
<TextBlock Text="Title" Margin="5,0,0,0" FontWeight="Bold"/>
</Border>
</Grid>
</DataTemplate>
</ListView.HeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:MediaItem">
<Grid IsHitTestVisible="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{x:Bind Path=MediumInfo.Name}"/>
<TextBlock Grid.Column="1" Text="{x:Bind Path=Name}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Grid.Row="2"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button Command="{x:Bind ViewModel.AddEditCommand}"
Content="Add/Edit Item"
Margin="8,8,0,8"/>
<Button Command="{x:Bind ViewModel.DeleteCommand}"
Content="Delete Item"
Margin="8"/>
</StackPanel>
</Grid>
</Page>
@@ -0,0 +1,20 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
using MyMediaCollection.ViewModels;
namespace MyMediaCollection.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
ViewModel = App.HostContainer.Services.GetService<MainViewModel>();
this.InitializeComponent();
}
public MainViewModel ViewModel;
}
}
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyMediaCollection.app"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!--The ID below informs the system that this application is compatible with OS features first introduced in Windows 8.
For more info see https://docs.microsoft.com/windows/win32/sysinfo/targeting-your-application-at-windows-8-1
It is also necessary to support features in unpackaged applications, for example the custom titlebar implementation.-->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect:
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update
-->
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
</windowsSettings>
</application>
</assembly>