diff --git a/.idea/.idea.GazpromApp/.idea/avalonia.xml b/.idea/.idea.GazpromApp/.idea/avalonia.xml index e376bd8..19730a9 100644 --- a/.idea/.idea.GazpromApp/.idea/avalonia.xml +++ b/.idea/.idea.GazpromApp/.idea/avalonia.xml @@ -4,6 +4,10 @@ diff --git a/GazpromApp.csproj b/GazpromApp.csproj index c2883fe..7118db3 100644 --- a/GazpromApp.csproj +++ b/GazpromApp.csproj @@ -9,20 +9,26 @@ - - + - - - - + + + + None All - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/Messages/EditPointDone.cs b/Messages/EditPointDone.cs new file mode 100644 index 0000000..6244253 --- /dev/null +++ b/Messages/EditPointDone.cs @@ -0,0 +1,8 @@ +using GazpromApp.ViewModels; + +namespace GazpromApp.Messages; + +public class EditPointDone(PointViewModel pointViewModel) +{ + public PointViewModel Point { get; } = pointViewModel; +} \ No newline at end of file diff --git a/Messages/EditPointMessage.cs b/Messages/EditPointMessage.cs new file mode 100644 index 0000000..3098dc8 --- /dev/null +++ b/Messages/EditPointMessage.cs @@ -0,0 +1,6 @@ +using CommunityToolkit.Mvvm.Messaging.Messages; +using GazpromApp.ViewModels; + +namespace GazpromApp.Messages; + +public class EditPointMessage : AsyncRequestMessage; \ No newline at end of file diff --git a/Messages/NewPointDone.cs b/Messages/NewPointDone.cs new file mode 100644 index 0000000..90048b6 --- /dev/null +++ b/Messages/NewPointDone.cs @@ -0,0 +1,8 @@ +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 new file mode 100644 index 0000000..73d4a80 --- /dev/null +++ b/Messages/NewPointMessage.cs @@ -0,0 +1,5 @@ +using GazpromApp.ViewModels; +using CommunityToolkit.Mvvm.Messaging.Messages; +namespace GazpromApp.Messages; + +public class NewPointMessage : AsyncRequestMessage; \ No newline at end of file diff --git a/Models/Equipment.cs b/Models/Equipment.cs new file mode 100644 index 0000000..fb7dbf1 --- /dev/null +++ b/Models/Equipment.cs @@ -0,0 +1,24 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace GazpromApp.Models; + +public class Equipment +{ + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + [Required] + public string Name { get; set; } = string.Empty; + [Required] + public string Description { get; set; } = string.Empty; + [Required] + public int PointId { get; set; } + public int ImageId { get; set; } + + // EF stuff + public Image? Image { get; set; } + public Models.Point? Point { get; set; } + public List Properties { get; } = []; +} \ No newline at end of file diff --git a/Models/Image.cs b/Models/Image.cs new file mode 100644 index 0000000..fc83231 --- /dev/null +++ b/Models/Image.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace GazpromApp.Models; +public class Image +{ + + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + [Required] + public byte[] Bytes { get; set; } = []; + // EF stuff + public List Points { get; set; } = []; + public List Equipment { get; set; } = []; +} \ No newline at end of file diff --git a/Models/Point.cs b/Models/Point.cs new file mode 100644 index 0000000..c0f1f50 --- /dev/null +++ b/Models/Point.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace GazpromApp.Models; + +public class Point +{ + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + [Required] + public string? Name { get; set; } = string.Empty; + [Required] + public string? Description { get; set; } = string.Empty; + [Required] + public double Latitude { get; set; } = 0; + [Required] + public double Longitude { get; set; } = 0; + // EF stuff + public List Images { get; set; } = []; + public List Equipment { get; set; } = []; + public List Properties { get; } = []; +} \ No newline at end of file diff --git a/Models/Property.cs b/Models/Property.cs new file mode 100644 index 0000000..250a8fe --- /dev/null +++ b/Models/Property.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace GazpromApp.Models; + +public class Property +{ + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + public int ParentId { get; set; } + [Required] + public string Name { get; set; } = string.Empty; + public string Value { get; set; } = string.Empty; + public List Points { get; set; } = []; + public List Equipment { get; } = []; +} \ No newline at end of file diff --git a/Program.cs b/Program.cs index ae29361..68c3d53 100644 --- a/Program.cs +++ b/Program.cs @@ -17,5 +17,6 @@ sealed class Program => AppBuilder.Configure() .UsePlatformDetect() .WithInterFont() - .LogToTrace(); + .LogToTrace() + .WithDeveloperTools(); } \ No newline at end of file diff --git a/Utils/AppDbContext.cs b/Utils/AppDbContext.cs new file mode 100644 index 0000000..94c19a2 --- /dev/null +++ b/Utils/AppDbContext.cs @@ -0,0 +1,47 @@ +using Microsoft.EntityFrameworkCore; +using GazpromApp.Models; + +namespace GazpromApp.Utils; + +public class AppDbContext : DbContext +{ + public DbSet Image { get; set; } + public DbSet Equipment { get; set; } + public DbSet Point { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + base.OnConfiguring(optionsBuilder); + optionsBuilder.UseSqlite("Data Source=MainDB.db"); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + // Images + modelBuilder.Entity() + .HasMany(e => e.Images) + .WithMany(e => e.Points); + + modelBuilder.Entity() + .HasOne(e => e.Image) + .WithMany(e => e.Equipment) + .HasForeignKey(e => e.ImageId); + + // Properties + modelBuilder.Entity() + .HasMany(e => e.Properties) + .WithMany(e => e.Points); + + modelBuilder.Entity() + .HasMany(e => e.Properties) + .WithMany(e => e.Equipment); + + // Equipment + modelBuilder.Entity() + .HasOne(e => e.Point) + .WithMany(e => e.Equipment) + .HasForeignKey(e => e.PointId); + } +} \ No newline at end of file diff --git a/ViewModels/Basic/ImageViewModel.cs b/ViewModels/Basic/ImageViewModel.cs new file mode 100644 index 0000000..3b38795 --- /dev/null +++ b/ViewModels/Basic/ImageViewModel.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices.JavaScript; +using Avalonia.Media.Imaging; +using CommunityToolkit.Mvvm.ComponentModel; +using GazpromApp.Models; + +namespace GazpromApp.ViewModels; + +public partial class ImageViewModel : ViewModelBase +{ + [ObservableProperty] + private byte[]? _bytes; + // EF stuff + [ObservableProperty] + private List? _points; + [ObservableProperty] + private List? _equipment; + [ObservableProperty] + private Bitmap? _fileName; + + public ImageViewModel(Image image) + { + Bytes = image.Bytes; + Equipment = image.Equipment; + Points = image.Points; + } + + public Image GetImage() + { + return new Image() + { + Bytes = Bytes ?? [], + Equipment = Equipment ?? [], + Points = Points ?? [], + }; + } +} \ No newline at end of file diff --git a/ViewModels/Basic/PointViewModel.cs b/ViewModels/Basic/PointViewModel.cs new file mode 100644 index 0000000..1ca233b --- /dev/null +++ b/ViewModels/Basic/PointViewModel.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using CommunityToolkit.Mvvm.ComponentModel; +using GazpromApp.Models; + +namespace GazpromApp.ViewModels; + +public partial class PointViewModel : ViewModelBase +{ + [ObservableProperty] + private string? _name; + + [ObservableProperty] + private string? _description; + + [ObservableProperty] + private double? _latitude; + + [ObservableProperty] + private double? _longitude; + // EF stuff + [ObservableProperty] + private List? _images; + [ObservableProperty] + private List? _equipment; + [ObservableProperty] + private List? _properties; + + public PointViewModel(Models.Point point) + { + Name = point.Name; + Description = point.Description; + Latitude = point.Latitude; + Longitude = point.Longitude; + Images = point.Images; + Equipment = point.Equipment; + Properties = point.Properties; + } + + public Models.Point GetPoint() + { + return new Models.Point() + { + Name = this.Name, + Description = this.Description + }; + } +} \ No newline at end of file diff --git a/ViewModels/EditPointDialogViewModel.cs b/ViewModels/EditPointDialogViewModel.cs new file mode 100644 index 0000000..5015529 --- /dev/null +++ b/ViewModels/EditPointDialogViewModel.cs @@ -0,0 +1,6 @@ +namespace GazpromApp.ViewModels; + +public partial class EditPointDialogViewModel : ViewModelBase +{ + +} \ No newline at end of file diff --git a/ViewModels/MainWindowViewModel.cs b/ViewModels/MainWindowViewModel.cs index 5a0ef18..6285428 100644 --- a/ViewModels/MainWindowViewModel.cs +++ b/ViewModels/MainWindowViewModel.cs @@ -1,6 +1,40 @@ -namespace GazpromApp.ViewModels; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace GazpromApp.ViewModels; +using GazpromApp.Messages; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Threading.Tasks; public partial class MainWindowViewModel : ViewModelBase { - public string Greeting { get; } = "Welcome to Avalonia!"; + public ObservableCollection Points { get; } = new ObservableCollection(); + + [RelayCommand] + private async Task AddPoint() + { + var point = await WeakReferenceMessenger.Default.Send(new NewPointMessage()); + if (point != null) + Points.Add(point); + IsLineVisible = true; + } + + [RelayCommand] + private void RemovePoint(PointViewModel point) + { + Points.Remove(point); + if (Points.Count == 0) + IsLineVisible = false; + } + + [RelayCommand] + private void EditPoint(PointViewModel point) + { + Debug.WriteLine(point.Name); + } + + [ObservableProperty] + private bool _isLineVisible; } \ No newline at end of file diff --git a/ViewModels/NewPointDialogViewModel.cs b/ViewModels/NewPointDialogViewModel.cs new file mode 100644 index 0000000..97de157 --- /dev/null +++ b/ViewModels/NewPointDialogViewModel.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using Avalonia; +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.Messages; +using Image = GazpromApp.Models.Image; +using Point = GazpromApp.Models.Point; + +namespace GazpromApp.ViewModels; + +public partial class NewPointDialogViewModel : ViewModelBase +{ + [ObservableProperty] + private string? _name; + + [ObservableProperty] + private string? _description; + + public ObservableCollection Images { get; } = new ObservableCollection(); + + [RelayCommand] + public void DeleteImage(ImageViewModel image) + { + Images.Remove(image); + if (Images.Count == 0) + IsImagesEmpty = true; + } + + [RelayCommand] + private async Task AddImage(Window w) + { + IReadOnlyList files = await w.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions() + { + AllowMultiple = true, + Title = "Выберите одну или несколько картинок", + FileTypeFilter = [FilePickerFileTypes.ImageAll], + }); + foreach (var file in files) + Images.Add(new ImageViewModel(new Image()) { FileName = new Bitmap(file.Path.AbsolutePath) }); + IsImagesEmpty = false; + } + + [ObservableProperty] + private bool _isImagesEmpty = true; + + private bool CanExecuteEnterPoint() => !string.IsNullOrEmpty(Name) || !string.IsNullOrEmpty(Description); + + [RelayCommand(CanExecute = nameof(CanExecuteEnterPoint))] + public void EnterPoint() + { + Point point = new Point() + { + Name = Name, + Description = Description, + Images = Images.Select(e => e.GetImage()).ToList() + }; + WeakReferenceMessenger.Default.Send(new NewPointDone(new PointViewModel(point))); + } +} \ No newline at end of file diff --git a/Views/EditPointDialog.axaml b/Views/EditPointDialog.axaml new file mode 100644 index 0000000..6a66c72 --- /dev/null +++ b/Views/EditPointDialog.axaml @@ -0,0 +1,9 @@ + + Welcome to Avalonia! + diff --git a/Views/EditPointDialog.axaml.cs b/Views/EditPointDialog.axaml.cs new file mode 100644 index 0000000..065da9e --- /dev/null +++ b/Views/EditPointDialog.axaml.cs @@ -0,0 +1,18 @@ +using Avalonia.Controls; +using CommunityToolkit.Mvvm.Messaging; +using GazpromApp.Messages; + +namespace GazpromApp.Views; + +public partial class EditPointDialog : Window +{ + public EditPointDialog() + { + InitializeComponent(); + + WeakReferenceMessenger.Default.Register(this, static (w, m) => + { + w.Close(m.Point); + }); + } +} \ No newline at end of file diff --git a/Views/MainWindow.axaml b/Views/MainWindow.axaml index 170d666..b5a9aac 100644 --- a/Views/MainWindow.axaml +++ b/Views/MainWindow.axaml @@ -1,20 +1,87 @@ + Title="GazpromApp" + WindowStartupLocation="CenterScreen"> - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Views/MainWindow.axaml.cs b/Views/MainWindow.axaml.cs index b5fc08a..76bc523 100644 --- a/Views/MainWindow.axaml.cs +++ b/Views/MainWindow.axaml.cs @@ -1,4 +1,7 @@ using Avalonia.Controls; +using GazpromApp.Messages; +using GazpromApp.ViewModels; +using CommunityToolkit.Mvvm.Messaging; namespace GazpromApp.Views; @@ -7,5 +10,25 @@ public partial class MainWindow : Window public MainWindow() { InitializeComponent(); + + if (Design.IsDesignMode) + return; + + WeakReferenceMessenger.Default.Register(this, static (w, m) => + { + NewPointDialog dialog = new NewPointDialog() + { + DataContext = new NewPointDialogViewModel() + }; + m.Reply(dialog.ShowDialog(w)); + }); + WeakReferenceMessenger.Default.Register(this, static (w, m) => + { + EditPointDialog dialog = new EditPointDialog() + { + DataContext = new EditPointDialogViewModel() + }; + m.Reply(dialog.ShowDialog(w)); + }); } } \ No newline at end of file diff --git a/Views/NewPointDialog.axaml b/Views/NewPointDialog.axaml new file mode 100644 index 0000000..abb18e8 --- /dev/null +++ b/Views/NewPointDialog.axaml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +