From 08eaa30209fb07132671006392af33044278fb88 Mon Sep 17 00:00:00 2001 From: retr0volkov <68907954+retr0volkov@users.noreply.github.com> Date: Wed, 30 Jul 2025 09:08:22 +0300 Subject: [PATCH] Removed dialogs for editing equipment and points, and their related resources --- .idea/.idea.GazpromApp/.idea/avalonia.xml | 7 + .idea/.idea.GazpromApp/.idea/dataSources.xml | 7 + App.axaml | 5 +- GazpromApp.csproj | 2 + Messages/EditEquipmentMessage.cs | 4 +- Messages/EditPointMessage.cs | 6 +- Messages/NewEquipmentDone.cs | 8 - Messages/NewEquipmentMessage.cs | 6 - Messages/NewPointDone.cs | 8 - Messages/NewPointMessage.cs | 6 - ...pmentDone.cs => RemoveEquipmentMessage.cs} | 2 +- Messages/RemoveInspectionMessage.cs | 8 + ...EditPointDone.cs => RemovePointMessage.cs} | 2 +- Messages/RemovePropertyMessage.cs | 8 + Messages/RequestImageMessage.cs | 11 + Messages/SaveChangesMessage.cs | 9 + Messages/SaveChangesResult.cs | 7 + Messages/SelectionChangedMessage.cs | 8 + Models/Equipment.cs | 29 ++- Models/Image.cs | 10 +- Models/Inspection.cs | 5 +- Models/Point.cs | 6 +- Models/Property.cs | 12 +- Utils/AppDbContext.cs | 63 +---- Utils/ParentsHelper.cs | 53 +++++ Utils/PropertyParentHelper.cs | 30 --- ViewModels/Basic/EquipmentViewModel.cs | 60 ++++- ViewModels/Basic/ImageViewModel.cs | 48 ++-- ViewModels/Basic/InspectionViewModel.cs | 23 ++ ViewModels/Basic/PointViewModel.cs | 54 ++++- ViewModels/Basic/PropertyViewModel.cs | 37 ++- ViewModels/Basic/SaveChangesViewModel.cs | 42 ++++ ...ViewModel.cs => EditEquipmentViewModel.cs} | 143 +++++------ ...alogViewModel.cs => EditPointViewModel.cs} | 118 +++------ ViewModels/MainWindowViewModel.cs | 214 ++++++++++++----- ViewModels/NewEquipmentDialogViewModel.cs | 94 -------- ViewModels/NewPointDialogViewModel.cs | 93 -------- Views/EditEquipmentDialog.axaml | 225 ------------------ Views/EditEquipmentDialog.axaml.cs | 57 ----- Views/EditEquipmentView.axaml | 135 +++++++++++ Views/EditEquipmentView.axaml.cs | 13 + Views/EditPointDialog.axaml | 220 ----------------- Views/EditPointDialog.axaml.cs | 119 --------- Views/EditPointView.axaml | 112 +++++++++ Views/EditPointView.axaml.cs | 13 + Views/EquipmentView.axaml | 38 +++ Views/EquipmentView.axaml.cs | 13 + Views/InspectionView.axaml | 55 +++++ Views/InspectionView.axaml.cs | 13 + Views/MainWindow.axaml | 103 +++----- Views/MainWindow.axaml.cs | 86 +++++-- Views/NewEquipmentDialog.axaml | 76 ------ Views/NewEquipmentDialog.axaml.cs | 37 --- Views/NewPointDialog.axaml | 82 ------- Views/NewPointDialog.axaml.cs | 36 --- Views/PointView.axaml | 60 +++++ Views/PointView.axaml.cs | 14 ++ Views/PropertyView.axaml | 43 ++++ Views/PropertyView.axaml.cs | 13 + Views/SaveChangesView.axaml | 40 ++++ Views/SaveChangesView.axaml.cs | 26 ++ .../net8.0/Avalonia/Resources.Inputs.cache | 2 +- obj/Debug/net8.0/Avalonia/resources | Bin 473854 -> 462311 bytes obj/Debug/net8.0/GazpromApp.AssemblyInfo.cs | 15 +- .../GazpromApp.AssemblyInfoInputs.cache | 2 +- ....GeneratedMSBuildEditorConfig.editorconfig | 17 +- obj/Debug/net8.0/GazpromApp.assets.cache | Bin 77739 -> 80054 bytes .../GazpromApp.csproj.AssemblyReference.cache | Bin 35117 -> 37400 bytes .../GazpromApp.csproj.FileListAbsolute.txt | 6 + obj/GazpromApp.csproj.nuget.dgspec.json | 8 + obj/GazpromApp.csproj.nuget.g.props | 2 +- obj/GazpromApp.csproj.nuget.g.targets | 4 +- obj/project.assets.json | 97 ++++++++ obj/project.nuget.cache | 5 +- obj/project.packagespec.json | 2 +- obj/rider.project.model.nuget.info | 2 +- obj/rider.project.restore.info | 2 +- 77 files changed, 1498 insertions(+), 1543 deletions(-) delete mode 100644 Messages/NewEquipmentDone.cs delete mode 100644 Messages/NewEquipmentMessage.cs delete mode 100644 Messages/NewPointDone.cs delete mode 100644 Messages/NewPointMessage.cs rename Messages/{EditEquipmentDone.cs => RemoveEquipmentMessage.cs} (66%) create mode 100644 Messages/RemoveInspectionMessage.cs rename Messages/{EditPointDone.cs => RemovePointMessage.cs} (66%) create mode 100644 Messages/RemovePropertyMessage.cs create mode 100644 Messages/RequestImageMessage.cs create mode 100644 Messages/SaveChangesMessage.cs create mode 100644 Messages/SaveChangesResult.cs create mode 100644 Messages/SelectionChangedMessage.cs create mode 100644 Utils/ParentsHelper.cs delete mode 100644 Utils/PropertyParentHelper.cs create mode 100644 ViewModels/Basic/SaveChangesViewModel.cs rename ViewModels/{EditEquipmentDialogViewModel.cs => EditEquipmentViewModel.cs} (57%) rename ViewModels/{EditPointDialogViewModel.cs => EditPointViewModel.cs} (64%) delete mode 100644 ViewModels/NewEquipmentDialogViewModel.cs delete mode 100644 ViewModels/NewPointDialogViewModel.cs delete mode 100644 Views/EditEquipmentDialog.axaml delete mode 100644 Views/EditEquipmentDialog.axaml.cs create mode 100644 Views/EditEquipmentView.axaml create mode 100644 Views/EditEquipmentView.axaml.cs delete mode 100644 Views/EditPointDialog.axaml delete mode 100644 Views/EditPointDialog.axaml.cs create mode 100644 Views/EditPointView.axaml create mode 100644 Views/EditPointView.axaml.cs create mode 100644 Views/EquipmentView.axaml create mode 100644 Views/EquipmentView.axaml.cs create mode 100644 Views/InspectionView.axaml create mode 100644 Views/InspectionView.axaml.cs delete mode 100644 Views/NewEquipmentDialog.axaml delete mode 100644 Views/NewEquipmentDialog.axaml.cs delete mode 100644 Views/NewPointDialog.axaml delete mode 100644 Views/NewPointDialog.axaml.cs create mode 100644 Views/PointView.axaml create mode 100644 Views/PointView.axaml.cs create mode 100644 Views/PropertyView.axaml create mode 100644 Views/PropertyView.axaml.cs create mode 100644 Views/SaveChangesView.axaml create mode 100644 Views/SaveChangesView.axaml.cs diff --git a/.idea/.idea.GazpromApp/.idea/avalonia.xml b/.idea/.idea.GazpromApp/.idea/avalonia.xml index 122bbc9..a9dc23c 100644 --- a/.idea/.idea.GazpromApp/.idea/avalonia.xml +++ b/.idea/.idea.GazpromApp/.idea/avalonia.xml @@ -5,13 +5,20 @@ + + + + + + + diff --git a/.idea/.idea.GazpromApp/.idea/dataSources.xml b/.idea/.idea.GazpromApp/.idea/dataSources.xml index ecc4b04..609f8c1 100644 --- a/.idea/.idea.GazpromApp/.idea/dataSources.xml +++ b/.idea/.idea.GazpromApp/.idea/dataSources.xml @@ -9,5 +9,12 @@ jdbc:sqlite:identifier.sqlite $ProjectFileDir$ + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:$USER_HOME$/AppData/Local/GazpromApp/GazpromApp.db + $ProjectFileDir$ + \ No newline at end of file diff --git a/App.axaml b/App.axaml index 7287b36..1559c72 100644 --- a/App.axaml +++ b/App.axaml @@ -2,6 +2,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="GazpromApp.App" xmlns:local="using:GazpromApp" + xmlns:icons="using:Material.Icons.Avalonia" + xmlns:material="using:Material.Styles.Themes" RequestedThemeVariant="Default"> @@ -10,6 +12,7 @@ - + + \ No newline at end of file diff --git a/GazpromApp.csproj b/GazpromApp.csproj index 7a9e47b..56fe392 100644 --- a/GazpromApp.csproj +++ b/GazpromApp.csproj @@ -26,6 +26,8 @@ + + all diff --git a/Messages/EditEquipmentMessage.cs b/Messages/EditEquipmentMessage.cs index 00ae057..440747f 100644 --- a/Messages/EditEquipmentMessage.cs +++ b/Messages/EditEquipmentMessage.cs @@ -1,9 +1,9 @@ -using CommunityToolkit.Mvvm.Messaging.Messages; +using System; using GazpromApp.ViewModels; namespace GazpromApp.Messages; -public class EditEquipmentMessage(EquipmentViewModel equipment) : AsyncRequestMessage +public class EditEquipmentMessage(EquipmentViewModel equipment) { public EquipmentViewModel Equipment { get; } = equipment; } \ No newline at end of file diff --git a/Messages/EditPointMessage.cs b/Messages/EditPointMessage.cs index 85e0314..e074f02 100644 --- a/Messages/EditPointMessage.cs +++ b/Messages/EditPointMessage.cs @@ -1,9 +1,9 @@ -using CommunityToolkit.Mvvm.Messaging.Messages; +using GazpromApp.Models; using GazpromApp.ViewModels; namespace GazpromApp.Messages; -public class EditPointMessage(PointViewModel pointViewModel) : AsyncRequestMessage +public class EditPointMessage(PointViewModel point) { - public PointViewModel Point { get; } = pointViewModel; + public PointViewModel Point { get; } = point; } \ No newline at end of file diff --git a/Messages/NewEquipmentDone.cs b/Messages/NewEquipmentDone.cs deleted file mode 100644 index e22b780..0000000 --- a/Messages/NewEquipmentDone.cs +++ /dev/null @@ -1,8 +0,0 @@ -using GazpromApp.ViewModels; - -namespace GazpromApp.Messages; - -public class NewEquipmentDone(EquipmentViewModel equipment) -{ - public EquipmentViewModel Equipment { get; } = equipment; -} \ No newline at end of file diff --git a/Messages/NewEquipmentMessage.cs b/Messages/NewEquipmentMessage.cs deleted file mode 100644 index e7e3d39..0000000 --- a/Messages/NewEquipmentMessage.cs +++ /dev/null @@ -1,6 +0,0 @@ -using CommunityToolkit.Mvvm.Messaging.Messages; -using GazpromApp.ViewModels; - -namespace GazpromApp.Messages; - -public class NewEquipmentMessage : AsyncRequestMessage; \ No newline at end of file diff --git a/Messages/NewPointDone.cs b/Messages/NewPointDone.cs deleted file mode 100644 index 90048b6..0000000 --- a/Messages/NewPointDone.cs +++ /dev/null @@ -1,8 +0,0 @@ -using GazpromApp.ViewModels; - -namespace GazpromApp.Messages; - -public class NewPointDone(PointViewModel pointViewModel) -{ - public PointViewModel Point { get; } = pointViewModel; -} \ No newline at end of file diff --git a/Messages/NewPointMessage.cs b/Messages/NewPointMessage.cs deleted file mode 100644 index b376345..0000000 --- a/Messages/NewPointMessage.cs +++ /dev/null @@ -1,6 +0,0 @@ -using CommunityToolkit.Mvvm.Messaging.Messages; -using GazpromApp.ViewModels; - -namespace GazpromApp.Messages; - -public class NewPointMessage : AsyncRequestMessage; \ No newline at end of file diff --git a/Messages/EditEquipmentDone.cs b/Messages/RemoveEquipmentMessage.cs similarity index 66% rename from Messages/EditEquipmentDone.cs rename to Messages/RemoveEquipmentMessage.cs index 2d9fe72..1e6cc11 100644 --- a/Messages/EditEquipmentDone.cs +++ b/Messages/RemoveEquipmentMessage.cs @@ -2,7 +2,7 @@ namespace GazpromApp.Messages; -public class EditEquipmentDone(EquipmentViewModel equipment) +public class RemoveEquipmentMessage(EquipmentViewModel equipment) { public EquipmentViewModel Equipment { get; } = equipment; } \ No newline at end of file diff --git a/Messages/RemoveInspectionMessage.cs b/Messages/RemoveInspectionMessage.cs new file mode 100644 index 0000000..e902fd7 --- /dev/null +++ b/Messages/RemoveInspectionMessage.cs @@ -0,0 +1,8 @@ +using GazpromApp.ViewModels; + +namespace GazpromApp.Messages; + +public class RemoveInspectionMessage(InspectionViewModel inspection) +{ + public InspectionViewModel Inspection { get; } = inspection; +} \ No newline at end of file diff --git a/Messages/EditPointDone.cs b/Messages/RemovePointMessage.cs similarity index 66% rename from Messages/EditPointDone.cs rename to Messages/RemovePointMessage.cs index 6244253..e669790 100644 --- a/Messages/EditPointDone.cs +++ b/Messages/RemovePointMessage.cs @@ -2,7 +2,7 @@ namespace GazpromApp.Messages; -public class EditPointDone(PointViewModel pointViewModel) +public class RemovePointMessage(PointViewModel pointViewModel) { public PointViewModel Point { get; } = pointViewModel; } \ No newline at end of file diff --git a/Messages/RemovePropertyMessage.cs b/Messages/RemovePropertyMessage.cs new file mode 100644 index 0000000..deab9cc --- /dev/null +++ b/Messages/RemovePropertyMessage.cs @@ -0,0 +1,8 @@ +using GazpromApp.ViewModels; + +namespace GazpromApp.Messages; + +public class RemovePropertyMessage(PropertyViewModel property) +{ + public PropertyViewModel Property { get; } = property; +} \ No newline at end of file diff --git a/Messages/RequestImageMessage.cs b/Messages/RequestImageMessage.cs new file mode 100644 index 0000000..356d9c3 --- /dev/null +++ b/Messages/RequestImageMessage.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Avalonia.Platform.Storage; +using CommunityToolkit.Mvvm.Messaging.Messages; +using GazpromApp.ViewModels; + +namespace GazpromApp.Messages; + +public class RequestImageMessage(ViewModelBase viewModel) : AsyncRequestMessage> +{ + private ViewModelBase Caller { get; set; } = viewModel; +} \ No newline at end of file diff --git a/Messages/SaveChangesMessage.cs b/Messages/SaveChangesMessage.cs new file mode 100644 index 0000000..9ca09d5 --- /dev/null +++ b/Messages/SaveChangesMessage.cs @@ -0,0 +1,9 @@ +using CommunityToolkit.Mvvm.Messaging.Messages; +using GazpromApp.ViewModels; + +namespace GazpromApp.Messages; + +public class SaveChangesMessage(SaveChangesViewModel saveChanges) : AsyncRequestMessage +{ + public SaveChangesViewModel SaveChanges { get; } = saveChanges; +} \ No newline at end of file diff --git a/Messages/SaveChangesResult.cs b/Messages/SaveChangesResult.cs new file mode 100644 index 0000000..e69d9dc --- /dev/null +++ b/Messages/SaveChangesResult.cs @@ -0,0 +1,7 @@ +namespace GazpromApp.Messages; + +public class SaveChangesResult(bool save, bool cancel) +{ + public bool Save { get; } = save; + public bool Cancel { get; } = cancel; +} \ No newline at end of file diff --git a/Messages/SelectionChangedMessage.cs b/Messages/SelectionChangedMessage.cs new file mode 100644 index 0000000..03a8b39 --- /dev/null +++ b/Messages/SelectionChangedMessage.cs @@ -0,0 +1,8 @@ +using GazpromApp.ViewModels; + +namespace GazpromApp.Messages; + +public class SelectionChangedMessage(ViewModelBase viewModel) +{ + public ViewModelBase ViewModel { get; } = viewModel; +} \ No newline at end of file diff --git a/Models/Equipment.cs b/Models/Equipment.cs index 2e8d6c0..aafa26a 100644 --- a/Models/Equipment.cs +++ b/Models/Equipment.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; @@ -7,28 +8,42 @@ namespace GazpromApp.Models; public class Equipment { + public Equipment() + { + LocalId = Id == Guid.Empty ? Guid.NewGuid() : Id; + } + [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [JsonIgnore] - public int Id { get; set; } + public Guid Id { get; set; } + + [JsonIgnore] + [NotMapped] + public Guid LocalId { get; set; } [Required] public string? Name { get; set; } = string.Empty; public string? Description { get; set; } = string.Empty; + public DateTime CreationDate { get; set; } // Foreign keys - [Required] [JsonIgnore] - public int PointId { get; set; } + public Guid? PointId { get; set; } [JsonIgnore] - public List ImageIds { get; set; } = []; + public List ImageIds { get; set; } = []; [JsonIgnore] - public List InspectionIds { get; set; } = []; + public List InspectionIds { get; set; } = []; + [JsonIgnore] + public List EquipmentIds { get; set; } = []; + public Guid? ParentEquipmentId { get; set; } // Navigation Properties public List Images { get; set; } = []; [JsonIgnore] - public Point Point { get; set; } = null!; + public Point? Point { get; set; } = null!; public List Properties { get; set; } = []; public List Inspections { get; set; } = []; + public List Equipments { get; set; } = []; + public Equipment? ParentEquipment { get; set; } } \ No newline at end of file diff --git a/Models/Image.cs b/Models/Image.cs index cd02e00..0b7d8f9 100644 --- a/Models/Image.cs +++ b/Models/Image.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using System; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; @@ -9,16 +10,17 @@ public class Image [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [JsonIgnore] - public int Id { get; set; } + public Guid Id { get; set; } [Required] public byte[] Bytes { get; set; } = []; + public DateTime CreationDate { get; set; } // Foreign keys [JsonIgnore] - public int? PointId { get; set; } + public Guid? PointId { get; set; } [JsonIgnore] - public int? EquipmentId { get; set; } + public Guid? EquipmentId { get; set; } // Navigation Properties [JsonIgnore] diff --git a/Models/Inspection.cs b/Models/Inspection.cs index 6e64e6b..afa8c18 100644 --- a/Models/Inspection.cs +++ b/Models/Inspection.cs @@ -10,10 +10,11 @@ public class Inspection [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [JsonIgnore] - public int Id { get; set; } + public Guid Id { get; set; } [Required] public string Name { get; set; } = string.Empty; + public DateTime CreationDate { get; set; } [Required] public DateTimeOffset InspectionDate { get; set; } [Required] @@ -23,7 +24,7 @@ public class Inspection // Foreign keys [JsonIgnore] - public int EquipmentId { get; set; } + public Guid EquipmentId { get; set; } // Navigation Properties [JsonIgnore] diff --git a/Models/Point.cs b/Models/Point.cs index de1e9a9..f174300 100644 --- a/Models/Point.cs +++ b/Models/Point.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; @@ -10,11 +11,12 @@ public class Point [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [JsonIgnore] - public int Id { get; set; } + public Guid Id { get; set; } = Guid.NewGuid(); [Required] public string? Name { get; set; } = string.Empty; public string? Description { get; set; } = string.Empty; + public DateTime CreationDate { get; set; } public double Latitude { get; set; } public double Longitude { get; set; } // Navigation Properties diff --git a/Models/Property.cs b/Models/Property.cs index 83975e2..f162100 100644 --- a/Models/Property.cs +++ b/Models/Property.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; @@ -10,27 +11,28 @@ public class Property [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [JsonIgnore] - public int Id { get; set; } + public Guid Id { get; set; } [Required] public string Name { get; set; } = string.Empty; public string Value { get; set; } = string.Empty; + public DateTime CreationDate { get; set; } // For self-referencing hierarchy (Property of a Property) [JsonIgnore] - public int? ParentPropertyId { get; set; } + public Guid? ParentPropertyId { get; set; } [JsonIgnore] public Property? Parent { get; set; } // For a Property of a Point [JsonIgnore] - public int? PointId { get; set; } + public Guid? PointId { get; set; } [JsonIgnore] public Point? Point { get; set; } // For a Property of an Equipment [JsonIgnore] - public int? EquipmentId { get; set; } + public Guid? EquipmentId { get; set; } [JsonIgnore] public Equipment? Equipment { get; set; } diff --git a/Utils/AppDbContext.cs b/Utils/AppDbContext.cs index f0f16e9..cbca3a4 100644 --- a/Utils/AppDbContext.cs +++ b/Utils/AppDbContext.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using GazpromApp.Models; using Microsoft.EntityFrameworkCore; @@ -17,13 +18,15 @@ public class AppDbContext : DbContext public AppDbContext() { var path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - DbPath = System.IO.Path.Combine(path, "GazpromApp.db"); + Directory.CreateDirectory(Path.Combine(path, "GazpromApp")); + DbPath = Path.Combine(Path.Combine(path, "GazpromApp"), "GazpromApp.db"); } public AppDbContext(DbContextOptions options) : base(options) { var path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - DbPath = System.IO.Path.Combine(path, "GazpromApp.db"); + Directory.CreateDirectory(Path.Combine(path, "GazpromApp")); + DbPath = Path.Combine(Path.Combine(path, "GazpromApp"), "GazpromApp.db"); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) @@ -41,6 +44,7 @@ public class AppDbContext : DbContext .HasMany(p => p.Equipment) .WithOne(e => e.Point) .HasForeignKey(e => e.PointId) + .IsRequired(false) .OnDelete(DeleteBehavior.Cascade); // Deleting a Point deletes its Equipment. modelBuilder.Entity() @@ -63,6 +67,13 @@ public class AppDbContext : DbContext .HasForeignKey(i => i.EquipmentId) .OnDelete(DeleteBehavior.Cascade); // Equipment has one Image. + modelBuilder.Entity() + .HasMany(e => e.Equipments) + .WithOne(e => e.ParentEquipment) + .HasForeignKey(e => e.ParentEquipmentId) + .IsRequired(false) + .OnDelete(DeleteBehavior.Cascade); // Equipment has many Equipments. + // ==== PROPERTY RELATIONSHIPS ==== // Property as a child of a Point modelBuilder.Entity() @@ -87,53 +98,5 @@ public class AppDbContext : DbContext .HasForeignKey(p => p.ParentPropertyId) .IsRequired(false) .OnDelete(DeleteBehavior.Cascade); // Deleting a parent Property deletes its children. - - // modelBuilder.Entity() - // .Property(p => p.Id) - // .ValueGeneratedOnAdd(); - // modelBuilder.Entity() - // .Property(e => e.Id) - // .ValueGeneratedOnAdd(); - // modelBuilder.Entity() - // .Property(i => i.Id) - // .ValueGeneratedOnAdd(); - // modelBuilder.Entity() - // .Property(p => p.Id) - // .ValueGeneratedOnAdd(); - // modelBuilder.Entity() - // .Property(i => i.Id) - // .ValueGeneratedOnAdd(); - // - // modelBuilder.Entity() - // .HasMany(p => p.Images) - // .WithMany(i => i.Points); - // modelBuilder.Entity() - // .HasMany(p => p.Equipment) - // .WithOne(e => e.Point) - // .HasForeignKey(e => e.PointId); - // modelBuilder.Entity() - // .HasMany(p => p.Properties) - // .WithOne(p => p.Point) - // .HasForeignKey(p => p.ParentId) - // .IsRequired(false); - // - // modelBuilder.Entity() - // .HasOne(e => e.Image) - // .WithMany(i => i.Equipment); - // modelBuilder.Entity() - // .HasMany(e => e.Properties) - // .WithOne(p => p.Equipment) - // .HasForeignKey(p => p.ParentId) - // .IsRequired(false); - // modelBuilder.Entity() - // .HasMany(e => e.Inspections) - // .WithOne(i => i.Equipment) - // .HasForeignKey(i => i.EquipmentId); - // - // modelBuilder.Entity() - // .HasMany(p => p.Properties) - // .WithOne(p => p.Parent) - // .HasForeignKey(p => p.ParentId) - // .IsRequired(false); } } \ No newline at end of file diff --git a/Utils/ParentsHelper.cs b/Utils/ParentsHelper.cs new file mode 100644 index 0000000..217b449 --- /dev/null +++ b/Utils/ParentsHelper.cs @@ -0,0 +1,53 @@ +using System.Linq; +using GazpromApp.Models; + +namespace GazpromApp.Utils; + +public static class ParentsHelper +{ + public static void SetPropertyParents(this Point point) + { + foreach (var prop in point.Properties) + { + SetPropertyParentsRecursively(prop); + } + + foreach (var prop in point.Equipment.SelectMany(equipment => equipment.Properties)) + { + SetPropertyParentsRecursively(prop); + } + } + + private static void SetPropertyParentsRecursively(Property property) + { + foreach (var childProp in property.Properties) + { + childProp.Parent = property; + childProp.ParentPropertyId = property.Id; + SetPropertyParentsRecursively(childProp); + } + } + + public static void SetEquipmentParents(this Point point) + { + foreach (var prop in point.Equipment) + { + SetEquipmentParentsRecursively(prop); + } + + foreach (var prop in point.Equipment.SelectMany(equipment1 => equipment1.Equipments)) + { + SetEquipmentParentsRecursively(prop); + } + } + + private static void SetEquipmentParentsRecursively(Equipment equipment) + { + foreach (var childProp in equipment.Equipments) + { + childProp.ParentEquipment = equipment; + childProp.ParentEquipmentId = equipment.Id; + SetEquipmentParentsRecursively(childProp); + } + } +} \ No newline at end of file diff --git a/Utils/PropertyParentHelper.cs b/Utils/PropertyParentHelper.cs deleted file mode 100644 index c082e83..0000000 --- a/Utils/PropertyParentHelper.cs +++ /dev/null @@ -1,30 +0,0 @@ -using GazpromApp.Models; -using System.Linq; - -namespace GazpromApp.Utils; - -public static class PropertyParentHelper -{ - public static void SetPropertyParents(this Point point) - { - foreach (var prop in point.Properties) - { - SetParentsRecursively(prop); - } - - foreach (var prop in point.Equipment.SelectMany(equipment => equipment.Properties)) - { - SetParentsRecursively(prop); - } - } - - private static void SetParentsRecursively(Property property) - { - foreach (var childProp in property.Properties) - { - childProp.Parent = property; - childProp.ParentPropertyId = property.Id; - SetParentsRecursively(childProp); - } - } -} \ No newline at end of file diff --git a/ViewModels/Basic/EquipmentViewModel.cs b/ViewModels/Basic/EquipmentViewModel.cs index 8092024..0ce2cc5 100644 --- a/ViewModels/Basic/EquipmentViewModel.cs +++ b/ViewModels/Basic/EquipmentViewModel.cs @@ -1,7 +1,11 @@ using System; using System.Collections.ObjectModel; +using System.Diagnostics; using System.Linq; using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using GazpromApp.Messages; using GazpromApp.Models; using GazpromApp.Utils; @@ -9,6 +13,7 @@ namespace GazpromApp.ViewModels; public partial class EquipmentViewModel : ViewModelBase { + public ViewModelBase? Parent { get; set; } private Equipment Equipment { get; } [ObservableProperty] @@ -17,31 +22,72 @@ public partial class EquipmentViewModel : ViewModelBase [ObservableProperty] private string? _description; - public ObservableCollection Images { get; init; } = []; - - public ObservableCollection Properties { get; } + [ObservableProperty] + private DateTime _creationDate; - public ObservableCollection Inspections { get; } + public ObservableCollection Images { get; set; } = []; + + public ObservableCollection Properties { get; set; } + + public ObservableCollection Inspections { get; set; } + + public ObservableCollection Equipments { get; set; } [ObservableProperty] private string _nearestInspection; + [RelayCommand] + private void AddEquipment() + { + Equipments.Add(new EquipmentViewModel{ Name = "Новое оборудование", Description = "Описание оборудования", Parent = this}); + CallSavePoint(this); + } + + [RelayCommand] + private void RemoveEquipment() + { + WeakReferenceMessenger.Default.Send(new RemoveEquipmentMessage(this)); + } + + private void RemoveEquipment(object sender, RemoveEquipmentMessage msg) + { + if (Equipments.Contains(msg.Equipment)) + Equipments.Remove(msg.Equipment); + CallSavePoint(this); + } + + private void CallSavePoint(EquipmentViewModel equipment) + { + var current = equipment; + while (current.Parent is EquipmentViewModel model) + current = model ?? throw new InvalidOperationException("Parent equipment not found in hierarchy"); + + if (current.Parent is PointViewModel pointViewModel) + WeakReferenceMessenger.Default.Send(new EditPointMessage(pointViewModel)); + } + public EquipmentViewModel() { Name = string.Empty; Description = string.Empty; + CreationDate = DateTime.Now; Properties = []; Inspections = []; + Equipments = []; NearestInspection = "Нету инспекций"; Equipment = new Equipment(); + + WeakReferenceMessenger.Default.Register(this, RemoveEquipment); } public EquipmentViewModel(Equipment equipment) { Name = equipment.Name; Description = equipment.Description; + CreationDate = equipment.CreationDate; Images = new ObservableCollection(equipment.Images.Select(i => new ImageViewModel(i))); Properties = new ObservableCollection(equipment.Properties.Select(e => new PropertyViewModel(e))); + Equipments = new ObservableCollection(equipment.Equipments.Select(e => new EquipmentViewModel(e){Parent = this})); var times = equipment.Inspections.Select(e => new InspectionViewModel(e)) .Select(e => e.GetNearestDates()) @@ -59,14 +105,20 @@ public partial class EquipmentViewModel : ViewModelBase if (Inspections.Count == 0) NearestInspection = "Нету инспекций"; Equipment = equipment; + + WeakReferenceMessenger.Default.Register(this, RemoveEquipment); } public Equipment GetEquipment() { Equipment.Name = Name; Equipment.Description = Description; + Equipment.CreationDate = CreationDate; Equipment.Images = Images.Select(e => e.GetImage()).ToList(); Equipment.Inspections = Inspections.Select(e => e.GetInspection()).ToList(); + Equipment.Properties = Properties.Select(e => e.GetProperty()).ToList(); + Equipment.Equipments = Equipments.Select(e => e.GetEquipment()).ToList(); + return Equipment; } } \ No newline at end of file diff --git a/ViewModels/Basic/ImageViewModel.cs b/ViewModels/Basic/ImageViewModel.cs index 4e04f2b..f42432c 100644 --- a/ViewModels/Basic/ImageViewModel.cs +++ b/ViewModels/Basic/ImageViewModel.cs @@ -1,31 +1,49 @@ -using System.IO; +using System; +using System.IO; +using System.Threading.Tasks; using Avalonia.Media.Imaging; using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using GazpromApp.Messages; using GazpromApp.Models; namespace GazpromApp.ViewModels; public partial class ImageViewModel : ViewModelBase { + public ViewModelBase? Parent; private Image Image { get; } - - [ObservableProperty] - private byte[]? _bytes; - + + [ObservableProperty] private byte[]? _bytes; + + [ObservableProperty] private DateTime _creationDate; + // EF stuff - - [ObservableProperty] - private Point? _point; - - [ObservableProperty] - private Equipment? _equipment; - - [ObservableProperty] - private Bitmap? _fileName; + + [ObservableProperty] private Point? _point; + + [ObservableProperty] private Equipment? _equipment; + + [ObservableProperty] private Bitmap? _fileName; + + [RelayCommand] + private async Task ViewImage() => _ = await WeakReferenceMessenger.Default.Send(new ViewImageMessage(this)); + + [RelayCommand] + private void DeleteImage() => _ = Parent switch + { + PointViewModel pmv => pmv.Images.Remove(this), + EquipmentViewModel emv => emv.Images.Remove(this), + EditPointViewModel editPointViewModel => editPointViewModel.Images.Remove(this), + EditEquipmentViewModel editEquipmentViewModel => editEquipmentViewModel.Images.Remove(this), + _ => false + }; public ImageViewModel(Image image) { Bytes = image.Bytes; + CreationDate = image.CreationDate; Equipment = image.Equipment; Point = image.Point; FileName = image.Bytes is { Length: > 0 } ? new Bitmap(new MemoryStream(image.Bytes)) : null; @@ -35,6 +53,7 @@ public partial class ImageViewModel : ViewModelBase public ImageViewModel() { Bytes = null; + CreationDate = DateTime.Now; Equipment = null; Point = null; Image = new Image(); @@ -45,6 +64,7 @@ public partial class ImageViewModel : ViewModelBase var ms = new MemoryStream(); FileName?.Save(ms); Image.Bytes = ms.ToArray(); + Image.CreationDate = CreationDate; Image.Equipment = Equipment; Image.Point = Point; return Image; diff --git a/ViewModels/Basic/InspectionViewModel.cs b/ViewModels/Basic/InspectionViewModel.cs index bf7f4b9..1a18eea 100644 --- a/ViewModels/Basic/InspectionViewModel.cs +++ b/ViewModels/Basic/InspectionViewModel.cs @@ -1,6 +1,10 @@ using System; +using System.Linq; using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; using GazpromApp.Enums; +using GazpromApp.Messages; using GazpromApp.Models; namespace GazpromApp.ViewModels; @@ -11,6 +15,8 @@ public partial class InspectionViewModel : ViewModelBase [ObservableProperty] private string _name; [ObservableProperty] + private DateTime _creationDate; + [ObservableProperty] private DateTimeOffset _inspectionDate; [ObservableProperty] private PeriodType _periodType; @@ -21,10 +27,25 @@ public partial class InspectionViewModel : ViewModelBase [ObservableProperty] private bool _isEditing; + + [RelayCommand] + private void EditInspection() + { + IsEditing = !IsEditing; + } + + [RelayCommand] + private void RemoveInspection() => WeakReferenceMessenger.Default.Send(new RemoveInspectionMessage(this)); + + // ReSharper disable once MemberCanBeMadeStatic.Global + public PeriodType[] AllPeriods => Enum.GetValues() + .Where(p => p != PeriodType.Unknown) + .ToArray(); public InspectionViewModel() { Name = string.Empty; + CreationDate = DateTime.Now; InspectionDate = DateTimeOffset.Now; PeriodType = 0; PeriodValue = 1; @@ -34,6 +55,7 @@ public partial class InspectionViewModel : ViewModelBase public InspectionViewModel(Inspection inspection) { Name = inspection.Name; + CreationDate = inspection.CreationDate; InspectionDate = inspection.InspectionDate; PeriodType = inspection.PeriodType.ToPeriodType(); PeriodValue = inspection.PeriodValue; @@ -43,6 +65,7 @@ public partial class InspectionViewModel : ViewModelBase public Inspection GetInspection() { Inspection.Name = Name; + Inspection.CreationDate = CreationDate; Inspection.InspectionDate = InspectionDate; Inspection.PeriodType = PeriodType.ToDescription(); Inspection.PeriodValue = PeriodValue; diff --git a/ViewModels/Basic/PointViewModel.cs b/ViewModels/Basic/PointViewModel.cs index dc826ae..fd1f3d0 100644 --- a/ViewModels/Basic/PointViewModel.cs +++ b/ViewModels/Basic/PointViewModel.cs @@ -1,5 +1,12 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using GazpromApp.Messages; using GazpromApp.Models; namespace GazpromApp.ViewModels; @@ -14,39 +21,64 @@ public partial class PointViewModel : ViewModelBase [ObservableProperty] private string? _description; + [ObservableProperty] + private DateTime _creationDate; + [ObservableProperty] private double? _latitude; [ObservableProperty] private double? _longitude; + + [RelayCommand] + private void AddEquipment() + { + Equipment.Add(new EquipmentViewModel(new Equipment()) {Name = "Новое оборудование", Description = "Описание оборудования", Parent = this}); + WeakReferenceMessenger.Default.Send(new EditPointMessage(this)); + } + + [RelayCommand] + private void RemovePoint() + { + WeakReferenceMessenger.Default.Send(new RemovePointMessage(this)); + } + + private void RemoveEquipment(object sender, RemoveEquipmentMessage msg) + { + if (msg.Equipment.GetEquipment().Point == GetPoint()) + Equipment.Remove(msg.Equipment); + } - // EF stuff [ObservableProperty] - private List _images; + private ObservableCollection _images; [ObservableProperty] - private List _equipment; + private ObservableCollection _equipment; [ObservableProperty] - private List _properties; + private ObservableCollection _properties; public PointViewModel(Point point) { Point = point; Name = point.Name; + CreationDate = point.CreationDate; Description = point.Description; Latitude = point.Latitude; Longitude = point.Longitude; - Images = point.Images; - Equipment = point.Equipment; - Properties = point.Properties; + Images = new ObservableCollection(point.Images.Select(i => new ImageViewModel(i))); + Equipment = new ObservableCollection(point.Equipment.Select(i => new EquipmentViewModel(i){Parent = this})); + Properties = new ObservableCollection(point.Properties.Select(i => new PropertyViewModel(i))); + + WeakReferenceMessenger.Default.Register(this, RemoveEquipment); } public Point GetPoint() { Point.Name = Name; Point.Description = Description; - Point.Equipment = Equipment; - Point.Images = Images; - Point.Properties = Properties; + Point.CreationDate = CreationDate; + Point.Equipment = Equipment.Select(e => e.GetEquipment()).ToList(); + Point.Images = Images.Select(i => i.GetImage()).ToList(); + Point.Properties = Properties.Select(p => p.GetProperty()).ToList(); return Point; } } \ No newline at end of file diff --git a/ViewModels/Basic/PropertyViewModel.cs b/ViewModels/Basic/PropertyViewModel.cs index 32b20ed..616577f 100644 --- a/ViewModels/Basic/PropertyViewModel.cs +++ b/ViewModels/Basic/PropertyViewModel.cs @@ -1,6 +1,10 @@ -using System.Collections.ObjectModel; +using System; +using System.Collections.ObjectModel; using System.Linq; using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using GazpromApp.Messages; using GazpromApp.Models; namespace GazpromApp.ViewModels; @@ -8,6 +12,8 @@ namespace GazpromApp.ViewModels; public partial class PropertyViewModel : ViewModelBase { + public ViewModelBase? Parent { get; init; } + private Property Property { get; } [ObservableProperty] @@ -16,19 +22,36 @@ public partial class PropertyViewModel : ViewModelBase [ObservableProperty] private string _value; + [ObservableProperty] + private DateTime _creationDate; + public ObservableCollection Properties { get; set; } // Visual and other properties - public PropertyViewModel? Parent { get; init; } - [ObservableProperty] private bool _isEditing; + [RelayCommand] + private void AddProperty() => Properties.Add(new PropertyViewModel() {Parent = this}); + + [RelayCommand] + private void RemoveProperty() => WeakReferenceMessenger.Default.Send(new RemovePropertyMessage(this)); + + [RelayCommand] + private void EditProperty() => IsEditing = !IsEditing; + + private void RemoveProperty(object sender, RemovePropertyMessage msg) + { + if (Properties.Contains(msg.Property)) + Properties.Remove(msg.Property); + } + public Property GetProperty(Property? parent = null) { Property.Name = Name; Property.Value = Value; + Property.CreationDate = CreationDate; Property.Parent = parent; Property.Properties = Properties.Select(e => e.GetProperty(Property)).ToList(); return Property; @@ -38,19 +61,25 @@ public partial class PropertyViewModel : ViewModelBase { Name = "Имя свойства"; Value = "Значение свойства"; + CreationDate = DateTime.Now; Properties = []; Parent = null; Property = new Property(); + + WeakReferenceMessenger.Default.Register(this, RemoveProperty); } public PropertyViewModel(Property property, PropertyViewModel? parent = null) { Name = property.Name; Value = property.Value; + CreationDate = property.CreationDate; Properties = new ObservableCollection( property.Properties.Select(e => new PropertyViewModel(e, this))); Parent = property.Parent is null ? null : parent; Property = property; + + WeakReferenceMessenger.Default.Register(this, RemoveProperty); } public PropertyViewModel(PropertyViewModel property) @@ -60,5 +89,7 @@ public partial class PropertyViewModel : ViewModelBase Properties = []; Parent = property; Property = new Property(); + + WeakReferenceMessenger.Default.Register(this, RemoveProperty); } } \ No newline at end of file diff --git a/ViewModels/Basic/SaveChangesViewModel.cs b/ViewModels/Basic/SaveChangesViewModel.cs new file mode 100644 index 0000000..34153c2 --- /dev/null +++ b/ViewModels/Basic/SaveChangesViewModel.cs @@ -0,0 +1,42 @@ +using Avalonia.Controls; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using GazpromApp.Enums; +using GazpromApp.Messages; +using GazpromApp.Models; +using GazpromApp.Views; + +namespace GazpromApp.ViewModels; + +public partial class SaveChangesViewModel : ViewModelBase +{ + [ObservableProperty] + private bool _isSave; + [ObservableProperty] + private bool _isCancel; + + [RelayCommand] + private void Cancel(Window w) + { + ((SaveChangesView)w).CanClose = true; + WeakReferenceMessenger.Default.Send(new SaveChangesResult(false, true)); + w.Close(this); + } + + [RelayCommand] + private void Save(Window w) + { + ((SaveChangesView)w).CanClose = true; + WeakReferenceMessenger.Default.Send(new SaveChangesResult(true, false)); + w.Close(this); + } + + [RelayCommand] + private void DontSave(Window w) + { + ((SaveChangesView)w).CanClose = true; + WeakReferenceMessenger.Default.Send(new SaveChangesResult(false, false)); + w.Close(this); + } +} \ No newline at end of file diff --git a/ViewModels/EditEquipmentDialogViewModel.cs b/ViewModels/EditEquipmentViewModel.cs similarity index 57% rename from ViewModels/EditEquipmentDialogViewModel.cs rename to ViewModels/EditEquipmentViewModel.cs index 68d9813..fcb9b2a 100644 --- a/ViewModels/EditEquipmentDialogViewModel.cs +++ b/ViewModels/EditEquipmentViewModel.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using System.Web; using Avalonia.Controls; using Avalonia.Media.Imaging; -using Avalonia.Platform.Storage; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; @@ -16,26 +15,15 @@ using Image = GazpromApp.Models.Image; namespace GazpromApp.ViewModels; -public partial class EditEquipmentDialogViewModel : ViewModelBase +public partial class EditEquipmentViewModel : ViewModelBase { [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(CompleteEditCommand))] private string? _name; - [ObservableProperty] - [NotifyCanExecuteChangedFor(nameof(CompleteEditCommand))] - private string? _description; - [ObservableProperty] private bool _isNameEditing; - [ObservableProperty] - private bool _isDescriptionEditing; - - public ObservableCollection Images { get; set; } - - public ObservableCollection Inspections { get; set; } - [RelayCommand] private void EditName() { @@ -43,6 +31,13 @@ public partial class EditEquipmentDialogViewModel : ViewModelBase IsDescriptionEditing = false; } + [ObservableProperty] + [NotifyCanExecuteChangedFor(nameof(CompleteEditCommand))] + private string? _description; + + [ObservableProperty] + private bool _isDescriptionEditing; + [RelayCommand] private void EditDescription() { @@ -50,20 +45,17 @@ public partial class EditEquipmentDialogViewModel : ViewModelBase IsNameEditing = false; } + public ObservableCollection Images { get; set; } + [RelayCommand] private async Task AddImage(Window w) { - var files = await w.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions - { - AllowMultiple = true, - Title = "Выберите одну или несколько картинок", - FileTypeFilter = [FilePickerFileTypes.ImageAll] - }); + var files = await WeakReferenceMessenger.Default.Send(new RequestImageMessage(this)); foreach (var file in files) { try { - Images.Add(new ImageViewModel(new Image()) { FileName = new Bitmap(HttpUtility.UrlDecode(file.Path.AbsolutePath) ) }); + Images.Add(new ImageViewModel(new Image()) { FileName = new Bitmap(HttpUtility.UrlDecode(file.Path.AbsolutePath)), Parent = this}); } catch (ArgumentException) { @@ -76,35 +68,20 @@ public partial class EditEquipmentDialogViewModel : ViewModelBase _ = await WeakReferenceMessenger.Default.Send(new ErrorMessage(new ErrorViewModel(error))); } } - - IsImagesEmpty = Images.Count == 0; } + public ObservableCollection Inspections { get; set; } + [RelayCommand] - private static async Task ViewImage(ImageViewModel image) => _ = await WeakReferenceMessenger.Default.Send(new ViewImageMessage(image)); + private void AddInspection() => Inspections.Add(new InspectionViewModel + { Name = "Название инспекции", InspectionDate = DateTimeOffset.Now, PeriodValue = 1, PeriodType = PeriodType.Day }); + + private void RemoveInspection(object sender, RemoveInspectionMessage msg) + { + if (Inspections.Contains(msg.Inspection)) + Inspections.Remove(msg.Inspection); + } - [RelayCommand] - private void DeleteImage(ImageViewModel image) - { - Images.Remove(image); - IsImagesEmpty = Images.Count == 0; - } - - [RelayCommand] - private void AddInspection() - { - Inspections.Add(new InspectionViewModel { Name = "Название инспекции", InspectionDate = DateTimeOffset.Now, PeriodValue = 1, PeriodType = PeriodType.Day }); - } - - [RelayCommand] - private void EditInspection(InspectionViewModel inspection) - { - foreach (var item in Inspections) - if (item != inspection) - item.IsEditing = false; - inspection.IsEditing = !inspection.IsEditing; - } - [RelayCommand] private void DeleteInspection(InspectionViewModel inspection) { @@ -114,21 +91,12 @@ public partial class EditEquipmentDialogViewModel : ViewModelBase public ObservableCollection Properties { get; set; } [RelayCommand] - private void AddPropertySimple() => Properties.Add(new PropertyViewModel()); + private void AddProperty() => Properties.Add(new PropertyViewModel {Parent = this}); - [RelayCommand] - private static void AddProperty(PropertyViewModel property) => property.Properties.Add(new PropertyViewModel(property)); - - [RelayCommand] - private static void EditProperty(PropertyViewModel property) => property.IsEditing = !property.IsEditing; - - [RelayCommand] - private void DeleteProperty(PropertyViewModel property) + private void RemoveProperty(object sender, RemovePropertyMessage msg) { - if (property.Parent is null) - Properties.Remove(property); - else - property.Parent?.Properties.Remove(property); + if (Properties.Contains(msg.Property)) + Properties.Remove(msg.Property); } private bool CanCompleteEdit() => !string.IsNullOrWhiteSpace(Name); @@ -136,32 +104,24 @@ public partial class EditEquipmentDialogViewModel : ViewModelBase [RelayCommand(CanExecute = nameof(CanCompleteEdit))] private void CompleteEdit() { - IsClosed = true; - var equipment = new Equipment - { - Name = Name, - Description = Description, - Images = Images.Select(e => e.GetImage()).ToList(), - Inspections = Inspections.Select(e => e.GetInspection()).ToList(), - Properties = Properties.Select(e => e.GetProperty()).ToList() - }; - WeakReferenceMessenger.Default.Send(new EditEquipmentDone(new EquipmentViewModel(equipment))); + _equipment.Name = Name; + _equipment.Description = Description; + _equipment.Images = Images; + _equipment.Inspections = Inspections; + _equipment.Properties = Properties; + _ = _equipment.GetEquipment(); + + CallUpdatePoint(_equipment); } - + [RelayCommand] private void CancelEdit(Window w) { - IsClosed = true; - WeakReferenceMessenger.Default.Send(new EditEquipmentDone(_equipment)); + CallUpdatePoint(_equipment); } // Visual and other properties - - [ObservableProperty] - private bool _isImagesEmpty; - [ObservableProperty] - private bool _isClosed; private readonly EquipmentViewModel _equipment; @@ -169,8 +129,23 @@ public partial class EditEquipmentDialogViewModel : ViewModelBase public PeriodType[] AllPeriods => Enum.GetValues() .Where(p => p != PeriodType.Unknown) .ToArray(); + + private void CallUpdatePoint(ViewModelBase vm) + { + switch (vm) + { + case PointViewModel pointViewModel: + WeakReferenceMessenger.Default.Send(new EditPointMessage(pointViewModel)); + break; + case EquipmentViewModel equipmentViewModel: + CallUpdatePoint(equipmentViewModel.Parent ?? throw new InvalidOperationException("Parent equipment not found in hierarchy")); + break; + default: + throw new ArgumentOutOfRangeException(nameof(vm), vm, null); + } + } - public EditEquipmentDialogViewModel() + public EditEquipmentViewModel() { Name = "Testing"; Description = "Testing"; @@ -178,12 +153,10 @@ public partial class EditEquipmentDialogViewModel : ViewModelBase Inspections = []; Properties = []; - IsImagesEmpty = true; - _equipment = new EquipmentViewModel(); } - public EditEquipmentDialogViewModel(EquipmentViewModel equipment) + public EditEquipmentViewModel(EquipmentViewModel equipment) { Name = equipment.Name; Description = equipment.Description; @@ -191,8 +164,14 @@ public partial class EditEquipmentDialogViewModel : ViewModelBase Inspections = new ObservableCollection(equipment.Inspections.Select(e => e)); Properties = new ObservableCollection(equipment.Properties.Select(e => e)); - IsImagesEmpty = Images.Count == 0; - _equipment = equipment; + + WeakReferenceMessenger.Default.Register(this, RemoveProperty); + WeakReferenceMessenger.Default.Register(this, RemoveInspection); + } + + ~EditEquipmentViewModel() + { + WeakReferenceMessenger.Default.UnregisterAll(this); } } \ No newline at end of file diff --git a/ViewModels/EditPointDialogViewModel.cs b/ViewModels/EditPointViewModel.cs similarity index 64% rename from ViewModels/EditPointDialogViewModel.cs rename to ViewModels/EditPointViewModel.cs index 4720234..7cedae1 100644 --- a/ViewModels/EditPointDialogViewModel.cs +++ b/ViewModels/EditPointViewModel.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using System.Web; using Avalonia.Controls; using Avalonia.Media.Imaging; -using Avalonia.Platform.Storage; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; @@ -16,7 +15,7 @@ using Image = GazpromApp.Models.Image; namespace GazpromApp.ViewModels; -public partial class EditPointDialogViewModel : ViewModelBase +public partial class EditPointViewModel : ViewModelBase { [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(CompleteEditCommand))] @@ -51,17 +50,12 @@ public partial class EditPointDialogViewModel : ViewModelBase [RelayCommand] private async Task AddImage(Window w) { - var files = await w.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions - { - AllowMultiple = true, - Title = "Выберите одну или несколько картинок", - FileTypeFilter = [FilePickerFileTypes.ImageAll] - }); + var files = await WeakReferenceMessenger.Default.Send(new RequestImageMessage(this)); foreach (var file in files) { try { - Images.Add(new ImageViewModel(new Image()) { FileName = new Bitmap(HttpUtility.UrlDecode(file.Path.AbsolutePath) ) }); + Images.Add(new ImageViewModel(new Image()) { FileName = new Bitmap(HttpUtility.UrlDecode(file.Path.AbsolutePath)), Parent = this}); } catch (ArgumentException) { @@ -74,66 +68,19 @@ public partial class EditPointDialogViewModel : ViewModelBase _ = await WeakReferenceMessenger.Default.Send(new ErrorMessage(new ErrorViewModel(error))); } } - - IsImagesEmpty = Images.Count == 0; } - [RelayCommand] - private void DeleteImage(ImageViewModel image) - { - Images.Remove(image); - IsImagesEmpty = Images.Count == 0; - } - - [RelayCommand] - private static async Task ViewImage(ImageViewModel image) => _ = await WeakReferenceMessenger.Default.Send(new ViewImageMessage(image)); - public ObservableCollection Equipment { get; set; } - [RelayCommand] - private async Task AddEquipment() - { - var equipment = await WeakReferenceMessenger.Default.Send(new NewEquipmentMessage()); - - if (equipment != null) - Equipment.Add(equipment); - - IsEquipmentEmpty = Equipment.Count == 0; - } - - [RelayCommand] - private async Task EditEquipment(EquipmentViewModel equipment) - { - var e = await WeakReferenceMessenger.Default.Send(new EditEquipmentMessage(equipment)) ?? equipment; - Equipment[Equipment.IndexOf(equipment)] = e; - } - - [RelayCommand] - private void DeleteEquipment(EquipmentViewModel equipment) - { - Equipment.Remove(equipment); - - IsEquipmentEmpty = Equipment.Count == 0; - } - public ObservableCollection Properties { get; set; } [RelayCommand] - private void AddPropertySimple() => Properties.Add(new PropertyViewModel()); + private void AddProperty() => Properties.Add(new PropertyViewModel {Parent = this}); - [RelayCommand] - private static void AddProperty(PropertyViewModel property) => property.Properties.Add(new PropertyViewModel(property)); - - [RelayCommand] - private static void EditProperty(PropertyViewModel property) => property.IsEditing = !property.IsEditing; - - [RelayCommand] - private void DeleteProperty(PropertyViewModel property) + private void RemoveProperty(object sender, RemovePropertyMessage msg) { - if (property.Parent is null) - Properties.Remove(property); - else - property.Parent?.Properties.Remove(property); + if (Properties.Contains(msg.Property)) + Properties.Remove(msg.Property); } private bool CanCompleteEdit() => !string.IsNullOrWhiteSpace(Name); @@ -141,41 +88,30 @@ public partial class EditPointDialogViewModel : ViewModelBase [RelayCommand(CanExecute = nameof(CanCompleteEdit))] private void CompleteEdit() { - IsClosed = true; _point.Name = Name; _point.Description = Description; - _point.Images = Images.Select(e => e.GetImage()).ToList(); - _point.Equipment = Equipment.Select(e => e.GetEquipment()).ToList(); - _point.Properties = Properties.Select(e => e.GetProperty()).ToList(); + _point.Images = Images; + _point.Equipment = Equipment; + _point.Properties = Properties; _ = _point.GetPoint(); - WeakReferenceMessenger.Default.Send(new EditPointDone(new PointViewModel(_point.GetPoint()))); + WeakReferenceMessenger.Default.Send(new EditPointMessage(_point)); } [RelayCommand] private void CancelEdit(Window w) { - IsClosed = true; - WeakReferenceMessenger.Default.Send(new EditPointDone(_point)); + WeakReferenceMessenger.Default.Send(new EditPointMessage(_point)); } // Visual and other properties - [ObservableProperty] - private bool _isImagesEmpty; - - [ObservableProperty] - private bool _isEquipmentEmpty; - - [ObservableProperty] - private bool _isClosed; - private readonly PointViewModel _point; - public EditPointDialogViewModel() + public EditPointViewModel() { - Name = ""; - Description = ""; + Name = "fsdfasdfasd"; + Description = "asfasdfasdfa"; Images = []; Equipment = [ new EquipmentViewModel @@ -245,23 +181,25 @@ public partial class EditPointDialogViewModel : ViewModelBase } ]; - IsImagesEmpty = true; - IsEquipmentEmpty = true; - - _point = new PointViewModel(new Point()); + _point = new PointViewModel(new Point()); } - public EditPointDialogViewModel(PointViewModel point) + public EditPointViewModel(PointViewModel point) { Name = point.Name; Description = point.Description; - Images = new ObservableCollection(point.Images.Select(e => new ImageViewModel(e))); - Equipment = new ObservableCollection(point.Equipment.Select(e => new EquipmentViewModel(e))); - Properties = new ObservableCollection(point.Properties.Select(e => new PropertyViewModel(e))); - - IsImagesEmpty = Images.Count == 0; - IsEquipmentEmpty = Equipment.Count == 0; + Images = new ObservableCollection(point.Images); + Images.ToList().ForEach(i => i.Parent = this); + Equipment = new ObservableCollection(point.Equipment); + Properties = new ObservableCollection(point.Properties); _point = point; + + WeakReferenceMessenger.Default.Register(this, RemoveProperty); + } + + ~EditPointViewModel() + { + WeakReferenceMessenger.Default.UnregisterAll(this); } } \ No newline at end of file diff --git a/ViewModels/MainWindowViewModel.cs b/ViewModels/MainWindowViewModel.cs index 33ec53a..b803084 100644 --- a/ViewModels/MainWindowViewModel.cs +++ b/ViewModels/MainWindowViewModel.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using System.IO; using System.Collections.ObjectModel; +using System.IO; using System.Linq; using System.Threading.Tasks; using System.Web; @@ -21,14 +21,19 @@ public partial class MainWindowViewModel : ViewModelBase { public ObservableCollection Points { get; set; } + [ObservableProperty] + private object? _selectedItem; + + [ObservableProperty] + private ViewModelBase? _selectedItemViewModel; + [RelayCommand] private async Task AddPoint() { - var point = await WeakReferenceMessenger.Default.Send(new NewPointMessage()); - if (point == null) return; - IsLoading = true; + var point = new PointViewModel(new Point {Name = "Новый объект", Description = "Описание объекта"}); + _context.Point.Add(point.GetPoint()); await _context.SaveChangesAsync(); @@ -37,49 +42,16 @@ public partial class MainWindowViewModel : ViewModelBase IsLoading = false; } - [RelayCommand] - private async Task EditPoint(PointViewModel point) - { - var p = await WeakReferenceMessenger.Default.Send(new EditPointMessage(point)); - if (p == null) return; - - IsLoading = true; - - _context.Update(p.GetPoint()); - await _context.SaveChangesAsync(); - - var index = Points.IndexOf(point); - if (index != -1) - Points[index] = p; - - IsLoading = false; - } - - [RelayCommand] - private async Task RemovePoint(PointViewModel point) - { - IsLoading = true; - - var pointInDb = await _context.Point.FindAsync(point.GetPoint().Id); - if (pointInDb == null) return; - - _context.Remove(pointInDb); - await _context.SaveChangesAsync(); - - Points.Remove(point); - - IsLoading = false; - } - [RelayCommand] private async Task ImportPoint(Window w) { + Directory.CreateDirectory(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "GazpromApp")); var files = await w.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { AllowMultiple = true, SuggestedStartLocation = await w.StorageProvider.TryGetFolderFromPathAsync( - Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)) + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "GazpromApp")) }); if (files.Count < 1) return; @@ -92,6 +64,7 @@ public partial class MainWindowViewModel : ViewModelBase var decoded = HttpUtility.UrlDecode(url); var point = DataSerialization.LoadPoint(decoded); point?.SetPropertyParents(); + point?.SetEquipmentParents(); if (point == null) continue; _context.Point.Add(point); @@ -108,33 +81,81 @@ public partial class MainWindowViewModel : ViewModelBase [RelayCommand] private async Task ExportPoint(Window w) { + Directory.CreateDirectory(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "GazpromApp")); var folder = await w.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions { AllowMultiple = false, SuggestedStartLocation = await w.StorageProvider.TryGetFolderFromPathAsync( - Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)) + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "GazpromApp")) }); if (folder.Count != 1) return; foreach (var point in Points) DataSerialization.SavePoint(point.GetPoint(), Path.Combine(HttpUtility.UrlDecode(folder[0].Path.AbsolutePath), point.Name + ".json") ); } + + [ObservableProperty] + [NotifyCanExecuteChangedFor(nameof(SyncDbCommand))] + private bool _canSync = false; + + private bool CanSyncDb() => CanSync; + + [RelayCommand(CanExecute = nameof(CanSyncDb))] + private async Task SyncDb() + { + IsLoading = true; + + foreach (var point in _pointsToUpdate) + { + _context.Update(point.GetPoint()); + + var index = Points.IndexOf(point); + if (index != -1) + Points[index] = point; + } + await _context.SaveChangesAsync(); + + foreach (var point in _pointsToDelete) + { + var pointInDb = await _context.Point.FindAsync(point.GetPoint().Id); + if (pointInDb == null) continue; + + _context.Remove(pointInDb); + + Points.Remove(point); + } + await _context.SaveChangesAsync(); + + _pointsToUpdate.Clear(); + _pointsToDelete.Clear(); + + + IsLoading = false; + CanSync = false; + } // Visual and other properties [ObservableProperty] private bool _isLoading = true; private readonly AppDbContext _context; - private async Task LoadChildrenForProperties(List properties) + private async Task LoadChildrenForProperties(List properties, HashSet loadedIds) { if (properties.Count == 0) return; - var parentIds = properties.Select(p => p.Id).ToList(); + var parentIds = properties.Select(p => p.Id) + .Where(id => !loadedIds.Contains(id)) + .ToList(); + + if (parentIds.Count == 0) + return; + + parentIds.ForEach(id => loadedIds.Add(id)); var children = await _context.Property .Where(p => p.ParentPropertyId.HasValue && parentIds.Contains(p.ParentPropertyId.Value)) - .AsNoTracking() + .OrderBy(p => p.CreationDate) .ToListAsync(); if (children.Count != 0) @@ -142,36 +163,85 @@ public partial class MainWindowViewModel : ViewModelBase var childrenLookup = children.ToLookup(child => child.ParentPropertyId); foreach (var parent in properties.Where(parent => childrenLookup.Contains(parent.Id))) - parent.Properties.AddRange(childrenLookup[parent.Id]); + { + parent.Properties.Clear(); + parent.Properties.AddRange(childrenLookup[parent.Id]); + } - // ReSharper disable once TailRecursiveCall - await LoadChildrenForProperties(children); + await LoadChildrenForProperties(children, loadedIds); } } + + private async Task LoadChildrenForEquipments(List equipments, HashSet loadedIds) + { + if (equipments.Count == 0) + return; + + var parentIds = equipments.Select(e => e.Id) + .Where(id => !loadedIds.Contains(id)) + .ToList(); + + if (parentIds.Count == 0) + return; + + parentIds.ForEach(id => loadedIds.Add(id)); + + var children = await _context.Equipment + .Where(e => e.ParentEquipmentId.HasValue && parentIds.Contains(e.ParentEquipmentId.Value)) + .OrderBy(e => e.CreationDate) + .ToListAsync(); + + if (children.Count != 0) + { + var childrenLookup = children.ToLookup(child => child.ParentEquipmentId); + + foreach (var parent in equipments.Where(parent => childrenLookup.Contains(parent.Id))) + { + parent.Equipments.Clear(); + parent.Equipments.AddRange(childrenLookup[parent.Id]); + } + + await LoadChildrenForEquipments(children, loadedIds); + } + } + private async Task LoadDataAsync() { IsLoading = true; + // ReSharper disable once EntityFramework.NPlusOne.IncompleteDataQuery var pointsFromDb = await _context.Point - .Include(p => p.Images) + .OrderBy(p => p.CreationDate) + .Include(p => p.Images + .OrderBy(i => i.CreationDate)) + .Include(p => p.Equipment + .OrderBy(e => e.CreationDate)) + .ThenInclude(e => e.Properties + .OrderBy(p => p.CreationDate)) .Include(p => p.Equipment) - .ThenInclude(e => e.Properties) + .ThenInclude(e => e.Images + .OrderBy(i => i.CreationDate)) .Include(p => p.Equipment) - .ThenInclude(e => e.Images) - .Include(p => p.Equipment) - .ThenInclude(e => e.Inspections) - .Include(p => p.Properties) - .AsNoTracking() + .ThenInclude(e => e.Inspections + .OrderBy(i => i.CreationDate)) + .Include(p => p.Properties + .OrderBy(p => p.CreationDate)) .ToListAsync(); var topLevelProperties = pointsFromDb.SelectMany(p => p.Properties) .Concat(pointsFromDb.SelectMany(p => p.Equipment).SelectMany(e => e.Properties)) .ToList(); - if (topLevelProperties.Count != 0) - await LoadChildrenForProperties(topLevelProperties); + var topLevelEquipments = pointsFromDb.SelectMany(p => p.Equipment) + .ToList(); + if (topLevelProperties.Count != 0) + await LoadChildrenForProperties(topLevelProperties, []); + + if (topLevelEquipments.Count != 0) + await LoadChildrenForEquipments(topLevelEquipments, []); + Points.Clear(); foreach (var point in pointsFromDb) Points.Add(new PointViewModel(point)); @@ -179,16 +249,48 @@ public partial class MainWindowViewModel : ViewModelBase IsLoading = false; } + private readonly ObservableCollection _pointsToUpdate = []; + private readonly ObservableCollection _pointsToDelete = []; + + private void EditPoint(object sender, EditPointMessage msg) + { + if (!_pointsToUpdate.Contains(msg.Point)) + _pointsToUpdate.Add(msg.Point); + CanSync = true; + } + + private void RemovePoint(object sender, RemovePointMessage msg) + { + if (!_pointsToDelete.Contains(msg.Point)) + _pointsToDelete.Add(msg.Point); + Points.Remove(msg.Point); + CanSync = true; + } + + private void HandleSelectionChanged(object sender, SelectionChangedMessage msg) + { + SelectedItemViewModel = msg.ViewModel switch + { + PointViewModel pointViewModel => new EditPointViewModel(pointViewModel), + EquipmentViewModel equipmentViewModel => new EditEquipmentViewModel(equipmentViewModel), + _ => msg.ViewModel + }; + } + public MainWindowViewModel(AppDbContext context) { Points = []; _context = context; _ = LoadDataAsync(); + + WeakReferenceMessenger.Default.Register(this, EditPoint); + WeakReferenceMessenger.Default.Register(this, RemovePoint); + WeakReferenceMessenger.Default.Register(this, HandleSelectionChanged); } public MainWindowViewModel() { - Points = [new PointViewModel(new Point()) { Name = "Name test", Description = "Description test" }]; + Points = [new PointViewModel(new Point()) { Name = "Новый объект", Description = "Описание объекта" }]; _context = new AppDbContext(); IsLoading = false; } diff --git a/ViewModels/NewEquipmentDialogViewModel.cs b/ViewModels/NewEquipmentDialogViewModel.cs deleted file mode 100644 index 0b1e93b..0000000 --- a/ViewModels/NewEquipmentDialogViewModel.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections.ObjectModel; -using System.Linq; -using System.Threading.Tasks; -using System.Web; -using Avalonia.Controls; -using Avalonia.Media.Imaging; -using Avalonia.Platform.Storage; -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.Input; -using CommunityToolkit.Mvvm.Messaging; -using GazpromApp.Enums; -using GazpromApp.Messages; -using GazpromApp.Models; -using Image = GazpromApp.Models.Image; - -namespace GazpromApp.ViewModels; - -public partial class NewEquipmentDialogViewModel : ViewModelBase -{ - [ObservableProperty] - [NotifyCanExecuteChangedFor(nameof(EnterEquipmentCommand))] - private string? _name; - - [ObservableProperty] - [NotifyCanExecuteChangedFor(nameof(EnterEquipmentCommand))] - private string? _description; - - public ObservableCollection Images { get; } = []; - - [RelayCommand] - private async Task AddImage(Window w) - { - var files = await w.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions - { - AllowMultiple = true, - Title = "Выберите одну или несколько картинок", - FileTypeFilter = [FilePickerFileTypes.ImageAll] - }); - - foreach (var file in files) - { - try - { - Images.Add(new ImageViewModel(new Image()) { FileName = new Bitmap(HttpUtility.UrlDecode(file.Path.AbsolutePath)) }); - } - catch (ArgumentException) - { - var error = new Error - { - Title = "Ошибка обработки картинки", - Message = $"Картинка не была добавлена: \"{HttpUtility.UrlDecode(file.Name)}\"", - Severity = ErrorSeverity.Error - }; - _ = await WeakReferenceMessenger.Default.Send(new ErrorMessage(new ErrorViewModel(error))); - } - } - - IsImagesEmpty = Images.Count == 0; - } - - [RelayCommand] - private void DeleteImage(ImageViewModel image) - { - Images.Remove(image); - IsImagesEmpty = Images.Count == 0; - } - - private bool CanEnterEquipment() => !string.IsNullOrEmpty(Name); - - [RelayCommand (CanExecute = nameof(CanEnterEquipment))] - private void EnterEquipment() - { - var equipment = new Equipment - { - Name = Name, - Description = Description ?? string.Empty, - Images = Images.Select(e => e.GetImage()).ToList() - - }; - WeakReferenceMessenger.Default.Send(new NewEquipmentDone(new EquipmentViewModel(equipment))); - } - - [RelayCommand] - private static void CancelEnterEquipment(Window w) - { - w.Close(); - } - - // Visual and other stuff - - [ObservableProperty] - private bool _isImagesEmpty; -} \ No newline at end of file diff --git a/ViewModels/NewPointDialogViewModel.cs b/ViewModels/NewPointDialogViewModel.cs deleted file mode 100644 index b8a6c22..0000000 --- a/ViewModels/NewPointDialogViewModel.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.ObjectModel; -using System.Linq; -using System.Threading.Tasks; -using System.Web; -using Avalonia.Controls; -using Avalonia.Media.Imaging; -using Avalonia.Platform.Storage; -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.Input; -using CommunityToolkit.Mvvm.Messaging; -using GazpromApp.Enums; -using GazpromApp.Messages; -using GazpromApp.Models; -using Image = GazpromApp.Models.Image; -using Point = GazpromApp.Models.Point; - -namespace GazpromApp.ViewModels; - -public partial class NewPointDialogViewModel : ViewModelBase -{ - [ObservableProperty] - [NotifyCanExecuteChangedFor(nameof(EnterPointCommand))] - private string? _name; - - [ObservableProperty] - [NotifyCanExecuteChangedFor(nameof(EnterPointCommand))] - private string? _description; - - public ObservableCollection Images { get; } = []; - - [RelayCommand] - private async Task AddImage(Window w) - { - var files = await w.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions - { - AllowMultiple = true, - Title = "Выберите одну или несколько картинок", - FileTypeFilter = [FilePickerFileTypes.ImageAll] - }); - foreach (var file in files) - { - try - { - Images.Add(new ImageViewModel(new Image()) { FileName = new Bitmap(HttpUtility.UrlDecode(file.Path.AbsolutePath)) }); - } - catch (ArgumentException) - { - var error = new Error - { - Title = "Ошибка обработки картинки", - Message = $"Картинка не была добавлена: \"{HttpUtility.UrlDecode(file.Name)}\"", - Severity = ErrorSeverity.Error - }; - _ = await WeakReferenceMessenger.Default.Send(new ErrorMessage(new ErrorViewModel(error))); - } - } - - IsImagesEmpty = Images.Count == 0; - } - - [RelayCommand] - private void DeleteImage(ImageViewModel image) - { - Images.Remove(image); - IsImagesEmpty = Images.Count == 0; - } - - private bool CanEnterPoint() => !string.IsNullOrEmpty(Name); - - [RelayCommand (CanExecute = nameof(CanEnterPoint))] - private void EnterPoint() - { - var point = new Point - { - Name = Name, - Description = Description, - Images = Images.Select(e => e.GetImage()).ToList() - }; - WeakReferenceMessenger.Default.Send(new NewPointDone(new PointViewModel(point))); - } - - [RelayCommand] - private static void CancelEnterPoint(Window w) - { - w.Close(); - } - - // Visual properties - - [ObservableProperty] - private bool _isImagesEmpty = true; -} \ No newline at end of file diff --git a/Views/EditEquipmentDialog.axaml b/Views/EditEquipmentDialog.axaml deleted file mode 100644 index 6077be6..0000000 --- a/Views/EditEquipmentDialog.axaml +++ /dev/null @@ -1,225 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Views/EquipmentView.axaml.cs b/Views/EquipmentView.axaml.cs new file mode 100644 index 0000000..a559c6d --- /dev/null +++ b/Views/EquipmentView.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace GazpromApp.Views; + +public partial class EquipmentView : UserControl +{ + public EquipmentView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Views/InspectionView.axaml b/Views/InspectionView.axaml new file mode 100644 index 0000000..20a7939 --- /dev/null +++ b/Views/InspectionView.axaml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Views/InspectionView.axaml.cs b/Views/InspectionView.axaml.cs new file mode 100644 index 0000000..33a2c0c --- /dev/null +++ b/Views/InspectionView.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace GazpromApp.Views; + +public partial class InspectionView : UserControl +{ + public InspectionView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Views/MainWindow.axaml b/Views/MainWindow.axaml index aea9bcf..2408919 100644 --- a/Views/MainWindow.axaml +++ b/Views/MainWindow.axaml @@ -4,43 +4,27 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:anim="https://github.com/whistyun/AnimatedImage.Avalonia" xmlns:vm="using:GazpromApp.ViewModels" + xmlns:views="using:GazpromApp.Views" + xmlns:icons="using:Material.Icons.Avalonia" mc:Ignorable="d" d:DesignWidth="1024" d:DesignHeight="600" - Width="1024" Height="600" x:Class="GazpromApp.Views.MainWindow" x:DataType="vm:MainWindowViewModel" x:Name="Window" Icon="/Assets/gazpromapp.ico" Title="GazpromApp" - WindowStartupLocation="CenterScreen"> + WindowState="Maximized" + Closing="Window_OnClosing"> - - - - - - - - - - - - - - - - - - + - - + + @@ -48,7 +32,7 @@ CommandParameter="{Binding #Window}"> - + @@ -57,66 +41,41 @@ CommandParameter="{Binding #Window}"> - + - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/Views/MainWindow.axaml.cs b/Views/MainWindow.axaml.cs index 8539032..19b4c67 100644 --- a/Views/MainWindow.axaml.cs +++ b/Views/MainWindow.axaml.cs @@ -1,4 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Platform.Storage; using CommunityToolkit.Mvvm.Messaging; using GazpromApp.Messages; using GazpromApp.ViewModels; @@ -7,29 +13,79 @@ namespace GazpromApp.Views; public partial class MainWindow : Window { + private bool _canClose = false; public MainWindow() { InitializeComponent(); if (Design.IsDesignMode) return; + + WeakReferenceMessenger.Default.Register(this, HandleImageSelection); + WeakReferenceMessenger.Default.Register(this, HandleImageViewer); + WeakReferenceMessenger.Default.Register(this, HandleErrorViewer); + WeakReferenceMessenger.Default.Register(this, HandleSave); + } + + private static void HandleImageSelection(MainWindow sender, RequestImageMessage msg) + { + var files = sender.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions + { + AllowMultiple = true, + Title = "Выберите одну или несколько картинок", + FileTypeFilter = [FilePickerFileTypes.ImageAll] + }); + + msg.Reply(files); + } - WeakReferenceMessenger.Default.Register(this, static (w, m) => + private static void HandleImageViewer(MainWindow sender, ViewImageMessage msg) + { + var dialog = new ImageView { - var dialog = new NewPointDialog - { - DataContext = new NewPointDialogViewModel() - }; - m.Reply(dialog.ShowDialog(w)); - }); - - WeakReferenceMessenger.Default.Register(this, static (w, m) => + DataContext = msg.Image + }; + msg.Reply(dialog.ShowDialog(sender)); + } + + private static void HandleErrorViewer(MainWindow sender, ErrorMessage msg) + { + var dialog = new ErrorView { - var dialog = new EditPointDialog - { - DataContext = new EditPointDialogViewModel(m.Point) - }; - m.Reply(dialog.ShowDialog(w)); - }); + DataContext = msg.Error + }; + msg.Reply(dialog.ShowDialog(sender)); + } + + private void TreeView_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) + { + if (e.AddedItems.Count > 0) + WeakReferenceMessenger.Default.Send(new SelectionChangedMessage((ViewModelBase)e.AddedItems[0]!)); + } + + private void HandleSave(object sender, SaveChangesResult msg) + { + if (msg.Cancel) return; + if (msg.Save) + ((MainWindowViewModel)DataContext!).SyncDbCommand.Execute(null); + ForceClose(); + } + + private void ForceClose() + { + _canClose = true; + Close(); + } + + private void Window_OnClosing(object? sender, WindowClosingEventArgs e) + { + if (_canClose) return; + if (!((MainWindowViewModel)DataContext!).CanSync) return; + var dialog = new SaveChangesView + { + DataContext = new SaveChangesViewModel() + }; + dialog.ShowDialog(this); + e.Cancel = true; } } \ No newline at end of file diff --git a/Views/NewEquipmentDialog.axaml b/Views/NewEquipmentDialog.axaml deleted file mode 100644 index 647f83c..0000000 --- a/Views/NewEquipmentDialog.axaml +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Views/PointView.axaml.cs b/Views/PointView.axaml.cs new file mode 100644 index 0000000..d12bc0f --- /dev/null +++ b/Views/PointView.axaml.cs @@ -0,0 +1,14 @@ +using System.Windows.Input; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace GazpromApp.Views; + +public partial class PointView : UserControl +{ + public PointView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Views/PropertyView.axaml b/Views/PropertyView.axaml new file mode 100644 index 0000000..9508677 --- /dev/null +++ b/Views/PropertyView.axaml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + diff --git a/Views/PropertyView.axaml.cs b/Views/PropertyView.axaml.cs new file mode 100644 index 0000000..6014913 --- /dev/null +++ b/Views/PropertyView.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace GazpromApp.Views; + +public partial class PropertyView : UserControl +{ + public PropertyView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Views/SaveChangesView.axaml b/Views/SaveChangesView.axaml new file mode 100644 index 0000000..b3607c9 --- /dev/null +++ b/Views/SaveChangesView.axaml @@ -0,0 +1,40 @@ + + + + + + + + + + + +