From 5b3fd2a30e784e2b6470e9bdac212a81e14df5c4 Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 30 Aug 2024 16:25:04 -0700 Subject: [PATCH 1/3] (#21) Reset of WPF sample --- samples/todoapp/Samples.TodoApp.sln | 20 +-- samples/todoapp/TodoApp.MAUI/MainPage.xaml.cs | 1 - samples/todoapp/TodoApp.WPF/App.xaml | 9 -- samples/todoapp/TodoApp.WPF/App.xaml.cs | 85 ------------- samples/todoapp/TodoApp.WPF/AssemblyInfo.cs | 14 -- .../TodoApp.WPF/Database/AppDbContext.cs | 57 --------- .../TodoApp.WPF/Database/IDbInitializer.cs | 23 ---- .../Database/OfflineClientEntity.cs | 19 --- .../todoapp/TodoApp.WPF/Database/TodoItem.cs | 16 --- .../todoapp/TodoApp.WPF/Images/AddItem.png | Bin 373 -> 0 bytes .../TodoApp.WPF/Images/RefreshItems.png | Bin 866 -> 0 bytes samples/todoapp/TodoApp.WPF/MainWindow.xaml | 85 ------------- .../todoapp/TodoApp.WPF/MainWindow.xaml.cs | 28 ---- .../TodoApp.WPF/Services/LoggingHandler.cs | 43 ------- .../todoapp/TodoApp.WPF/TodoApp.WPF.csproj | 31 ----- .../ViewModels/NotificationEventArgs.cs | 9 -- .../ViewModels/TodoListViewModel.cs | 120 ------------------ 17 files changed, 1 insertion(+), 559 deletions(-) delete mode 100644 samples/todoapp/TodoApp.WPF/App.xaml delete mode 100644 samples/todoapp/TodoApp.WPF/App.xaml.cs delete mode 100644 samples/todoapp/TodoApp.WPF/AssemblyInfo.cs delete mode 100644 samples/todoapp/TodoApp.WPF/Database/AppDbContext.cs delete mode 100644 samples/todoapp/TodoApp.WPF/Database/IDbInitializer.cs delete mode 100644 samples/todoapp/TodoApp.WPF/Database/OfflineClientEntity.cs delete mode 100644 samples/todoapp/TodoApp.WPF/Database/TodoItem.cs delete mode 100644 samples/todoapp/TodoApp.WPF/Images/AddItem.png delete mode 100644 samples/todoapp/TodoApp.WPF/Images/RefreshItems.png delete mode 100644 samples/todoapp/TodoApp.WPF/MainWindow.xaml delete mode 100644 samples/todoapp/TodoApp.WPF/MainWindow.xaml.cs delete mode 100644 samples/todoapp/TodoApp.WPF/Services/LoggingHandler.cs delete mode 100644 samples/todoapp/TodoApp.WPF/TodoApp.WPF.csproj delete mode 100644 samples/todoapp/TodoApp.WPF/ViewModels/NotificationEventArgs.cs delete mode 100644 samples/todoapp/TodoApp.WPF/ViewModels/TodoListViewModel.cs diff --git a/samples/todoapp/Samples.TodoApp.sln b/samples/todoapp/Samples.TodoApp.sln index 69781c3a..d11a6d7c 100644 --- a/samples/todoapp/Samples.TodoApp.sln +++ b/samples/todoapp/Samples.TodoApp.sln @@ -9,11 +9,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TodoApp.WinUI3", "TodoApp.W EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Datasync.Client", "..\..\src\CommunityToolkit.Datasync.Client\CommunityToolkit.Datasync.Client.csproj", "{2AC73FBE-9E76-4702-B551-B5884383CC68}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TodoApp.WPF", "TodoApp.WPF\TodoApp.WPF.csproj", "{410D4BBD-5ED7-4BC0-A2CF-547A4784732F}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Datasync.Server", "..\datasync-server\src\Sample.Datasync.Server\Sample.Datasync.Server.csproj", "{E67734DD-B397-4A65-AA50-D62F37EF05DD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TodoApp.MAUI", "TodoApp.MAUI\TodoApp.MAUI.csproj", "{00430043-04C5-4F8F-87A9-98ECC0051808}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TodoApp.MAUI", "TodoApp.MAUI\TodoApp.MAUI.csproj", "{00430043-04C5-4F8F-87A9-98ECC0051808}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -67,22 +65,6 @@ Global {2AC73FBE-9E76-4702-B551-B5884383CC68}.Release|x64.Build.0 = Release|Any CPU {2AC73FBE-9E76-4702-B551-B5884383CC68}.Release|x86.ActiveCfg = Release|Any CPU {2AC73FBE-9E76-4702-B551-B5884383CC68}.Release|x86.Build.0 = Release|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Debug|ARM64.Build.0 = Debug|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Debug|x64.ActiveCfg = Debug|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Debug|x64.Build.0 = Debug|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Debug|x86.ActiveCfg = Debug|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Debug|x86.Build.0 = Debug|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Release|Any CPU.Build.0 = Release|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Release|ARM64.ActiveCfg = Release|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Release|ARM64.Build.0 = Release|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Release|x64.ActiveCfg = Release|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Release|x64.Build.0 = Release|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Release|x86.ActiveCfg = Release|Any CPU - {410D4BBD-5ED7-4BC0-A2CF-547A4784732F}.Release|x86.Build.0 = Release|Any CPU {E67734DD-B397-4A65-AA50-D62F37EF05DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E67734DD-B397-4A65-AA50-D62F37EF05DD}.Debug|Any CPU.Build.0 = Debug|Any CPU {E67734DD-B397-4A65-AA50-D62F37EF05DD}.Debug|ARM64.ActiveCfg = Debug|Any CPU diff --git a/samples/todoapp/TodoApp.MAUI/MainPage.xaml.cs b/samples/todoapp/TodoApp.MAUI/MainPage.xaml.cs index e6a84b10..496fed08 100644 --- a/samples/todoapp/TodoApp.MAUI/MainPage.xaml.cs +++ b/samples/todoapp/TodoApp.MAUI/MainPage.xaml.cs @@ -29,7 +29,6 @@ public void OnListItemTapped(object sender, ItemTappedEventArgs e) { if (e.Item is TodoItem item) { - Debug.WriteLine($"[UI] >>> Item clicked: {item.Id}"); this._viewModel.SelectItemCommand.Execute(item); } diff --git a/samples/todoapp/TodoApp.WPF/App.xaml b/samples/todoapp/TodoApp.WPF/App.xaml deleted file mode 100644 index b4f48400..00000000 --- a/samples/todoapp/TodoApp.WPF/App.xaml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - diff --git a/samples/todoapp/TodoApp.WPF/App.xaml.cs b/samples/todoapp/TodoApp.WPF/App.xaml.cs deleted file mode 100644 index 3bec5789..00000000 --- a/samples/todoapp/TodoApp.WPF/App.xaml.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using System.Windows; -using TodoApp.WPF.Database; -using TodoApp.WPF.ViewModels; - -namespace TodoApp.WPF; - -/// -/// Interaction logic for App.xaml -/// -public partial class App : Application, IDisposable -{ - private readonly SqliteConnection dbConnection; - - /// - /// The IoC service provider - /// - public IServiceProvider Services { get; } - - public App() - { - // Create the connection to the SQLite database - this.dbConnection = new SqliteConnection("Data Source=:memory:"); - this.dbConnection.Open(); - - // Create the IoC Services provider. - Services = new ServiceCollection() - .AddTransient() - .AddScoped() - .AddDbContext(options => options.UseSqlite(this.dbConnection)) - .BuildServiceProvider(); - - // Initialize the database - InitializeDatabase(); - } - - private void InitializeDatabase() - { - // using IServiceScope scope = Ioc.Default.CreateScope(); - using IServiceScope scope = Services.CreateScope(); - IDbInitializer initializer = scope.ServiceProvider.GetRequiredService(); - initializer.Initialize(); - } - - /// - /// A helper method for getting a service from the services collection. - /// - /// - /// You can see this in action in the class. - /// - /// The type of the service. - /// An instance of the service - public static TService GetRequiredService() where TService : notnull - => ((App)App.Current).Services.GetRequiredService(); - - #region IDisposable - private bool hasDisposed; - - protected virtual void Dispose(bool disposing) - { - if (!this.hasDisposed) - { - if (disposing) - { - this.dbConnection.Close(); - } - - this.hasDisposed = true; - } - } - - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - #endregion -} - diff --git a/samples/todoapp/TodoApp.WPF/AssemblyInfo.cs b/samples/todoapp/TodoApp.WPF/AssemblyInfo.cs deleted file mode 100644 index 76e9b08e..00000000 --- a/samples/todoapp/TodoApp.WPF/AssemblyInfo.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Windows; - -[assembly: ThemeInfo( - ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located - //(used if a resource is not found in the page, - // or application resource dictionaries) - ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located - //(used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) -)] diff --git a/samples/todoapp/TodoApp.WPF/Database/AppDbContext.cs b/samples/todoapp/TodoApp.WPF/Database/AppDbContext.cs deleted file mode 100644 index dfd5c460..00000000 --- a/samples/todoapp/TodoApp.WPF/Database/AppDbContext.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using CommunityToolkit.Datasync.Client.Http; -using CommunityToolkit.Datasync.Client.Offline; -using Microsoft.EntityFrameworkCore; -using TodoApp.WPF.Services; - -namespace TodoApp.WPF.Database; - -public class AppDbContext(DbContextOptions options) : DbContext(options) -{ - public DbSet TodoItems => Set(); - - // protected override void OnDatasyncInitialization(DatasyncOfflineOptionsBuilder optionsBuilder) - // { - // HttpClientOptions clientOptions = new() - // { - // Endpoint = new Uri("https://Y.azurewebsites.net/"), - // HttpPipeline = [new LoggingHandler()] - // }; - // _ = optionsBuilder.UseHttpClientOptions(clientOptions); - // } - - public async Task SynchronizeAsync(CancellationToken cancellationToken = default) - { - // PushResult pushResult = await this.PushAsync(cancellationToken); - // if (!pushResult.IsSuccessful) - // { - // throw new ApplicationException($"Push failed: {pushResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}"); - // } - - // PullResult pullResult = await this.PullAsync(cancellationToken); - // if (!pullResult.IsSuccessful) - // { - // throw new ApplicationException($"Pull failed: {pullResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}"); - // } - } -} - -/// -/// Use this class to initialize the database. In this sample, we just create -/// the database using . However, you -/// may want to use migrations. -/// -/// The context for the database. -public class DbContextInitializer(AppDbContext context) : IDbInitializer -{ - /// - public void Initialize() - => context.Database.EnsureCreated(); - - /// - public Task InitializeAsync(CancellationToken cancellationToken = default) - => context.Database.EnsureCreatedAsync(cancellationToken); -} diff --git a/samples/todoapp/TodoApp.WPF/Database/IDbInitializer.cs b/samples/todoapp/TodoApp.WPF/Database/IDbInitializer.cs deleted file mode 100644 index 5be08ffb..00000000 --- a/samples/todoapp/TodoApp.WPF/Database/IDbInitializer.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace TodoApp.WPF.Database; - -/// -/// An interface to initialize a database. -/// -public interface IDbInitializer -{ - /// - /// Synchronously initialize the database. - /// - void Initialize(); - - /// - /// Asynchronously initialize the database. - /// - /// A to observe. - /// A task that resolves when complete. - Task InitializeAsync(CancellationToken cancellationToken = default); -} diff --git a/samples/todoapp/TodoApp.WPF/Database/OfflineClientEntity.cs b/samples/todoapp/TodoApp.WPF/Database/OfflineClientEntity.cs deleted file mode 100644 index bcf22230..00000000 --- a/samples/todoapp/TodoApp.WPF/Database/OfflineClientEntity.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.ComponentModel.DataAnnotations; - -namespace TodoApp.WPF.Database; - -/// -/// An abstract class for working with offline entities. -/// -public abstract class OfflineClientEntity -{ - [Key] - public string Id { get; set; } = Guid.NewGuid().ToString("N"); - public DateTimeOffset? UpdatedAt { get; set; } - public string? Version { get; set; } - public bool Deleted { get; set; } -} diff --git a/samples/todoapp/TodoApp.WPF/Database/TodoItem.cs b/samples/todoapp/TodoApp.WPF/Database/TodoItem.cs deleted file mode 100644 index 1205316e..00000000 --- a/samples/todoapp/TodoApp.WPF/Database/TodoItem.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Text.Json; - -namespace TodoApp.WPF.Database; - -public class TodoItem : OfflineClientEntity -{ - public string Title { get; set; } = string.Empty; - public bool IsComplete { get; set; } = false; - - public override string ToString() - => JsonSerializer.Serialize(this); -} \ No newline at end of file diff --git a/samples/todoapp/TodoApp.WPF/Images/AddItem.png b/samples/todoapp/TodoApp.WPF/Images/AddItem.png deleted file mode 100644 index 336647933fe4599d80f75fdb1fde84a11b007f44..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 373 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmUKs7M+SzC{oH>NS%G|oWRD45dJguM!v-tY$DUh!@P+6=(yLU`z6LcLCBs@Y8vBJ&@uo z@Q5sCVBk9f!i-b3`J{n@L7py-AsP4HUU%d>ARyx4`1bGnHR&53xLR!%yWh>5%)l@= z{qeoeN7j@l9}r~9fGPW`{);2J(GBA);em4n4LvDUbW?Cg~ z4LNmTdO!`5ARB`7(@M${i&7cN%ggmL^RkPR6AM!H@{7`Ezq647Dq`?-^>bP0l+XkK DhrDoO diff --git a/samples/todoapp/TodoApp.WPF/Images/RefreshItems.png b/samples/todoapp/TodoApp.WPF/Images/RefreshItems.png deleted file mode 100644 index 51748eddc844cf9b3171899c9a02611217e23c78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 866 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmUKs7M+SzC{oH>NS%G|oWRD45dJguM!v-tY$DUh!@P+6=(yLU`z6LcLCBs@Y8vBJ&@uo z@Q5sCVBk9f!i-b3`J@>bnCv}W978hhy}fZdOTVI(R zb=+pb{OouK!Mk2Aynz#ExL=qc9&n*mqCoJD_o8E`cNn>SU@N@Hd1RK{+nMX$Mc$}i z;mZ4E<@?o^rLUY^!9b?Uu&2;xahv?7$@({aW&@IFrpSsOi3o zd*6(?Gc*q$Snw;$cxJH8SMF^F%)+mV_f6fB8CE00y!Vy)N@LZ|{-gv84h!|@t(}T? zU5y_V9%OVrS}ZYxk4a815UA>5o3Vs)#>^Uio+oSV0#8OCzwIZ+%zlsI-Ps>-%b`gKpys_^H#~#nWfsbMs+5Fg(uc$1)s&R+E zX@9JlShw!0{4DdNbLSeKRHl4o2!1r>A)gILlKzk1-LBp-!TZnNKb5)2`IqgFpq!5z z$o@;tw_+r)1~PWvHx?cu5?b2o=Ryk8L5XgR;t?oQ^V z6X`M2mxSqEb1}K3Y^g4Lr0&hJv_FU3YbMxiw4Y>?p(|#+UK9lWGw)|yyt15q%7m}o z!1SeB;u=wsl30>zm0Xkxq!^40j0|)QjdTqyLX3>842-OdEwl{`tPBh;#y%HA(U6;; zl9^VCTf+i|_*Fm+k{}y`^V3So6N^$A%FE03GV`*FlM@S4_413-XTP(N0xDwgboFyt I=akR{0ExO?!vFvP diff --git a/samples/todoapp/TodoApp.WPF/MainWindow.xaml b/samples/todoapp/TodoApp.WPF/MainWindow.xaml deleted file mode 100644 index cfcdeca3..00000000 --- a/samples/todoapp/TodoApp.WPF/MainWindow.xaml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/todoapp/TodoApp.WPF/MainWindow.xaml.cs b/samples/todoapp/TodoApp.WPF/MainWindow.xaml.cs deleted file mode 100644 index b60bdbf7..00000000 --- a/samples/todoapp/TodoApp.WPF/MainWindow.xaml.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Windows; -using System.Windows.Input; -using TodoApp.WPF.ViewModels; - -namespace TodoApp.WPF; -/// -/// Interaction logic for MainWindow.xaml -/// -public partial class MainWindow : Window -{ - public MainWindow() - { - InitializeComponent(); - DataContext = App.GetRequiredService(); - } - - protected async void TextboxKeyDownHandler(object sender, KeyEventArgs e) - { - if (e.Key is Key.Return or Key.Enter) - { - await ((TodoListViewModel)DataContext).AddItemAsync(); - } - } -} \ No newline at end of file diff --git a/samples/todoapp/TodoApp.WPF/Services/LoggingHandler.cs b/samples/todoapp/TodoApp.WPF/Services/LoggingHandler.cs deleted file mode 100644 index c9c40bb4..00000000 --- a/samples/todoapp/TodoApp.WPF/Services/LoggingHandler.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Net.Http; - -namespace TodoApp.WPF.Services; - -/// -/// A delegating handler that logs the request/response to stdout. -/// -public class LoggingHandler : DelegatingHandler -{ - public LoggingHandler() : base() - { - } - - public LoggingHandler(HttpMessageHandler innerHandler) : base(innerHandler) - { - } - - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - Debug.WriteLine($"[HTTP] >>> {request.Method} {request.RequestUri}"); - await WriteContentAsync(request.Content, cancellationToken); - - HttpResponseMessage response = await base.SendAsync(request, cancellationToken); - - Debug.WriteLine($"[HTTP] <<< {response.StatusCode} {response.ReasonPhrase}"); - await WriteContentAsync(response.Content, cancellationToken); - - return response; - } - - private static async Task WriteContentAsync(HttpContent? content, CancellationToken cancellationToken = default) - { - if (content != null) - { - Debug.WriteLine($"[HTTP] >>> {await content.ReadAsStringAsync(cancellationToken)}"); - } - } -} diff --git a/samples/todoapp/TodoApp.WPF/TodoApp.WPF.csproj b/samples/todoapp/TodoApp.WPF/TodoApp.WPF.csproj deleted file mode 100644 index 18a5930a..00000000 --- a/samples/todoapp/TodoApp.WPF/TodoApp.WPF.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - WinExe - net8.0-windows - enable - enable - true - - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/todoapp/TodoApp.WPF/ViewModels/NotificationEventArgs.cs b/samples/todoapp/TodoApp.WPF/ViewModels/NotificationEventArgs.cs deleted file mode 100644 index 6d1f93d1..00000000 --- a/samples/todoapp/TodoApp.WPF/ViewModels/NotificationEventArgs.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace TodoApp.WPF.ViewModels; - -internal record NotificationEventArgs(string Title, string Message, bool IsError) -{ -} diff --git a/samples/todoapp/TodoApp.WPF/ViewModels/TodoListViewModel.cs b/samples/todoapp/TodoApp.WPF/ViewModels/TodoListViewModel.cs deleted file mode 100644 index 1acb756f..00000000 --- a/samples/todoapp/TodoApp.WPF/ViewModels/TodoListViewModel.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using CommunityToolkit.Datasync.Client; -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.Input; -using Microsoft.EntityFrameworkCore; -using TodoApp.WPF.Database; - -namespace TodoApp.WPF.ViewModels; - -/// -/// The view model for the TodoListWindow. -/// -public partial class TodoListViewModel(AppDbContext service) : ObservableRecipient -{ - internal event EventHandler? NotificationHandler; - - [ObservableProperty] - private bool isRefreshing = false; - - [ObservableProperty] - private ConcurrentObservableCollection items = new([.. service.TodoItems]); - - [ObservableProperty] - private string title = string.Empty; - - [RelayCommand] - public async Task AddItemAsync(CancellationToken cancellationToken = default) - { - try - { - // Create a new item - TodoItem addition = new() - { - Id = Guid.NewGuid().ToString("N"), - Title = Title - }; - - // Add te item to the database - _ = service.TodoItems.Add(addition); - _ = await service.SaveChangesAsync(cancellationToken); - - // Add the item to the end of the list. - Items.Add(addition); - - // Update the title field ready for ext insertion. - Title = string.Empty; - } - catch (Exception ex) - { - NotificationHandler?.Invoke(this, new NotificationEventArgs(ex.GetType().Name, ex.Message, true)); - } - finally - { - NotificationHandler?.Invoke(this, new NotificationEventArgs("Item Added", "", false)); - } - } - - [RelayCommand] - public async Task EditItemAsync(string itemId, CancellationToken cancellationToken = default) - { - try - { - // Retrieve the item (by ID) from the service. - TodoItem item = await service.TodoItems.FindAsync([itemId], cancellationToken) - ?? throw new ApplicationException($"Item with ID '{itemId}' not found."); - - // Update the item in the database - item.IsComplete = !item.IsComplete; - _ = service.TodoItems.Update(item); - _ = await service.SaveChangesAsync(cancellationToken); - - // Update the item in the list - _ = Items.ReplaceIf(x => x.Id == itemId, item); - } - catch (Exception ex) - { - NotificationHandler?.Invoke(this, new NotificationEventArgs(ex.GetType().Name, ex.Message, true)); - } - finally - { - NotificationHandler?.Invoke(this, new NotificationEventArgs("Item Updated", "", false)); - } - } - - [RelayCommand] - public async Task LoadPageAsync(CancellationToken cancellationToken = default) - { - await RefreshItemsAsync(cancellationToken); - } - - [RelayCommand] - public async Task RefreshItemsAsync(CancellationToken cancellationToken = default) - { - try - { - IsRefreshing = true; - - // Synchronize data with the remote service (if any). - await service.SynchronizeAsync(cancellationToken); - - // Pull all items from the database. - IEnumerable itemsFromDatabase = await service.TodoItems.ToListAsync(cancellationToken); - - // Replace all the items in the collection. - Items.ReplaceAll(itemsFromDatabase); - } - catch (Exception ex) - { - NotificationHandler?.Invoke(this, new NotificationEventArgs(ex.GetType().Name, ex.Message, true)); - } - finally - { - IsRefreshing = false; - NotificationHandler?.Invoke(this, new NotificationEventArgs("Items Refreshed", "", false)); - } - } -} From d62a82ffcdcb41227ba6f5235047bf7247211765 Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 30 Aug 2024 17:14:48 -0700 Subject: [PATCH 2/3] (#21) Initial version of the WPF application. --- samples/todoapp/Samples.TodoApp.sln | 18 ++++ samples/todoapp/TodoApp.WPF/App.xaml | 9 ++ samples/todoapp/TodoApp.WPF/App.xaml.cs | 81 ++++++++++++++++++ samples/todoapp/TodoApp.WPF/AssemblyInfo.cs | 14 ++++ .../TodoApp.WPF/Database/AppDbContext.cs | 59 +++++++++++++ .../TodoApp.WPF/Database/IDbInitializer.cs | 23 +++++ .../Database/OfflineClientEntity.cs | 19 +++++ .../todoapp/TodoApp.WPF/Database/TodoItem.cs | 10 +++ samples/todoapp/TodoApp.WPF/MainWindow.xaml | 59 +++++++++++++ .../todoapp/TodoApp.WPF/MainWindow.xaml.cs | 23 +++++ .../TodoApp.WPF/Services/AlertService.cs | 37 ++++++++ .../todoapp/TodoApp.WPF/TodoApp.WPF.csproj | 19 +++++ .../ViewModels/TodoListViewModel.cs | 84 +++++++++++++++++++ .../TodoApp.WinUI3/Database/AppDbContext.cs | 2 +- 14 files changed, 456 insertions(+), 1 deletion(-) create mode 100644 samples/todoapp/TodoApp.WPF/App.xaml create mode 100644 samples/todoapp/TodoApp.WPF/App.xaml.cs create mode 100644 samples/todoapp/TodoApp.WPF/AssemblyInfo.cs create mode 100644 samples/todoapp/TodoApp.WPF/Database/AppDbContext.cs create mode 100644 samples/todoapp/TodoApp.WPF/Database/IDbInitializer.cs create mode 100644 samples/todoapp/TodoApp.WPF/Database/OfflineClientEntity.cs create mode 100644 samples/todoapp/TodoApp.WPF/Database/TodoItem.cs create mode 100644 samples/todoapp/TodoApp.WPF/MainWindow.xaml create mode 100644 samples/todoapp/TodoApp.WPF/MainWindow.xaml.cs create mode 100644 samples/todoapp/TodoApp.WPF/Services/AlertService.cs create mode 100644 samples/todoapp/TodoApp.WPF/TodoApp.WPF.csproj create mode 100644 samples/todoapp/TodoApp.WPF/ViewModels/TodoListViewModel.cs diff --git a/samples/todoapp/Samples.TodoApp.sln b/samples/todoapp/Samples.TodoApp.sln index d11a6d7c..1204e402 100644 --- a/samples/todoapp/Samples.TodoApp.sln +++ b/samples/todoapp/Samples.TodoApp.sln @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Datasync.Server", ". EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TodoApp.MAUI", "TodoApp.MAUI\TodoApp.MAUI.csproj", "{00430043-04C5-4F8F-87A9-98ECC0051808}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TodoApp.WPF", "TodoApp.WPF\TodoApp.WPF.csproj", "{A0996FB8-890D-4E90-A881-01F9EF709711}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -105,6 +107,22 @@ Global {00430043-04C5-4F8F-87A9-98ECC0051808}.Release|x86.ActiveCfg = Release|Any CPU {00430043-04C5-4F8F-87A9-98ECC0051808}.Release|x86.Build.0 = Release|Any CPU {00430043-04C5-4F8F-87A9-98ECC0051808}.Release|x86.Deploy.0 = Release|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Debug|ARM64.Build.0 = Debug|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Debug|x64.ActiveCfg = Debug|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Debug|x64.Build.0 = Debug|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Debug|x86.ActiveCfg = Debug|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Debug|x86.Build.0 = Debug|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Release|Any CPU.Build.0 = Release|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Release|ARM64.ActiveCfg = Release|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Release|ARM64.Build.0 = Release|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Release|x64.ActiveCfg = Release|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Release|x64.Build.0 = Release|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Release|x86.ActiveCfg = Release|Any CPU + {A0996FB8-890D-4E90-A881-01F9EF709711}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/samples/todoapp/TodoApp.WPF/App.xaml b/samples/todoapp/TodoApp.WPF/App.xaml new file mode 100644 index 00000000..b4f48400 --- /dev/null +++ b/samples/todoapp/TodoApp.WPF/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/samples/todoapp/TodoApp.WPF/App.xaml.cs b/samples/todoapp/TodoApp.WPF/App.xaml.cs new file mode 100644 index 00000000..9bf55a19 --- /dev/null +++ b/samples/todoapp/TodoApp.WPF/App.xaml.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using System.Windows; +using TodoApp.WPF.Database; +using TodoApp.WPF.Services; +using TodoApp.WPF.ViewModels; + +namespace TodoApp.WPF; + +/// +/// Interaction logic for App.xaml +/// +public partial class App : Application, IDisposable +{ + private readonly SqliteConnection dbConnection; + public IServiceProvider Services { get; } + + public App() + { + this.dbConnection = new SqliteConnection("Data Source=:memory:"); + this.dbConnection.Open(); + + // Create the IoC Services provider. + Services = new ServiceCollection() + .AddTransient() + .AddScoped() + .AddScoped() + .AddDbContext(options => options.UseSqlite(this.dbConnection)) + .BuildServiceProvider(); + + // Initialize the database + InitializeDatabase(); + } + + private void InitializeDatabase() + { + using IServiceScope scope = Services.CreateScope(); + IDbInitializer initializer = scope.ServiceProvider.GetRequiredService(); + initializer.Initialize(); + } + + /// + /// A helper method for getting a service from the services collection. + /// + /// + /// You can see this in action in the class. + /// + /// The type of the service. + /// An instance of the service + public static TService GetRequiredService() where TService : notnull + => ((App)App.Current).Services.GetRequiredService(); + + #region IDisposable + private bool hasDisposed; + + protected virtual void Dispose(bool disposing) + { + if (!this.hasDisposed) + { + if (disposing) + { + this.dbConnection.Close(); + } + + this.hasDisposed = true; + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + #endregion +} + diff --git a/samples/todoapp/TodoApp.WPF/AssemblyInfo.cs b/samples/todoapp/TodoApp.WPF/AssemblyInfo.cs new file mode 100644 index 00000000..76e9b08e --- /dev/null +++ b/samples/todoapp/TodoApp.WPF/AssemblyInfo.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/samples/todoapp/TodoApp.WPF/Database/AppDbContext.cs b/samples/todoapp/TodoApp.WPF/Database/AppDbContext.cs new file mode 100644 index 00000000..b00f3b1c --- /dev/null +++ b/samples/todoapp/TodoApp.WPF/Database/AppDbContext.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using CommunityToolkit.Datasync.Client.Http; +using CommunityToolkit.Datasync.Client.Offline; +using Microsoft.EntityFrameworkCore; + +namespace TodoApp.WPF.Database; + +public class AppDbContext(DbContextOptions options) : DbContext(options) +{ + public DbSet TodoItems => Set(); + + //protected override void OnDatasyncInitialization(DatasyncOfflineOptionsBuilder optionsBuilder) + //{ + // HttpClientOptions clientOptions = new() + // { + // Endpoint = new Uri("https://YOURSITEHERE.azurewebsites.net/"), + // HttpPipeline = [new LoggingHandler()] + // }; + // _ = optionsBuilder.UseHttpClientOptions(clientOptions); + //} + + public async Task SynchronizeAsync(CancellationToken cancellationToken = default) + { + //PushResult pushResult = await this.PushAsync(cancellationToken); + //if (!pushResult.IsSuccessful) + //{ + // throw new ApplicationException($"Push failed: {pushResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}"); + //} + + //PullResult pullResult = await this.PullAsync(cancellationToken); + //if (!pullResult.IsSuccessful) + //{ + // throw new ApplicationException($"Pull failed: {pullResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}"); + //} + } +} + +/// +/// Use this class to initialize the database. In this sample, we just create +/// the database using . However, you +/// may want to use migrations. +/// +/// The context for the database. +public class DbContextInitializer(AppDbContext context) : IDbInitializer +{ + /// + public void Initialize() + { + _ = context.Database.EnsureCreated(); + // Task.Run(async () => await context.SynchronizeAsync()); + } + + /// + public Task InitializeAsync(CancellationToken cancellationToken = default) + => context.Database.EnsureCreatedAsync(cancellationToken); +} diff --git a/samples/todoapp/TodoApp.WPF/Database/IDbInitializer.cs b/samples/todoapp/TodoApp.WPF/Database/IDbInitializer.cs new file mode 100644 index 00000000..5be08ffb --- /dev/null +++ b/samples/todoapp/TodoApp.WPF/Database/IDbInitializer.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace TodoApp.WPF.Database; + +/// +/// An interface to initialize a database. +/// +public interface IDbInitializer +{ + /// + /// Synchronously initialize the database. + /// + void Initialize(); + + /// + /// Asynchronously initialize the database. + /// + /// A to observe. + /// A task that resolves when complete. + Task InitializeAsync(CancellationToken cancellationToken = default); +} diff --git a/samples/todoapp/TodoApp.WPF/Database/OfflineClientEntity.cs b/samples/todoapp/TodoApp.WPF/Database/OfflineClientEntity.cs new file mode 100644 index 00000000..bcf22230 --- /dev/null +++ b/samples/todoapp/TodoApp.WPF/Database/OfflineClientEntity.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel.DataAnnotations; + +namespace TodoApp.WPF.Database; + +/// +/// An abstract class for working with offline entities. +/// +public abstract class OfflineClientEntity +{ + [Key] + public string Id { get; set; } = Guid.NewGuid().ToString("N"); + public DateTimeOffset? UpdatedAt { get; set; } + public string? Version { get; set; } + public bool Deleted { get; set; } +} diff --git a/samples/todoapp/TodoApp.WPF/Database/TodoItem.cs b/samples/todoapp/TodoApp.WPF/Database/TodoItem.cs new file mode 100644 index 00000000..f46888d6 --- /dev/null +++ b/samples/todoapp/TodoApp.WPF/Database/TodoItem.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace TodoApp.WPF.Database; +public class TodoItem : OfflineClientEntity +{ + public string Title { get; set; } = string.Empty; + public bool IsComplete { get; set; } = false; +} diff --git a/samples/todoapp/TodoApp.WPF/MainWindow.xaml b/samples/todoapp/TodoApp.WPF/MainWindow.xaml new file mode 100644 index 00000000..e89414ed --- /dev/null +++ b/samples/todoapp/TodoApp.WPF/MainWindow.xaml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + +