diff --git a/TimetableDesigner.Core/BaseGroup.cs b/TimetableDesigner.Core/BaseGroup.cs new file mode 100644 index 0000000..7771057 --- /dev/null +++ b/TimetableDesigner.Core/BaseGroup.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TimetableDesigner.Core +{ + public abstract class BaseGroup : IUnit + { + #region FIELDS + + private string _name; + private string _shortName; + + #endregion + + + + #region PROPERTIES + + public string Name + { + get => _name; + set => _name = value; + } + public string ShortName + { + get => _shortName; + set => _shortName = value; + } + + #endregion + + + + #region CONSTRUCTORS + + public BaseGroup() + { + _name = string.Empty; + _shortName = string.Empty; + } + + #endregion + } +} diff --git a/TimetableDesigner.Core/Class.cs b/TimetableDesigner.Core/Class.cs index c401feb..44b325e 100644 --- a/TimetableDesigner.Core/Class.cs +++ b/TimetableDesigner.Core/Class.cs @@ -6,14 +6,60 @@ using System.Threading.Tasks; namespace TimetableDesigner.Core { + [Serializable] public class Class { + #region FIELDS + + private string _name; + private Teacher? _teacher; + private BaseGroup? _group; + private Classroom? _classroom; + private TimetableDay? _day; + private TimetableSpan? _slot; + private byte[] _color; + + #endregion + + + #region PROPERTIES - public string Name { get; set; } - public Teacher? Teacher { get; set; } - public IGroup? Group { get; set; } - public Classroom? Classroom { get; set; } + public string Name + { + get => _name; + set => _name = value; + } + public Teacher? Teacher + { + get => _teacher; + set => _teacher = value; + } + public BaseGroup? Group + { + get => _group; + set => _group = value; + } + public Classroom? Classroom + { + get => _classroom; + set => _classroom = value; + } + public TimetableDay? Day + { + get => _day; + set => _day = value; + } + public TimetableSpan? Slot + { + get => _slot; + set => _slot = value; + } + public byte[] Color + { + get => _color; + set => _color = value; + } #endregion @@ -23,10 +69,13 @@ namespace TimetableDesigner.Core public Class() { - Name = string.Empty; - Teacher = null; - Group = null; - Classroom = null; + _name = string.Empty; + _teacher = null; + _group = null; + _classroom = null; + _day = null; + _slot = null; + _color = new byte[3] { 0xFA, 0x5A, 0x5A }; } #endregion diff --git a/TimetableDesigner.Core/Classroom.cs b/TimetableDesigner.Core/Classroom.cs index a3d7573..d3950b2 100644 --- a/TimetableDesigner.Core/Classroom.cs +++ b/TimetableDesigner.Core/Classroom.cs @@ -7,14 +7,48 @@ using System.Threading.Tasks; namespace TimetableDesigner.Core { - public class Classroom + [Serializable] + public class Classroom : IUnit { + #region FIELDS + + private string _name; + private string _shortName; + private string _description; + private bool _isCapacityLimited; + private uint _capacity; + + #endregion + + + #region PROPERTIES - public string Name { get; set; } - public string Description { get; set; } - public bool IsCapacityLimited { get; set; } - public uint Capacity { get; set; } + public string Name + { + get => _name; + set => _name = value; + } + public string ShortName + { + get => _shortName; + set => _shortName = value; + } + public string Description + { + get => _description; + set => _description = value; + } + public bool IsCapacityLimited + { + get => _isCapacityLimited; + set => _isCapacityLimited = value; + } + public uint Capacity + { + get => _capacity; + set => _capacity = value; + } #endregion @@ -24,10 +58,11 @@ namespace TimetableDesigner.Core public Classroom() { - Name = string.Empty; - Description = string.Empty; - IsCapacityLimited = false; - Capacity = 1; + _name = string.Empty; + _shortName = string.Empty; + _description = string.Empty; + _isCapacityLimited = false; + _capacity = 1; } #endregion diff --git a/TimetableDesigner.Core/Group.cs b/TimetableDesigner.Core/Group.cs index e4c9e55..8a1d4bd 100644 --- a/TimetableDesigner.Core/Group.cs +++ b/TimetableDesigner.Core/Group.cs @@ -6,13 +6,26 @@ using System.Threading.Tasks; namespace TimetableDesigner.Core { - public class Group : IGroup + [Serializable] + public class Group : BaseGroup { + #region FIELDS + + private string _description; + private HashSet _assignedSubgroups; + + #endregion + + + #region PROPERTIES - public string Name { get; set; } - public string Description { get; set; } - public ICollection AssignedSubgroups { get; set; } + public string Description + { + get => _description; + set => _description = value; + } + public ICollection AssignedSubgroups => _assignedSubgroups; #endregion @@ -20,11 +33,10 @@ namespace TimetableDesigner.Core #region CONSTRUCTORS - public Group() + public Group() : base() { - Name = string.Empty; - Description = string.Empty; - AssignedSubgroups = new HashSet(); + _description = string.Empty; + _assignedSubgroups = new HashSet(); } #endregion diff --git a/TimetableDesigner.Core/IGroup.cs b/TimetableDesigner.Core/IUnit.cs similarity index 89% rename from TimetableDesigner.Core/IGroup.cs rename to TimetableDesigner.Core/IUnit.cs index d4d6b83..fa02155 100644 --- a/TimetableDesigner.Core/IGroup.cs +++ b/TimetableDesigner.Core/IUnit.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace TimetableDesigner.Core { - public interface IGroup + public interface IUnit { #region PROPERTIES diff --git a/TimetableDesigner.Core/Project.cs b/TimetableDesigner.Core/Project.cs index 85d9cb2..1419e0f 100644 --- a/TimetableDesigner.Core/Project.cs +++ b/TimetableDesigner.Core/Project.cs @@ -9,17 +9,47 @@ namespace TimetableDesigner.Core [Serializable] public class Project { + #region FIELDS + + private Guid _guid; + private string _name; + private string _author; + private string _description; + private TimetableTemplate _timetableTemplate; + private HashSet _classrooms; + private HashSet _teachers; + private HashSet _groups; + private HashSet _subgroups; + private HashSet _classes; + + #endregion + + + #region PROPERTIES - public string Name { get; set; } - public string Author { get; set; } - public string Description { get; set; } - public TimetableTemplate TimetableTemplate { get; set; } - public ICollection Classrooms { get; set; } - public ICollection Teachers { get; set; } - public ICollection Groups { get; set; } - public ICollection Subgroups { get; set; } - public ICollection Classes { get; set; } + public Guid Guid => _guid; + public string Name + { + get => _name; + set => _name = value; + } + public string Author + { + get => _author; + set => _author = value; + } + public string Description + { + get => _description; + set => _description = value; + } + public TimetableTemplate TimetableTemplate => _timetableTemplate; + public ICollection Classrooms => _classrooms; + public ICollection Teachers => _teachers; + public ICollection Groups => _groups; + public ICollection Subgroups => _subgroups; + public ICollection Classes => _classes; #endregion @@ -29,14 +59,16 @@ namespace TimetableDesigner.Core public Project() { - Name = string.Empty; - Author = string.Empty; - Description = string.Empty; - TimetableTemplate = new TimetableTemplate(); - Classrooms = new HashSet(); - Teachers = new HashSet(); - Groups = new HashSet(); - Subgroups = new HashSet(); + _guid = Guid.NewGuid(); + _name = string.Empty; + _author = string.Empty; + _description = string.Empty; + _timetableTemplate = new TimetableTemplate(); + _classrooms = new HashSet(); + _teachers = new HashSet(); + _groups = new HashSet(); + _subgroups = new HashSet(); + _classes = new HashSet(); } #endregion diff --git a/TimetableDesigner.Core/Subgroup.cs b/TimetableDesigner.Core/Subgroup.cs index 2079fed..22c0c40 100644 --- a/TimetableDesigner.Core/Subgroup.cs +++ b/TimetableDesigner.Core/Subgroup.cs @@ -6,22 +6,13 @@ using System.Threading.Tasks; namespace TimetableDesigner.Core { - public class Subgroup : IGroup + [Serializable] + public class Subgroup : BaseGroup { - #region PROPERTIES - - public string Name { get; set; } - - #endregion - - - #region CONSTRUCTORS - public Subgroup() - { - Name = string.Empty; - } + public Subgroup() : base() + { } #endregion } diff --git a/TimetableDesigner.Core/Teacher.cs b/TimetableDesigner.Core/Teacher.cs index 479f677..a201c6b 100644 --- a/TimetableDesigner.Core/Teacher.cs +++ b/TimetableDesigner.Core/Teacher.cs @@ -3,16 +3,42 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using TimetableDesigner.Customs; namespace TimetableDesigner.Core { - public class Teacher + [Serializable] + public class Teacher : IUnit { + #region FIELDS + + private string _name; + private string _shortName; + private string _description; + private JsonSerializableDictionary _availabilityHours; + + #endregion + + + #region PROPERTIES - public string Name { get; set; } - public string Description { get; set; } - public IDictionary AvailabilityHours { get; set; } + public string Name + { + get => _name; + set => _name = value; + } + public string ShortName + { + get => _shortName; + set => _shortName = value; + } + public string Description + { + get => _description; + set => _description = value; + } + public IDictionary AvailabilityHours => _availabilityHours; #endregion @@ -22,9 +48,10 @@ namespace TimetableDesigner.Core public Teacher() { - Name = string.Empty; - Description = string.Empty; - AvailabilityHours = new Dictionary(); + _name = string.Empty; + _shortName = string.Empty; + _description = string.Empty; + _availabilityHours = new JsonSerializableDictionary(); } #endregion diff --git a/TimetableDesigner.Core/TimetableDay.cs b/TimetableDesigner.Core/TimetableDay.cs index ff7bfb8..bfc4aaa 100644 --- a/TimetableDesigner.Core/TimetableDay.cs +++ b/TimetableDesigner.Core/TimetableDay.cs @@ -9,9 +9,21 @@ namespace TimetableDesigner.Core [Serializable] public class TimetableDay { + #region FIELDS + + private string _name; + + #endregion + + + #region PROPERTIES - public string Name { get; set; } + public string Name + { + get => _name; + set => _name = value; + } #endregion @@ -20,8 +32,8 @@ namespace TimetableDesigner.Core #region CONSTRUCTORS public TimetableDay(string name) - { - Name = name; + { + _name = name; } #endregion diff --git a/TimetableDesigner.Core/TimetableDesigner.Core.csproj b/TimetableDesigner.Core/TimetableDesigner.Core.csproj index cb6a55a..5417903 100644 --- a/TimetableDesigner.Core/TimetableDesigner.Core.csproj +++ b/TimetableDesigner.Core/TimetableDesigner.Core.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/TimetableDesigner.Core/TimetableSpan.cs b/TimetableDesigner.Core/TimetableSpan.cs index 273c930..41d6ec1 100644 --- a/TimetableDesigner.Core/TimetableSpan.cs +++ b/TimetableDesigner.Core/TimetableSpan.cs @@ -1,14 +1,24 @@ using System; +using System.Diagnostics; namespace TimetableDesigner.Core { [Serializable] public class TimetableSpan { + #region FIELDS + + private TimeOnly _from; + private TimeOnly _to; + + #endregion + + + #region PROPERTIES - public TimeOnly From { get; private set; } - public TimeOnly To { get; private set; } + public TimeOnly From => _from; + public TimeOnly To => _to; #endregion @@ -20,11 +30,11 @@ namespace TimetableDesigner.Core { if (to <= from) { - throw new ArgumentException("\"to\" cannot be less or equal to \"from\""); + throw new ArgumentException("Ending value (\"to\") of TimetableSpan have to be greater than starting value (\"from\")"); } - From = from; - To = to; + _from = from; + _to = to; } #endregion @@ -33,27 +43,33 @@ namespace TimetableDesigner.Core #region PUBLIC METHODS - internal TimetableSpanCollision CheckCollision(TimetableSpan slot) + public override bool Equals(object? obj) => obj is TimetableSpan slot && From == slot.From && To == slot.To; + + public override int GetHashCode() => HashCode.Combine(From, To); + + public override string? ToString() => $"{From} - {To}"; + + public TimetableSpanCollision CheckCollision(TimetableSpan slot) { if (slot.To <= this.From) { return TimetableSpanCollision.CheckedSlotBefore; } - else if (this.To <= slot.From) + else if (this.To <= slot.From) { return TimetableSpanCollision.CheckedSlotAfter; } else { - if (this.From < slot.From && slot.To < this.To) + if (this.From <= slot.From && slot.To <= this.To) { return TimetableSpanCollision.CheckedSlotIn; } - else if (this.From < slot.From && slot.From < this.To && this.To < slot.To) + else if (this.From < slot.From && slot.From < this.To && this.To <= slot.To) { return TimetableSpanCollision.CheckedSlotFromIn; } - else if (slot.From < this.From && this.From < slot.To && slot.To < this.To) + else if (slot.From < this.From && this.From < slot.To && slot.To <= this.To) { return TimetableSpanCollision.CheckedSlotToIn; } @@ -64,12 +80,6 @@ namespace TimetableDesigner.Core } } - public override bool Equals(object? obj) => obj is TimetableSpan slot && From == slot.From && To == slot.To; - - public override int GetHashCode() => HashCode.Combine(From, To); - - public override string? ToString() => $"{From}-{To}"; - #endregion } } diff --git a/TimetableDesigner.Core/TimetableSpanCollection.cs b/TimetableDesigner.Core/TimetableSpanCollection.cs index c1df85e..21de77f 100644 --- a/TimetableDesigner.Core/TimetableSpanCollection.cs +++ b/TimetableDesigner.Core/TimetableSpanCollection.cs @@ -7,11 +7,12 @@ using System.Threading.Tasks; namespace TimetableDesigner.Core { + [Serializable] public class TimetableSpanCollection : ICollection { #region FIELDS - private IList _list; + private List _list; #endregion @@ -20,7 +21,7 @@ namespace TimetableDesigner.Core #region PROPERTIES public int Count => _list.Count; - public bool IsReadOnly => _list.IsReadOnly; + public bool IsReadOnly => ((ICollection)_list).IsReadOnly; #endregion diff --git a/TimetableDesigner.Core/TimetableSpanCollision.cs b/TimetableDesigner.Core/TimetableSpanCollision.cs index 6b5229f..49689dc 100644 --- a/TimetableDesigner.Core/TimetableSpanCollision.cs +++ b/TimetableDesigner.Core/TimetableSpanCollision.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace TimetableDesigner.Core { - internal enum TimetableSpanCollision + public enum TimetableSpanCollision { CheckedSlotBefore, CheckedSlotAfter, diff --git a/TimetableDesigner.Core/TimetableTemplate.cs b/TimetableDesigner.Core/TimetableTemplate.cs index 98c2626..6fb5fde 100644 --- a/TimetableDesigner.Core/TimetableTemplate.cs +++ b/TimetableDesigner.Core/TimetableTemplate.cs @@ -20,8 +20,8 @@ namespace TimetableDesigner.Core #region PROPERTIES - public IEnumerable Days => _days; - public IEnumerable Slots => _slots; + public ICollection Days => _days; + public ICollection Slots => _slots; #endregion @@ -36,19 +36,5 @@ namespace TimetableDesigner.Core } #endregion - - - - #region PUBLIC METHODS - - public void AddDay(TimetableDay name) => _days.Add(name); - - public bool RemoveDay(TimetableDay day) => _days.Remove(day); - - public void AddSlot(TimetableSpan slot) => _slots.Add(slot); - - public bool RemoveSlot(TimetableSpan slot) => _slots.Remove(slot); - - #endregion } } diff --git a/TimetableDesigner.Customs/JsonSerializableDictionary.cs b/TimetableDesigner.Customs/JsonSerializableDictionary.cs new file mode 100644 index 0000000..d6530d6 --- /dev/null +++ b/TimetableDesigner.Customs/JsonSerializableDictionary.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TimetableDesigner.Customs +{ + [JsonArray] + public class JsonSerializableDictionary : Dictionary where TKey : notnull + { + } +} diff --git a/TimetableDesigner.Customs/TimetableDesigner.Customs.csproj b/TimetableDesigner.Customs/TimetableDesigner.Customs.csproj index cfadb03..51eb283 100644 --- a/TimetableDesigner.Customs/TimetableDesigner.Customs.csproj +++ b/TimetableDesigner.Customs/TimetableDesigner.Customs.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/TimetableDesigner.Scheduler/Scheduler.cs b/TimetableDesigner.Scheduler/Scheduler.cs new file mode 100644 index 0000000..b632a26 --- /dev/null +++ b/TimetableDesigner.Scheduler/Scheduler.cs @@ -0,0 +1,7 @@ +namespace TimetableDesigner.Scheduler +{ + public class Class1 + { + + } +} \ No newline at end of file diff --git a/TimetableDesigner.Scheduler/TimetableDesigner.Scheduler.csproj b/TimetableDesigner.Scheduler/TimetableDesigner.Scheduler.csproj new file mode 100644 index 0000000..cfadb03 --- /dev/null +++ b/TimetableDesigner.Scheduler/TimetableDesigner.Scheduler.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/TimetableDesigner.Tests/TimetableTest.cs b/TimetableDesigner.Tests/TimetableTest.cs index b691ee1..2d0c8c2 100644 --- a/TimetableDesigner.Tests/TimetableTest.cs +++ b/TimetableDesigner.Tests/TimetableTest.cs @@ -42,12 +42,9 @@ namespace TimetableDesigner.Tests { TimetableTemplate model = new TimetableTemplate(); - TimetableDay day = new TimetableDay - { - Name = "Monday" - }; + TimetableDay day = new TimetableDay("Monday"); - model.AddDay(day); + model.Days.Add(day); Assert.AreEqual(1, model.Days.Count()); Assert.AreEqual(day, model.Days.ToList()[0]); @@ -60,7 +57,7 @@ namespace TimetableDesigner.Tests TimetableSpan slot = new TimetableSpan(new TimeOnly(8, 0), new TimeOnly(9, 0)); - model.AddSlot(slot); + model.Slots.Add(slot); Assert.AreEqual(1, model.Slots.Count()); Assert.AreEqual(new TimetableSpan(new TimeOnly(8, 0), new TimeOnly(9, 0)), model.Slots.ToList()[0]); @@ -74,8 +71,8 @@ namespace TimetableDesigner.Tests TimetableSpan slot1 = new TimetableSpan(new TimeOnly(8, 15), new TimeOnly(9, 0)); TimetableSpan slot2 = new TimetableSpan(new TimeOnly(9, 15), new TimeOnly(10, 0)); - model.AddSlot(slot1); - model.AddSlot(slot2); + model.Slots.Add(slot1); + model.Slots.Add(slot2); Assert.AreEqual(2, model.Slots.Count()); Assert.AreEqual(new TimetableSpan(new TimeOnly(8, 15), new TimeOnly(9, 0)), model.Slots.ToList()[0]); @@ -90,8 +87,8 @@ namespace TimetableDesigner.Tests TimetableSpan slot1 = new TimetableSpan(new TimeOnly(8, 0), new TimeOnly(9, 0)); TimetableSpan slot2 = new TimetableSpan(new TimeOnly(9, 0), new TimeOnly(10, 0)); - model.AddSlot(slot1); - model.AddSlot(slot2); + model.Slots.Add(slot1); + model.Slots.Add(slot2); Assert.AreEqual(2, model.Slots.Count()); Assert.AreEqual(new TimetableSpan(new TimeOnly(8, 0), new TimeOnly(9, 0)), model.Slots.ToList()[0]); @@ -107,8 +104,8 @@ namespace TimetableDesigner.Tests TimetableSpan slot1 = new TimetableSpan(new TimeOnly(8, 0), new TimeOnly(9, 30)); TimetableSpan slot2 = new TimetableSpan(new TimeOnly(8, 30), new TimeOnly(10, 0)); - model.AddSlot(slot1); - model.AddSlot(slot2); + model.Slots.Add(slot1); + model.Slots.Add(slot2); Assert.Fail(); } @@ -123,9 +120,9 @@ namespace TimetableDesigner.Tests TimetableSpan slot2 = new TimetableSpan(new TimeOnly(10, 0), new TimeOnly(11, 0)); TimetableSpan slot3 = new TimetableSpan(new TimeOnly(8, 59), new TimeOnly(10, 1)); - model.AddSlot(slot1); - model.AddSlot(slot2); - model.AddSlot(slot3); + model.Slots.Add(slot1); + model.Slots.Add(slot2); + model.Slots.Add(slot3); Assert.Fail(); } @@ -143,13 +140,13 @@ namespace TimetableDesigner.Tests TimetableSpan slot6 = new TimetableSpan(new TimeOnly(9, 0), new TimeOnly(10, 0)); TimetableSpan slot7 = new TimetableSpan(new TimeOnly(11, 0), new TimeOnly(12, 0)); - model.AddSlot(slot1); - model.AddSlot(slot2); - model.AddSlot(slot3); - model.AddSlot(slot4); - model.AddSlot(slot5); - model.AddSlot(slot6); - model.AddSlot(slot7); + model.Slots.Add(slot1); + model.Slots.Add(slot2); + model.Slots.Add(slot3); + model.Slots.Add(slot4); + model.Slots.Add(slot5); + model.Slots.Add(slot6); + model.Slots.Add(slot7); List slots = model.Slots.ToList(); diff --git a/TimetableDesigner/App.config b/TimetableDesigner/App.config new file mode 100644 index 0000000..59a896c --- /dev/null +++ b/TimetableDesigner/App.config @@ -0,0 +1,15 @@ + + + + +
+ + + + + + 10 + + + + \ No newline at end of file diff --git a/TimetableDesigner/App.xaml b/TimetableDesigner/App.xaml index 15164af..f60bc5c 100644 --- a/TimetableDesigner/App.xaml +++ b/TimetableDesigner/App.xaml @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" ShutdownMode="OnMainWindowClose" - StartupUri="Views/MainWindow.xaml"> + Startup="OnStartup"> diff --git a/TimetableDesigner/App.xaml.cs b/TimetableDesigner/App.xaml.cs index 3fb8fd4..391ccd0 100644 --- a/TimetableDesigner/App.xaml.cs +++ b/TimetableDesigner/App.xaml.cs @@ -1,25 +1,88 @@ -using System; +global using TimetableDesigner.Services; +using Microsoft.ML.Data; +using System; using System.Collections.Generic; using System.ComponentModel.Design; using System.Configuration; using System.Data; +using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows; -using TimetableDesigner.Services; +using TimetableDesigner.Services.FileDialog; using TimetableDesigner.Services.MessageBox; using TimetableDesigner.Services.Project; +using TimetableDesigner.Services.Scheduler; using TimetableDesigner.Services.TabNavigation; +using TimetableDesigner.ViewModels.Views; +using TimetableDesigner.Views; namespace TimetableDesigner { public partial class App : Application { + #region FIELDS + + private IMessageBoxService _messageBoxService; + private IFileDialogService _fileDialogService; + private ITabNavigationService _tabNavigationService; + private IProjectService _projectService; + private ISchedulerService _schedulerService; + + #endregion + + + + #region CONSTRUCTORS + public App() { - ServiceProvider.Instance.AddService(new MessageBoxService()); - ServiceProvider.Instance.AddService(new TabNavigationService()); - ServiceProvider.Instance.AddService(new ProjectService()); + _messageBoxService = new MessageBoxService(); + _fileDialogService = new FileDialogService(); + _tabNavigationService = new TabNavigationService(); + _projectService = new ProjectService(); + _schedulerService = new SchedulerService(_projectService); + + ServiceProvider.Instance.AddService(_messageBoxService); + ServiceProvider.Instance.AddService(_fileDialogService); + ServiceProvider.Instance.AddService(_tabNavigationService); + ServiceProvider.Instance.AddService(_projectService); + ServiceProvider.Instance.AddService(_schedulerService); } + + #endregion + + + + #region PRIVATE METHODS + + private void OnStartup(object sender, StartupEventArgs e) + { + if (!Directory.Exists(Globals.Path.ApplicationData)) + { + Directory.CreateDirectory(Globals.Path.ApplicationData); + } + + _projectService.LoadRecentProjectsList(); + + MainWindow mainWindow = new MainWindow(); + mainWindow.Show(); + + if (e.Args.Length > 0 && File.Exists(e.Args[0])) + { + _projectService.Load(e.Args[0]); + } + else + { + TabItem welcomeTab = new TabItem() + { + Title = TimetableDesigner.Properties.Resources.Tabs_Welcome, + ViewModel = new WelcomeViewVM() + }; + _tabNavigationService.AddAndActivate(welcomeTab); + } + } + + #endregion } } diff --git a/TimetableDesigner/Commands/RelayCommand.cs b/TimetableDesigner/Commands/RelayCommand.cs index 4873f1b..050b2b7 100644 --- a/TimetableDesigner/Commands/RelayCommand.cs +++ b/TimetableDesigner/Commands/RelayCommand.cs @@ -39,10 +39,7 @@ namespace TimetableDesigner.Commands public bool CanExecute(object parameter) => _canExecute == null || _canExecute((T)parameter); - public void Execute(object parameter) - { - _execute((T)parameter); - } + public void Execute(object parameter) => _execute((T)parameter); #endregion diff --git a/TimetableDesigner/Controls/ClassControl.xaml b/TimetableDesigner/Controls/ClassControl.xaml new file mode 100644 index 0000000..131fd12 --- /dev/null +++ b/TimetableDesigner/Controls/ClassControl.xaml @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TimetableDesigner/Controls/ClassControl.xaml.cs b/TimetableDesigner/Controls/ClassControl.xaml.cs new file mode 100644 index 0000000..e87bc3e --- /dev/null +++ b/TimetableDesigner/Controls/ClassControl.xaml.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using TimetableDesigner.Core; +using TimetableDesigner.Services; +using TimetableDesigner.Services.Project; +using TimetableDesigner.Services.TabNavigation; +using TimetableDesigner.ViewModels.Models; +using TimetableDesigner.ViewModels.Models.Base; +using TimetableDesigner.ViewModels.Views; + +namespace TimetableDesigner.Controls +{ + public partial class ClassControl : UserControl + { + #region FIELDS + + private IProjectService _projectService; + private ITabNavigationService _tabNavigationService; + + #endregion + + + + #region PROPERTIES + + private static DependencyProperty RemoveButtonCommandProperty = DependencyProperty.Register( + "RemoveButtonCommand", + typeof(ICommand), + typeof(ClassControl), + new PropertyMetadata(null) + ); + public ICommand RemoveButtonCommand + { + get => (ICommand)GetValue(RemoveButtonCommandProperty); + set => SetValue(RemoveButtonCommandProperty, value); + } + + private static DependencyProperty RemoveButtonCommandParameterProperty = DependencyProperty.Register( + "RemoveButtonCommandParameter", + typeof(object), + typeof(ClassControl), + new PropertyMetadata(null) + ); + public object RemoveButtonCommandParameter + { + get => GetValue(RemoveButtonCommandParameterProperty); + set => SetValue(RemoveButtonCommandParameterProperty, value); + } + + private static DependencyProperty CloneButtonCommandProperty = DependencyProperty.Register( + "CloneButtonCommand", + typeof(ICommand), + typeof(ClassControl), + new PropertyMetadata(null) + ); + public ICommand CloneButtonCommand + { + get => (ICommand)GetValue(CloneButtonCommandProperty); + set => SetValue(CloneButtonCommandProperty, value); + } + + private static DependencyProperty CloneButtonCommandParameterProperty = DependencyProperty.Register( + "CloneButtonCommandParameter", + typeof(object), + typeof(ClassControl), + new PropertyMetadata(null) + ); + public object CloneButtonCommandParameter + { + get => GetValue(CloneButtonCommandParameterProperty); + set => SetValue(CloneButtonCommandParameterProperty, value); + } + + + public IProjectService ProjectService => _projectService; + + #endregion + + + + #region CONSTRUCTORS + + public ClassControl() + { + _projectService = ServiceProvider.Instance.GetService(); + _tabNavigationService = ServiceProvider.Instance.GetService(); + + InitializeComponent(); + } + + #endregion + + + + #region PRIVATE METHODS + + private void RemoveButton_Click(object sender, RoutedEventArgs e) => RemoveButtonCommand?.Execute(RemoveButtonCommandParameter); + + private void CloneButton_Click(object sender, RoutedEventArgs e) => CloneButtonCommand?.Execute(CloneButtonCommandParameter); + + #endregion + + } +} diff --git a/TimetableDesigner/Controls/DynamicGrid.xaml b/TimetableDesigner/Controls/DynamicGrid.xaml new file mode 100644 index 0000000..196cb83 --- /dev/null +++ b/TimetableDesigner/Controls/DynamicGrid.xaml @@ -0,0 +1,10 @@ + + + diff --git a/TimetableDesigner/Controls/DynamicGrid.xaml.cs b/TimetableDesigner/Controls/DynamicGrid.xaml.cs new file mode 100644 index 0000000..4ab9d81 --- /dev/null +++ b/TimetableDesigner/Controls/DynamicGrid.xaml.cs @@ -0,0 +1,602 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Threading; +using static TimetableDesigner.ViewModels.Views.TimetableEditorViewVM; + +namespace TimetableDesigner.Controls +{ + public partial class DynamicGrid : UserControl + { + #region FIELDS + + private Border[,]? _contentCells; + private Border[]? _columnHeadersCells; + private Border[]? _rowHeadersCells; + private List _dispatcherOperations; + + #endregion + + + + #region PROPERTIES + + private static DependencyProperty ColumnsProperty = DependencyProperty.Register( + "Columns", + typeof(int), + typeof(DynamicGrid), + new PropertyMetadata(0, new PropertyChangedCallback(OnLayoutChanged)) + ); + public int Columns + { + get => (int)GetValue(ColumnsProperty); + set => SetValue(ColumnsProperty, value); + } + + private static DependencyProperty RowsProperty = DependencyProperty.Register( + "Rows", + typeof(int), + typeof(DynamicGrid), + new PropertyMetadata(0, new PropertyChangedCallback(OnLayoutChanged)) + ); + public int Rows + { + get => (int)GetValue(RowsProperty); + set => SetValue(RowsProperty, value); + } + + + private static DependencyProperty ColumnHeadersSourceProperty = DependencyProperty.Register( + "ColumnHeadersSource", + typeof(IEnumerable), + typeof(DynamicGrid), + new PropertyMetadata(new PropertyChangedCallback(OnColumnHeadersChanged)) + ); + public IEnumerable ColumnHeadersSource + { + get => (IEnumerable)GetValue(ColumnHeadersSourceProperty); + set => SetValue(ColumnHeadersSourceProperty, value); + } + + + private static DependencyProperty ColumnHeadersTemplateProperty = DependencyProperty.Register( + "ColumnHeadersTemplate", + typeof(DataTemplate), + typeof(DynamicGrid), + new PropertyMetadata(new PropertyChangedCallback(OnColumnHeadersChanged)) + ); + public DataTemplate ColumnHeadersTemplate + { + get => (DataTemplate)GetValue(ColumnHeadersTemplateProperty); + set => SetValue(ColumnHeadersTemplateProperty, value); + } + + + private static DependencyProperty RowHeadersSourceProperty = DependencyProperty.Register( + "RowHeadersSource", + typeof(IEnumerable), + typeof(DynamicGrid), + new PropertyMetadata(new PropertyChangedCallback(OnRowHeadersChanged)) + ); + public IEnumerable RowHeadersSource + { + get => (IEnumerable)GetValue(RowHeadersSourceProperty); + set => SetValue(RowHeadersSourceProperty, value); + } + + private static DependencyProperty RowHeadersTemplateProperty = DependencyProperty.Register( + "RowHeadersTemplate", + typeof(DataTemplate), + typeof(DynamicGrid), + new PropertyMetadata(new PropertyChangedCallback(OnRowHeadersChanged)) + ); + public DataTemplate RowHeadersTemplate + { + get => (DataTemplate)GetValue(RowHeadersTemplateProperty); + set => SetValue(RowHeadersTemplateProperty, value); + } + + + private static DependencyProperty ItemsSourceProperty = DependencyProperty.Register( + "ItemsSource", + typeof(IEnumerable), + typeof(DynamicGrid), + new PropertyMetadata(new PropertyChangedCallback(OnItemsChanged)) + ); + public IEnumerable ItemsSource + { + get => (IEnumerable)GetValue(ItemsSourceProperty); + set => SetValue(ItemsSourceProperty, value); + } + + private static DependencyProperty ItemTemplateProperty = DependencyProperty.Register( + "ItemTemplate", + typeof(DataTemplate), + typeof(DynamicGrid), + new PropertyMetadata(new PropertyChangedCallback(OnItemsChanged)) + ); + public DataTemplate ItemTemplate + { + get => (DataTemplate)GetValue(ItemTemplateProperty); + set => SetValue(ItemTemplateProperty, value); + } + + private static DependencyProperty CellBorderThicknessProperty = DependencyProperty.Register( + "CellBorderThickness", + typeof(Thickness), + typeof(DynamicGrid), + new PropertyMetadata(new Thickness(0), new PropertyChangedCallback(OnCellBorderThicknessChanged)) + ); + public Thickness CellBorderThickness + { + get => (Thickness)GetValue(CellBorderThicknessProperty); + set => SetValue(CellBorderThicknessProperty, value); + } + + private static DependencyProperty CellBorderBrushProperty = DependencyProperty.Register( + "CellBorderBrush", + typeof(Brush), + typeof(DynamicGrid), + new PropertyMetadata(Brushes.Black, new PropertyChangedCallback(OnCellBorderBrushChanged)) + ); + public Brush CellBorderBrush + { + get => (Brush)GetValue(CellBorderBrushProperty); + set => SetValue(CellBorderBrushProperty, value); + } + + private static DependencyProperty HeaderCellBorderThicknessProperty = DependencyProperty.Register( + "HeaderCellBorderThickness", + typeof(Thickness?), + typeof(DynamicGrid), + new PropertyMetadata(null, new PropertyChangedCallback(OnHeaderCellBorderThicknessChanged)) + ); + public Thickness HeaderCellBorderThickness + { + get + { + Thickness? headerThickness = (Thickness)GetValue(HeaderCellBorderThicknessProperty); + if (headerThickness is null) + { + return CellBorderThickness; + } + else + { + return headerThickness.Value; + } + } + set => SetValue(HeaderCellBorderThicknessProperty, value); + } + + private static DependencyProperty HeaderCellBorderBrushProperty = DependencyProperty.Register( + "HeaderCellBorderBrush", + typeof(Brush), + typeof(DynamicGrid), + new PropertyMetadata(null, new PropertyChangedCallback(OnHeaderCellBorderBrushChanged)) + ); + public Brush? HeaderCellBorderBrush + { + get + { + Brush headerBrush = (Brush)GetValue(HeaderCellBorderBrushProperty); + if (headerBrush is null) + { + headerBrush = CellBorderBrush; + } + return headerBrush; + } + set => SetValue(HeaderCellBorderBrushProperty, value); + } + + private static DependencyProperty StrechHeaderCellsToContentCellsSizeProperty = DependencyProperty.Register( + "StrechHeaderCellsToContentCellsSize", + typeof(bool), + typeof(DynamicGrid), + new PropertyMetadata(false, new PropertyChangedCallback(OnStrechHeaderCellsToContentCellsSizeChanged)) + ); + public bool StrechHeaderCellsToContentCellsSize + { + get => (bool)GetValue(StrechHeaderCellsToContentCellsSizeProperty); + set => SetValue(StrechHeaderCellsToContentCellsSizeProperty, value); + } + + #endregion + + + + #region EVENTS + + private static DependencyProperty ItemMovedCommandProperty = DependencyProperty.Register( + "ItemMovedCommand", + typeof(ICommand), + typeof(DynamicGrid), + new PropertyMetadata(null) + ); + public ICommand ItemMovedCommand + { + get => (ICommand)GetValue(ItemMovedCommandProperty); + set => SetValue(ItemMovedCommandProperty, value); + } + + private static DependencyProperty ItemMovedCommandParameterProperty = DependencyProperty.Register( + "ItemMovedCommandParameter", + typeof(object), + typeof(DynamicGrid), + new PropertyMetadata(null) + ); + public object ItemMovedCommandParameter + { + get => (object)GetValue(ItemMovedCommandParameterProperty); + set => SetValue(ItemMovedCommandParameterProperty, value); + } + + [Browsable(true)] + [Category("Action")] + [Description("Invoked when item moved to another cell")] + public event EventHandler ItemMoved; + + #endregion + + + + #region CONSTRUCTORS + + public DynamicGrid() + { + _columnHeadersCells = null; + _rowHeadersCells = null; + _contentCells = null; + _dispatcherOperations = new List(); + + InitializeComponent(); + } + + #endregion + + + + #region PRIVATE METHODS + + private void LayoutRefresh() + { + BaseGrid.Children.Clear(); + BaseGrid.ColumnDefinitions.Clear(); + BaseGrid.RowDefinitions.Clear(); + + if (Rows <= 0 || Columns <= 0) + { + _rowHeadersCells = null; + _columnHeadersCells = null; + _contentCells = null; + return; + } + else + { + _rowHeadersCells = null; + _columnHeadersCells = null; + _contentCells = new Border[Rows, Columns]; + } + + bool columnHeaders = ColumnHeadersSource != null && ColumnHeadersSource.Cast().Any(); + short columnHeadersNum = Convert.ToInt16(columnHeaders); + + bool rowHeaders = RowHeadersSource != null && RowHeadersSource.Cast().Any(); + short rowHeadersNum = Convert.ToInt16(rowHeaders); + + if (columnHeaders) + { + RowDefinition columnHeaderRow = new RowDefinition(); + if (!StrechHeaderCellsToContentCellsSize) + { + columnHeaderRow.Height = GridLength.Auto; + } + BaseGrid.RowDefinitions.Add(columnHeaderRow); + _columnHeadersCells = new Border[Columns]; + + for (int column = 0; column < Columns; column++) + { + Border border = new Border() + { + BorderBrush = HeaderCellBorderBrush, + BorderThickness = HeaderCellBorderThickness, + }; + Grid.SetColumn(border, column + rowHeadersNum); + Grid.SetRow(border, 0); + BaseGrid.Children.Add(border); + _columnHeadersCells[column] = border; + } + ColumnHeadersRefresh(); + } + + if (rowHeaders) + { + ColumnDefinition rowHeaderColumn = new ColumnDefinition(); + if (!StrechHeaderCellsToContentCellsSize) + { + rowHeaderColumn.Width = GridLength.Auto; + } + BaseGrid.ColumnDefinitions.Add(rowHeaderColumn); + _rowHeadersCells = new Border[Rows]; + + for (int row = 0; row < Rows; row++) + { + Border border = new Border() + { + BorderBrush = HeaderCellBorderBrush, + BorderThickness = HeaderCellBorderThickness, + }; + Grid.SetColumn(border, 0); + Grid.SetRow(border, row + columnHeadersNum); + BaseGrid.Children.Add(border); + _rowHeadersCells[row] = border; + } + RowHeadersRefresh(); + } + + for (int _ = 0; _ < Rows; _++) + { + BaseGrid.RowDefinitions.Add(new RowDefinition() + { + Height = GridLength.Auto, + }); + } + for (int _ = 0; _ < Columns; _++) + { + BaseGrid.ColumnDefinitions.Add(new ColumnDefinition() + { + Width = GridLength.Auto, + }); + } + + for (int row = 0; row < Rows; row++) + { + for (int column = 0; column < Columns; column++) + { + Border border = new Border() + { + BorderBrush = CellBorderBrush, + BorderThickness = CellBorderThickness, + }; + Grid.SetColumn(border, column + rowHeadersNum); + Grid.SetRow(border, row + columnHeadersNum); + + UniformGrid grid = new UniformGrid() + { + Rows = 1, + AllowDrop = true, + Background = Brushes.Transparent, + }; + grid.Drop += OnItemMoved; + + border.Child = grid; + + BaseGrid.Children.Add(border); + _contentCells[row, column] = border; + } + } + ItemsRefresh(); + } + + private void ColumnHeadersRefresh(bool switched = false) => HeadersRefresh(switched, ColumnHeadersSource, _columnHeadersCells, ColumnHeadersTemplate); + private void RowHeadersRefresh(bool switched = false) => HeadersRefresh(switched, RowHeadersSource, _rowHeadersCells, RowHeadersTemplate); + private void HeadersRefresh(bool switched, IEnumerable headerSource, Border[]? headerCells, DataTemplate headerTemplate) + { + if (switched) + { + LayoutRefresh(); + } + + if (headerSource is not null && headerCells is not null) + { + IEnumerable headersSourceCollection = headerSource.Cast(); + for (int index = 0; index < headerCells.Length; index++) + { + headerCells[index].Child = null; + if (index < headersSourceCollection.Count()) + { + object headerItem = headersSourceCollection.ElementAt(index); + + FrameworkElement? control = null; + if (headerTemplate is not null) + { + control = headerTemplate.LoadContent() as FrameworkElement; + if (control is not null) + { + control.DataContext = headerItem; + } + } + control ??= new Label() + { + Content = headerItem.ToString() + }; + + headerCells[index].Child = control; + } + } + } + } + + private void CellBorderRefresh(CellBorderRefreshProperty mode, CellBorderRefreshType type) + { + if ((type & CellBorderRefreshType.Headers) == CellBorderRefreshType.Headers) + { + List> headerCellsCollection = new List>(); + if (_columnHeadersCells is not null) headerCellsCollection.Add(_columnHeadersCells); + if (_rowHeadersCells is not null) headerCellsCollection.Add(_rowHeadersCells); + foreach (IEnumerable cells in headerCellsCollection) + { + foreach (Border cell in cells) + { + if ((mode & CellBorderRefreshProperty.Thickness) == CellBorderRefreshProperty.Thickness) cell.BorderThickness = HeaderCellBorderThickness; + if ((mode & CellBorderRefreshProperty.Brush) == CellBorderRefreshProperty.Brush) cell.BorderBrush = HeaderCellBorderBrush; + } + } + } + if ((type & CellBorderRefreshType.Content) == CellBorderRefreshType.Content && _contentCells is not null) + { + foreach (Border cell in _contentCells) + { + if ((mode & CellBorderRefreshProperty.Thickness) == CellBorderRefreshProperty.Thickness) cell.BorderThickness = CellBorderThickness; + if ((mode & CellBorderRefreshProperty.Brush) == CellBorderRefreshProperty.Brush) cell.BorderBrush = CellBorderBrush; + } + } + } + + private void ItemsRefresh() + { + if (ItemsSource is not null && _contentCells is not null) + { + foreach (DispatcherOperation operation in _dispatcherOperations) + { + operation.Abort(); + } + _dispatcherOperations.Clear(); + + foreach (Border cell in _contentCells) + { + ((UniformGrid)cell.Child).Children.Clear(); + } + + IEnumerable itemsSourceCollection = ItemsSource.Cast(); + foreach (object item in itemsSourceCollection) + { + FrameworkElement? control = null; + if (ItemTemplate is not null) + { + control = ItemTemplate.LoadContent() as FrameworkElement; + if (control is not null) + { + control.DataContext = item; + } + } + control ??= new Label() + { + Content = item.ToString() + }; + + DispatcherOperation operation = Dispatcher.BeginInvoke(() => + { + int row = GetRow(control); + int column = GetColumn(control); + Border cell = _contentCells[row, column]; + UniformGrid cellGrid = (UniformGrid)cell.Child; + if (!cellGrid.Children.Contains(control)) + { + cellGrid.Children.Add(control); + } + }, DispatcherPriority.Loaded); + _dispatcherOperations.Add(operation); + } + } + } + + private void OnItemMoved(object sender, DragEventArgs e) + { + UniformGrid? target = sender as UniformGrid; + object item = e.Data.GetData(DataFormats.Serializable); + if (target is not null && item is UIElement element && _contentCells is not null) + { + int x = 0; + int y = 0; + for (x = 0; x < _contentCells.GetLength(0); x++) + { + for (y = 0; y < _contentCells.GetLength(1); y++) + { + if (_contentCells[x, y].Child.Equals(target)) + { + goto LoopEnd; + } + } + } + LoopEnd: + if (x != GetRow(element) || y != GetColumn(element)) + { + SetRow(element, x); + SetColumn(element, y); + ItemMoved?.Invoke(this, EventArgs.Empty); + ItemMovedCommand?.Execute(ItemMovedCommandParameter); + } + } + } + + private void HeaderRowColumnSizeRefresh() + { + GridLength size; + if (StrechHeaderCellsToContentCellsSize) + { + size = new GridLength(1, GridUnitType.Star); + } + else + { + size = GridLength.Auto; + } + + if (BaseGrid.RowDefinitions.Count > 1 && ColumnHeadersSource is not null) + { + BaseGrid.RowDefinitions[0].Height = size; + } + if (BaseGrid.ColumnDefinitions.Count > 1 && RowHeadersSource is not null) + { + BaseGrid.ColumnDefinitions[0].Width = size; + } + } + + private static void OnLayoutChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) => ((DynamicGrid)obj).LayoutRefresh(); + private static void OnColumnHeadersChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) => ((DynamicGrid)obj).ColumnHeadersRefresh((args.OldValue is null && args.NewValue is not null) || (args.OldValue is not null && args.NewValue is null)); + private static void OnRowHeadersChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) => ((DynamicGrid)obj).RowHeadersRefresh((args.OldValue is null && args.NewValue is not null) || (args.OldValue is not null && args.NewValue is null)); + private static void OnItemsChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) => ((DynamicGrid)obj).ItemsRefresh(); + private static void OnCellBorderThicknessChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) => ((DynamicGrid)obj).CellBorderRefresh(CellBorderRefreshProperty.Thickness, obj.GetValue(HeaderCellBorderThicknessProperty) is null ? CellBorderRefreshType.Headers | CellBorderRefreshType.Content : CellBorderRefreshType.Content); + private static void OnCellBorderBrushChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) => ((DynamicGrid)obj).CellBorderRefresh(CellBorderRefreshProperty.Brush, obj.GetValue(HeaderCellBorderBrushProperty) is null ? CellBorderRefreshType.Headers | CellBorderRefreshType.Content : CellBorderRefreshType.Content); + private static void OnHeaderCellBorderThicknessChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) => ((DynamicGrid)obj).CellBorderRefresh(CellBorderRefreshProperty.Thickness, CellBorderRefreshType.Headers); + private static void OnHeaderCellBorderBrushChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) => ((DynamicGrid)obj).CellBorderRefresh(CellBorderRefreshProperty.Brush, CellBorderRefreshType.Headers); + private static void OnStrechHeaderCellsToContentCellsSizeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) => ((DynamicGrid)obj).HeaderRowColumnSizeRefresh(); + + #endregion + + + + #region COLUMN/ROW SET/GET STATIC METHODS + + private static DependencyProperty ColumnProperty = DependencyProperty.RegisterAttached("Column", typeof(int), typeof(DynamicGrid), new PropertyMetadata(0)); + public static int GetColumn(UIElement element) => (int)element.GetValue(ColumnProperty); + public static void SetColumn(UIElement element, int value) => element.SetValue(ColumnProperty, value); + + private static DependencyProperty RowProperty = DependencyProperty.RegisterAttached("Row", typeof(int), typeof(DynamicGrid), new PropertyMetadata(0)); + public static int GetRow(UIElement element) => (int)element.GetValue(RowProperty); + public static void SetRow(UIElement element, int value) => element.SetValue(RowProperty, value); + + #endregion + + + + #region ENUMS + + [Flags] + private enum CellBorderRefreshProperty + { + Brush = 1, + Thickness = 2, + } + + [Flags] + private enum CellBorderRefreshType + { + Headers = 1, + Content = 2, + } + + #endregion + } +} diff --git a/TimetableDesigner/Controls/TimetableEditorControl.xaml b/TimetableDesigner/Controls/TimetableEditorControl.xaml deleted file mode 100644 index 52d4b1e..0000000 --- a/TimetableDesigner/Controls/TimetableEditorControl.xaml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TimetableDesigner/Controls/TimetableEditorControl.xaml.cs b/TimetableDesigner/Controls/TimetableEditorControl.xaml.cs deleted file mode 100644 index 83bed66..0000000 --- a/TimetableDesigner/Controls/TimetableEditorControl.xaml.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; -using TimetableDesigner.Services; -using TimetableDesigner.Services.Project; -using TimetableDesigner.ViewModels.Models; - -namespace TimetableDesigner.Controls -{ - public partial class TimetableEditorControl : UserControl - { - #region FIELDS - - private IProjectService _projectService; - - #endregion - - - - #region PROPERTIES - - public IGroupViewModel Group - { - get => (IGroupViewModel)GetValue(GroupProperty); - set => SetValue(GroupProperty, value); - } - public static readonly DependencyProperty GroupProperty = DependencyProperty.Register("Group", typeof(IGroupViewModel), typeof(TimetableEditorControl)); - - #endregion - - - - #region CONSTRUCTORS - - public TimetableEditorControl() - { - _projectService = ServiceProvider.Instance.GetService(); - - InitializeComponent(); - } - - #endregion - } -} diff --git a/TimetableDesigner/Converters/BooleanToGridColumnRowVisibilityConverter.cs b/TimetableDesigner/Converters/BooleanToGridColumnRowVisibilityConverter.cs new file mode 100644 index 0000000..00bd265 --- /dev/null +++ b/TimetableDesigner/Converters/BooleanToGridColumnRowVisibilityConverter.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Data; + +namespace TimetableDesigner.Converters +{ + class BooleanToGridColumnRowVisibilityConverter : IValueConverter + { + private static GridLength _notVisible = new GridLength(0, GridUnitType.Pixel); + private static GridLength _visible = new GridLength(1, GridUnitType.Star); + + // (bool)parameter - reverse output + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is null || value.GetType() != typeof(bool)) + { + return new GridLength(1, GridUnitType.Star); + } + bool isVisible = (bool)value; + + bool reverse = false; + if (parameter is not null && parameter.GetType() == typeof(bool)) + { + reverse = (bool)parameter; + } + + return isVisible ^ reverse ? _visible : _notVisible; + } + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is null || value.GetType() != typeof(GridLength)) + { + return true; + } + GridLength visibility = (GridLength)value; + + bool reverse = false; + if (parameter is not null && parameter.GetType() == typeof(bool)) + { + reverse = (bool)parameter; + } + + return visibility != _notVisible ^ reverse; + } + } +} diff --git a/TimetableDesigner/Converters/BooleanToVisibilityConverter.cs b/TimetableDesigner/Converters/BooleanToVisibilityConverter.cs new file mode 100644 index 0000000..c57cdf1 --- /dev/null +++ b/TimetableDesigner/Converters/BooleanToVisibilityConverter.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Data; + +namespace TimetableDesigner.Converters +{ + public class BooleanToVisibilityConverter : IValueConverter + { + // (bool)parameter - reverse output + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is null || value.GetType() != typeof(bool)) + { + return Visibility.Visible; + } + bool isVisible = (bool)value; + + bool reverse = false; + if (parameter is not null && parameter.GetType() == typeof(bool)) + { + reverse = (bool)parameter; + } + + return isVisible ^ reverse ? Visibility.Visible : Visibility.Collapsed; + } + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is null || value.GetType() != typeof(Visibility)) + { + return true; + } + Visibility visibility = (Visibility)value; + + bool reverse = false; + if (parameter is not null && parameter.GetType() == typeof(bool)) + { + reverse = (bool)parameter; + } + + return visibility == Visibility.Visible ^ reverse; + } + } +} diff --git a/TimetableDesigner/Converters/ByteArrayToColorConverter.cs b/TimetableDesigner/Converters/ByteArrayToColorConverter.cs new file mode 100644 index 0000000..08d4563 --- /dev/null +++ b/TimetableDesigner/Converters/ByteArrayToColorConverter.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Windows.Media; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; +using System.Diagnostics; + +namespace TimetableDesigner.Converters +{ + public class ByteArrayToColorConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + byte[] bytes = (byte[])value; + Color color; + switch (bytes.Length) + { + case < 3: color = Colors.Red; break; + case 3: color = Color.FromRgb(bytes[0], bytes[1], bytes[2]); break; + case > 4: color = Color.FromArgb(bytes[0], bytes[1], bytes[2], bytes[3]); break; + } + return color; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + Color color = (Color)value; + List bytes = new List(); + + if (color.A != 0xFF) bytes.Add(color.A); + bytes.Add(color.R); + bytes.Add(color.G); + bytes.Add(color.B); + + return bytes.ToArray(); + } + } +} diff --git a/TimetableDesigner/Converters/ColorBrightnessIsHigherToBooleanConverter.cs b/TimetableDesigner/Converters/ColorBrightnessIsHigherToBooleanConverter.cs new file mode 100644 index 0000000..a08ef5a --- /dev/null +++ b/TimetableDesigner/Converters/ColorBrightnessIsHigherToBooleanConverter.cs @@ -0,0 +1,39 @@ +using ControlzEx.Standard; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; + +namespace TimetableDesigner.Converters +{ + public class ColorBrightnessIsHigherToBooleanConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is byte[] color && (parameter is int min || (parameter is string minStr && int.TryParse(minStr, out min)))) + { + int start = 0; + if (color.Length == 4) + { + start = 1; + } + int result = (int)Math.Sqrt(color[start] * color[start] * .241 + + color[start + 1] * color[start + 1] * .691 + + color[start + 2] * color[start + 2] * .068); + return min < result; + } + return false; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/TimetableDesigner/Converters/IUnitVMToUnitNameConverter.cs b/TimetableDesigner/Converters/IUnitVMToUnitNameConverter.cs new file mode 100644 index 0000000..f3bb424 --- /dev/null +++ b/TimetableDesigner/Converters/IUnitVMToUnitNameConverter.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; +using TimetableDesigner.Core; +using TimetableDesigner.Properties; +using TimetableDesigner.ViewModels.Models; +using TimetableDesigner.ViewModels.Models.Base; + +namespace TimetableDesigner.Converters +{ + internal class IUnitVMToUnitNameConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is IUnitVM unit) + { + switch (unit) + { + case ClassroomVM classroom: return Resources.Global_Classroom; + case TeacherVM teacher: return Resources.Global_Teacher; + case GroupVM group: return Resources.Global_Group; + case SubgroupVM subgroup: return Resources.Global_Subgroup; + } + } + return string.Empty; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/TimetableDesigner/Converters/IntegerAddConstantConverter.cs b/TimetableDesigner/Converters/IntegerAddConstantConverter.cs index b0d0637..8c0bf2c 100644 --- a/TimetableDesigner/Converters/IntegerAddConstantConverter.cs +++ b/TimetableDesigner/Converters/IntegerAddConstantConverter.cs @@ -12,7 +12,7 @@ namespace TimetableDesigner.Converters { #region PUBLIC METHODS - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => (int)value + (int)parameter; + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => (int)value + int.Parse((string)parameter); public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => (int)value - (int)parameter; diff --git a/TimetableDesigner/Converters/TimetableDayToColumnNumberConverter.cs b/TimetableDesigner/Converters/TimetableDayToColumnNumberConverter.cs new file mode 100644 index 0000000..98b8dfb --- /dev/null +++ b/TimetableDesigner/Converters/TimetableDayToColumnNumberConverter.cs @@ -0,0 +1,60 @@ +using System; +using System.CodeDom; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; +using TimetableDesigner.Core; +using TimetableDesigner.Services; +using TimetableDesigner.Services.Project; +using TimetableDesigner.ViewModels.Models; + +namespace TimetableDesigner.Converters +{ + internal class TimetableDayToColumnNumberConverter : IValueConverter + { + #region FIELDS + + private readonly IProjectService _projectService; + + #endregion + + + + #region CONSTRUCTORS + + public TimetableDayToColumnNumberConverter() + { + _projectService = ServiceProvider.Instance.GetService(); + } + + #endregion + + + + #region PUBLIC METHODS + + //value - TimetableDay + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is TimetableDay day) + { + return _projectService.ProjectViewModel.TimetableTemplate.Days.IndexOf(day); + } + return 0; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is int index && index >= 0) + { + return _projectService.ProjectViewModel.TimetableTemplate.Days[index]; + } + return null; + } + + #endregion + } +} diff --git a/TimetableDesigner/Converters/TimetableSlotToRowNumberConverter.cs b/TimetableDesigner/Converters/TimetableSlotToRowNumberConverter.cs new file mode 100644 index 0000000..d5e4141 --- /dev/null +++ b/TimetableDesigner/Converters/TimetableSlotToRowNumberConverter.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; +using TimetableDesigner.Core; +using TimetableDesigner.Services.Project; +using TimetableDesigner.Services; +using System.Diagnostics; + +namespace TimetableDesigner.Converters +{ + internal class TimetableSlotToRowNumberConverter : IValueConverter + { + #region FIELDS + + private readonly IProjectService _projectService; + + #endregion + + + + #region CONSTRUCTORS + + public TimetableSlotToRowNumberConverter() + { + _projectService = ServiceProvider.Instance.GetService(); + } + + #endregion + + + + #region PUBLIC METHODS + + //value - TimetableSlot + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is TimetableSpan slot) + { + return _projectService.ProjectViewModel.TimetableTemplate.Slots.IndexOf(slot); + } + return 0; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is int index && index >= 0) + { + return _projectService.ProjectViewModel.TimetableTemplate.Slots[index]; + } + return null; + } + + #endregion + } +} diff --git a/TimetableDesigner/Converters/ViewModelToViewConverter.cs b/TimetableDesigner/Converters/ViewModelToViewConverter.cs index de6717c..41d19b7 100644 --- a/TimetableDesigner/Converters/ViewModelToViewConverter.cs +++ b/TimetableDesigner/Converters/ViewModelToViewConverter.cs @@ -19,12 +19,13 @@ namespace TimetableDesigner.Converters private static readonly Dictionary _viewModelViewPairs = new Dictionary { - { typeof(MainViewModel), typeof(MainWindow) }, - { typeof(WelcomeViewModel), typeof(WelcomeView) }, - { typeof(ProjectSettingsViewModel), typeof(ProjectSettingsView) }, - { typeof(ClassroomEditViewModel), typeof(ClassroomEditView) }, - { typeof(TeacherEditViewModel), typeof(TeacherEditView) }, - { typeof(GroupEditViewModel), typeof(GroupEditView) }, + { typeof(MainWindowVM), typeof(MainWindow) }, + { typeof(WelcomeViewVM), typeof(WelcomeView) }, + { typeof(ProjectSettingsViewVM), typeof(ProjectSettingsView) }, + { typeof(ClassroomEditorViewVM), typeof(ClassroomEditorView) }, + { typeof(TeacherEditorViewVM), typeof(TeacherEditorView) }, + { typeof(GroupEditorViewVM), typeof(GroupEditorView) }, + { typeof(TimetableEditorViewVM), typeof(TimetableEditorView) }, }; #endregion @@ -35,7 +36,7 @@ namespace TimetableDesigner.Converters public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - BaseViewViewModel? viewModel = value as BaseViewViewModel; + IViewVM? viewModel = value as IViewVM; if (viewModel is not null) { Type view = _viewModelViewPairs[viewModel.GetType()]; diff --git a/TimetableDesigner/Globals/Path.cs b/TimetableDesigner/Globals/Path.cs new file mode 100644 index 0000000..6fc3a06 --- /dev/null +++ b/TimetableDesigner/Globals/Path.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TimetableDesigner.Globals +{ + public static class Path + { + public static readonly string ApplicationData = $"{Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)}\\TimetableDesigner"; + public static readonly string RecentProjectsFile = $"{ApplicationData}\\recent"; + } +} diff --git a/TimetableDesigner/Properties/Resources.Designer.cs b/TimetableDesigner/Properties/Resources.Designer.cs index b16c91b..d2675f0 100644 --- a/TimetableDesigner/Properties/Resources.Designer.cs +++ b/TimetableDesigner/Properties/Resources.Designer.cs @@ -114,6 +114,114 @@ namespace TimetableDesigner.Properties { } } + /// + /// Looks up a localized string similar to During this class, other classes are taking place in this classroom. + /// + public static string Errors_ClassroomBusy { + get { + return ResourceManager.GetString("Errors.ClassroomBusy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Class has no assigned classroom. + /// + public static string Errors_ClassroomNotAssigned { + get { + return ResourceManager.GetString("Errors.ClassroomNotAssigned", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to During this class, group, to which selected subgroup is assigned, already has other classes. + /// + public static string Errors_GroupAllBusy { + get { + return ResourceManager.GetString("Errors.GroupAllBusy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to During this class, the selected group already has other classes. + /// + public static string Errors_GroupBusy { + get { + return ResourceManager.GetString("Errors.GroupBusy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Class has no assigned group. + /// + public static string Errors_GroupNotAssigned { + get { + return ResourceManager.GetString("Errors.GroupNotAssigned", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to During this class, part of selected group already has other classes in subgroup. + /// + public static string Errors_GroupPartBusy { + get { + return ResourceManager.GetString("Errors.GroupPartBusy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Class is not scheduled. + /// + public static string Errors_SlotNotAssigned { + get { + return ResourceManager.GetString("Errors.SlotNotAssigned", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to During this class, the selected teacher conducts other classes. + /// + public static string Errors_TeacherBusy { + get { + return ResourceManager.GetString("Errors.TeacherBusy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Class has no assigned teacher. + /// + public static string Errors_TeacherNotAssigned { + get { + return ResourceManager.GetString("Errors.TeacherNotAssigned", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Selected teacher is not available during the class. + /// + public static string Errors_TeacherNotAvailable { + get { + return ResourceManager.GetString("Errors.TeacherNotAvailable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All files. + /// + public static string Global_AllFiles { + get { + return ResourceManager.GetString("Global.AllFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Classroom. + /// + public static string Global_Classroom { + get { + return ResourceManager.GetString("Global.Classroom", resourceCulture); + } + } + /// /// Looks up a localized string similar to New classroom. /// @@ -150,6 +258,15 @@ namespace TimetableDesigner.Properties { } } + /// + /// Looks up a localized string similar to Group. + /// + public static string Global_Group { + get { + return ResourceManager.GetString("Global.Group", resourceCulture); + } + } + /// /// Looks up a localized string similar to no project loaded. /// @@ -159,6 +276,33 @@ namespace TimetableDesigner.Properties { } } + /// + /// Looks up a localized string similar to TimetableEditor project file. + /// + public static string Global_ProjectFiletypeDescription { + get { + return ResourceManager.GetString("Global.ProjectFiletypeDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Subgroup. + /// + public static string Global_Subgroup { + get { + return ResourceManager.GetString("Global.Subgroup", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Teacher. + /// + public static string Global_Teacher { + get { + return ResourceManager.GetString("Global.Teacher", resourceCulture); + } + } + /// /// Looks up a localized string similar to Delete. /// @@ -231,6 +375,51 @@ namespace TimetableDesigner.Properties { } } + /// + /// Looks up a localized string similar to Class name. + /// + public static string Main_Errors_ClassName { + get { + return ResourceManager.GetString("Main.Errors.ClassName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Day. + /// + public static string Main_Errors_Day { + get { + return ResourceManager.GetString("Main.Errors.Day", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Description. + /// + public static string Main_Errors_Description { + get { + return ResourceManager.GetString("Main.Errors.Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Slot. + /// + public static string Main_Errors_Slot { + get { + return ResourceManager.GetString("Main.Errors.Slot", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Source. + /// + public static string Main_Errors_Source { + get { + return ResourceManager.GetString("Main.Errors.Source", resourceCulture); + } + } + /// /// Looks up a localized string similar to Project is loaded. Do you want to save?. /// @@ -240,6 +429,97 @@ namespace TimetableDesigner.Properties { } } + /// + /// Looks up a localized string similar to Edit. + /// + public static string Main_Ribbon_Edit { + get { + return ResourceManager.GetString("Main.Ribbon.Edit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Timetable. + /// + public static string Main_Ribbon_Edit_Timetable { + get { + return ResourceManager.GetString("Main.Ribbon.Edit.Timetable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Autoschedule all unscheduled classes. + /// + public static string Main_Ribbon_Edit_Timetable_Autoschedule { + get { + return ResourceManager.GetString("Main.Ribbon.Edit.Timetable.Autoschedule", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Following classes could not be scheduled. + /// + public static string Main_Ribbon_Edit_Timetable_Autoschedule_Message_FollowingClassesCouldNotBeScheduled { + get { + return ResourceManager.GetString("Main.Ribbon.Edit.Timetable.Autoschedule.Message.FollowingClassesCouldNotBeSchedul" + + "ed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to more. + /// + public static string Main_Ribbon_Edit_Timetable_Autoschedule_Message_More { + get { + return ResourceManager.GetString("Main.Ribbon.Edit.Timetable.Autoschedule.Message.More", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There is no unscheduled classes. + /// + public static string Main_Ribbon_Edit_Timetable_Autoschedule_Message_NoUnscheduledClasses { + get { + return ResourceManager.GetString("Main.Ribbon.Edit.Timetable.Autoschedule.Message.NoUnscheduledClasses", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Successfully scheduled classes. + /// + public static string Main_Ribbon_Edit_Timetable_Autoschedule_Message_SuccessfullyScheduled { + get { + return ResourceManager.GetString("Main.Ribbon.Edit.Timetable.Autoschedule.Message.SuccessfullyScheduled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Classroom. + /// + public static string Main_Ribbon_Edit_Timetable_Autoschedule_Message_UnitClassroom { + get { + return ResourceManager.GetString("Main.Ribbon.Edit.Timetable.Autoschedule.Message.UnitClassroom", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Group. + /// + public static string Main_Ribbon_Edit_Timetable_Autoschedule_Message_UnitGroup { + get { + return ResourceManager.GetString("Main.Ribbon.Edit.Timetable.Autoschedule.Message.UnitGroup", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Teacher. + /// + public static string Main_Ribbon_Edit_Timetable_Autoschedule_Message_UnitTeacher { + get { + return ResourceManager.GetString("Main.Ribbon.Edit.Timetable.Autoschedule.Message.UnitTeacher", resourceCulture); + } + } + /// /// Looks up a localized string similar to Export. /// @@ -286,38 +566,47 @@ namespace TimetableDesigner.Properties { } /// - /// Looks up a localized string similar to New/Open. + /// Looks up a localized string similar to New. /// - public static string Main_Ribbon_File_NewOpen { + public static string Main_Ribbon_File_New { get { - return ResourceManager.GetString("Main.Ribbon.File.NewOpen", resourceCulture); + return ResourceManager.GetString("Main.Ribbon.File.New", resourceCulture); } } /// /// Looks up a localized string similar to New. /// - public static string Main_Ribbon_File_NewOpen_New { + public static string Main_Ribbon_File_New_New { get { - return ResourceManager.GetString("Main.Ribbon.File.NewOpen.New", resourceCulture); + return ResourceManager.GetString("Main.Ribbon.File.New.New", resourceCulture); } } /// /// Looks up a localized string similar to Open. /// - public static string Main_Ribbon_File_NewOpen_Open { + public static string Main_Ribbon_File_Open { get { - return ResourceManager.GetString("Main.Ribbon.File.NewOpen.Open", resourceCulture); + return ResourceManager.GetString("Main.Ribbon.File.Open", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open. + /// + public static string Main_Ribbon_File_Open_Open { + get { + return ResourceManager.GetString("Main.Ribbon.File.Open.Open", resourceCulture); } } /// /// Looks up a localized string similar to Open recent. /// - public static string Main_Ribbon_File_NewOpen_OpenRecent { + public static string Main_Ribbon_File_Open_OpenRecent { get { - return ResourceManager.GetString("Main.Ribbon.File.NewOpen.OpenRecent", resourceCulture); + return ResourceManager.GetString("Main.Ribbon.File.Open.OpenRecent", resourceCulture); } } @@ -483,6 +772,15 @@ namespace TimetableDesigner.Properties { } } + /// + /// Looks up a localized string similar to Are you sure you want to delete this classroom. This operation is irreversible. Classroom will be unassigned for all classes.. + /// + public static string Main_Treeview_Classrooms_Message_Remove { + get { + return ResourceManager.GetString("Main.Treeview.Classrooms.Message.Remove", resourceCulture); + } + } + /// /// Looks up a localized string similar to Edit timetable. /// @@ -520,7 +818,16 @@ namespace TimetableDesigner.Properties { } /// - /// Looks up a localized string similar to Are you sure you want to delete this subgroup. This operation is irreversible. Subgroup will be unassigned for all groups.. + /// Looks up a localized string similar to Are you sure you want to delete this group. This operation is irreversible. Group will be unassigned for all classes.. + /// + public static string Main_Treeview_Groups_Message_Remove { + get { + return ResourceManager.GetString("Main.Treeview.Groups.Message.Remove", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to delete this subgroup. This operation is irreversible. Subgroup will be unassigned for all groups and classes.. /// public static string Main_Treeview_Subgroups_Message_Remove { get { @@ -546,6 +853,15 @@ namespace TimetableDesigner.Properties { } } + /// + /// Looks up a localized string similar to Are you sure you want to delete this teacher. This operation is irreversible. Teacher will be unassigned for all classes.. + /// + public static string Main_Treeview_Teachers_Message_Remove { + get { + return ResourceManager.GetString("Main.Treeview.Teachers.Message.Remove", resourceCulture); + } + } + /// /// Looks up a localized string similar to Error. /// @@ -555,6 +871,15 @@ namespace TimetableDesigner.Properties { } } + /// + /// Looks up a localized string similar to Information. + /// + public static string MessageBox_Information { + get { + return ResourceManager.GetString("MessageBox.Information", resourceCulture); + } + } + /// /// Looks up a localized string similar to Question. /// @@ -564,6 +889,15 @@ namespace TimetableDesigner.Properties { } } + /// + /// Looks up a localized string similar to Warning. + /// + public static string MessageBox_Warning { + get { + return ResourceManager.GetString("MessageBox.Warning", resourceCulture); + } + } + /// /// Looks up a localized string similar to Author. /// @@ -681,6 +1015,15 @@ namespace TimetableDesigner.Properties { } } + /// + /// Looks up a localized string similar to Timetable editing. + /// + public static string Tabs_TimetableEdit { + get { + return ResourceManager.GetString("Tabs.TimetableEdit", resourceCulture); + } + } + /// /// Looks up a localized string similar to Welcome. /// @@ -761,5 +1104,23 @@ namespace TimetableDesigner.Properties { return ResourceManager.GetString("TeacherEdit.Name", resourceCulture); } } + + /// + /// Looks up a localized string similar to Autoschedule. + /// + public static string TimetableEditor_Autoschedule { + get { + return ResourceManager.GetString("TimetableEditor.Autoschedule", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unscheduled. + /// + public static string TimetableEditor_Unscheduled { + get { + return ResourceManager.GetString("TimetableEditor.Unscheduled", resourceCulture); + } + } } } diff --git a/TimetableDesigner/Properties/Resources.resx b/TimetableDesigner/Properties/Resources.resx index 186698c..c0c2017 100644 --- a/TimetableDesigner/Properties/Resources.resx +++ b/TimetableDesigner/Properties/Resources.resx @@ -135,6 +135,42 @@ Name + + During this class, other classes are taking place in this classroom + + + Class has no assigned classroom + + + During this class, group, to which selected subgroup is assigned, already has other classes + + + During this class, the selected group already has other classes + + + Class has no assigned group + + + During this class, part of selected group already has other classes in subgroup + + + Class is not scheduled + + + During this class, the selected teacher conducts other classes + + + Class has no assigned teacher + + + Selected teacher is not available during the class + + + All files + + + Classroom + New classroom @@ -147,9 +183,21 @@ New teacher + + Group + no project loaded + + TimetableEditor project file + + + Subgroup + + + Teacher + Delete @@ -174,9 +222,54 @@ Subgroups + + Class name + + + Day + + + Description + + + Slot + + + Source + Project is loaded. Do you want to save? + + Edit + + + Timetable + + + Autoschedule all unscheduled classes + + + Following classes could not be scheduled + + + more + + + There is no unscheduled classes + + + Successfully scheduled classes + + + Classroom + + + Group + + + Teacher + Export @@ -192,16 +285,19 @@ File - - New/Open - - + New - + + New + + Open - + + Open + + Open recent @@ -258,6 +354,9 @@ Edit classroom + + Are you sure you want to delete this classroom. This operation is irreversible. Classroom will be unassigned for all classes. + Edit timetable @@ -270,8 +369,11 @@ Edit group + + Are you sure you want to delete this group. This operation is irreversible. Group will be unassigned for all classes. + - Are you sure you want to delete this subgroup. This operation is irreversible. Subgroup will be unassigned for all groups. + Are you sure you want to delete this subgroup. This operation is irreversible. Subgroup will be unassigned for all groups and classes. Teachers @@ -279,12 +381,21 @@ Edit teacher + + Are you sure you want to delete this teacher. This operation is irreversible. Teacher will be unassigned for all classes. + Error + + Information + Question + + Warning + Author @@ -324,6 +435,9 @@ Teacher editing + + Timetable editing + Welcome @@ -351,4 +465,10 @@ Name + + Autoschedule + + + Unscheduled + \ No newline at end of file diff --git a/TimetableDesigner/Properties/Settings.Designer.cs b/TimetableDesigner/Properties/Settings.Designer.cs new file mode 100644 index 0000000..6a52d03 --- /dev/null +++ b/TimetableDesigner/Properties/Settings.Designer.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TimetableDesigner.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.4.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("10")] + public uint RecentProjectsCount { + get { + return ((uint)(this["RecentProjectsCount"])); + } + set { + this["RecentProjectsCount"] = value; + } + } + } +} diff --git a/TimetableDesigner/Properties/Settings.settings b/TimetableDesigner/Properties/Settings.settings new file mode 100644 index 0000000..a20a2b8 --- /dev/null +++ b/TimetableDesigner/Properties/Settings.settings @@ -0,0 +1,9 @@ + + + + + + 10 + + + \ No newline at end of file diff --git a/TimetableDesigner/Resources/Converters.xaml b/TimetableDesigner/Resources/Converters.xaml index 5007b48..7b4521a 100644 --- a/TimetableDesigner/Resources/Converters.xaml +++ b/TimetableDesigner/Resources/Converters.xaml @@ -5,4 +5,10 @@ + + + + + + \ No newline at end of file diff --git a/TimetableDesigner/Resources/Images.xaml b/TimetableDesigner/Resources/Images.xaml index e57d965..fb07e2d 100644 --- a/TimetableDesigner/Resources/Images.xaml +++ b/TimetableDesigner/Resources/Images.xaml @@ -2,8 +2,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib"> + - @@ -18,4 +18,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TimetableDesigner/Resources/Images/Autoschedule.png b/TimetableDesigner/Resources/Images/Autoschedule.png new file mode 100644 index 0000000..bfdad3a Binary files /dev/null and b/TimetableDesigner/Resources/Images/Autoschedule.png differ diff --git a/TimetableDesigner/Resources/Images/ClassroomBlack.png b/TimetableDesigner/Resources/Images/ClassroomBlack.png new file mode 100644 index 0000000..072425b Binary files /dev/null and b/TimetableDesigner/Resources/Images/ClassroomBlack.png differ diff --git a/TimetableDesigner/Resources/Images/ClassroomWhite.png b/TimetableDesigner/Resources/Images/ClassroomWhite.png new file mode 100644 index 0000000..80921cf Binary files /dev/null and b/TimetableDesigner/Resources/Images/ClassroomWhite.png differ diff --git a/TimetableDesigner/Resources/Images/CloneBlack.png b/TimetableDesigner/Resources/Images/CloneBlack.png new file mode 100644 index 0000000..29ef29b Binary files /dev/null and b/TimetableDesigner/Resources/Images/CloneBlack.png differ diff --git a/TimetableDesigner/Resources/Images/CloneWhite.png b/TimetableDesigner/Resources/Images/CloneWhite.png new file mode 100644 index 0000000..4b9e2d7 Binary files /dev/null and b/TimetableDesigner/Resources/Images/CloneWhite.png differ diff --git a/TimetableDesigner/Resources/Images/EditBlack.png b/TimetableDesigner/Resources/Images/EditBlack.png new file mode 100644 index 0000000..7fac261 Binary files /dev/null and b/TimetableDesigner/Resources/Images/EditBlack.png differ diff --git a/TimetableDesigner/Resources/Images/EditWhite.png b/TimetableDesigner/Resources/Images/EditWhite.png new file mode 100644 index 0000000..576f023 Binary files /dev/null and b/TimetableDesigner/Resources/Images/EditWhite.png differ diff --git a/TimetableDesigner/Resources/Images/Error.png b/TimetableDesigner/Resources/Images/Error.png new file mode 100644 index 0000000..fc00b23 Binary files /dev/null and b/TimetableDesigner/Resources/Images/Error.png differ diff --git a/TimetableDesigner/Resources/Images/GroupBlack.png b/TimetableDesigner/Resources/Images/GroupBlack.png new file mode 100644 index 0000000..44bf918 Binary files /dev/null and b/TimetableDesigner/Resources/Images/GroupBlack.png differ diff --git a/TimetableDesigner/Resources/Images/GroupWhite.png b/TimetableDesigner/Resources/Images/GroupWhite.png new file mode 100644 index 0000000..6a4b18e Binary files /dev/null and b/TimetableDesigner/Resources/Images/GroupWhite.png differ diff --git a/TimetableDesigner/Resources/Images/Info.png b/TimetableDesigner/Resources/Images/Info.png new file mode 100644 index 0000000..75ffe11 Binary files /dev/null and b/TimetableDesigner/Resources/Images/Info.png differ diff --git a/TimetableDesigner/Resources/Images/Remove.png b/TimetableDesigner/Resources/Images/RemoveBlack.png similarity index 100% rename from TimetableDesigner/Resources/Images/Remove.png rename to TimetableDesigner/Resources/Images/RemoveBlack.png diff --git a/TimetableDesigner/Resources/Images/RemoveWhite.png b/TimetableDesigner/Resources/Images/RemoveWhite.png new file mode 100644 index 0000000..0419745 Binary files /dev/null and b/TimetableDesigner/Resources/Images/RemoveWhite.png differ diff --git a/TimetableDesigner/Resources/Images/StudentsAdd.png b/TimetableDesigner/Resources/Images/StudentsAdd.png deleted file mode 100644 index 904fca3..0000000 Binary files a/TimetableDesigner/Resources/Images/StudentsAdd.png and /dev/null differ diff --git a/TimetableDesigner/Resources/Images/TeacherBlack.png b/TimetableDesigner/Resources/Images/TeacherBlack.png new file mode 100644 index 0000000..4bc4b78 Binary files /dev/null and b/TimetableDesigner/Resources/Images/TeacherBlack.png differ diff --git a/TimetableDesigner/Resources/Images/TeacherWhite.png b/TimetableDesigner/Resources/Images/TeacherWhite.png new file mode 100644 index 0000000..494135e Binary files /dev/null and b/TimetableDesigner/Resources/Images/TeacherWhite.png differ diff --git a/TimetableDesigner/Resources/Images/Warning.png b/TimetableDesigner/Resources/Images/Warning.png new file mode 100644 index 0000000..2baa822 Binary files /dev/null and b/TimetableDesigner/Resources/Images/Warning.png differ diff --git a/TimetableDesigner/Services/FileDialog/FileDialogService.cs b/TimetableDesigner/Services/FileDialog/FileDialogService.cs new file mode 100644 index 0000000..b2c32f4 --- /dev/null +++ b/TimetableDesigner/Services/FileDialog/FileDialogService.cs @@ -0,0 +1,78 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TimetableDesigner.Properties; + +namespace TimetableDesigner.Services.FileDialog +{ + public class FileDialogService : IFileDialogService + { + #region PUBLIC METHODS + + public string? OpenFile() => OpenFile(new Dictionary>(), true); + public string? OpenFile(IDictionary> types, bool allowAllFiles = false) + { + OpenFileDialog fileDialog = new OpenFileDialog(); + return BuildAndShowDialog(fileDialog, types, allowAllFiles); + } + + public string? SaveFile() => SaveFile(new Dictionary>(), true); + public string? SaveFile(IDictionary> types, bool allowAllFiles = false) + { + SaveFileDialog fileDialog = new SaveFileDialog(); + return BuildAndShowDialog(fileDialog, types, allowAllFiles); + } + + #endregion + + + + #region PRIVATE METHODS + + private static string? BuildAndShowDialog(Microsoft.Win32.FileDialog fileDialog, IDictionary> types, bool allowAllFiles) + { + fileDialog.Filter = BuildFilterString(types, allowAllFiles); + + if (fileDialog.ShowDialog() == true) + { + return fileDialog.FileName; + } + else + { + return null; + } + } + + private static string BuildFilterString(IDictionary> types, bool allFiles) + { + List typeStrings = new List(); + foreach (KeyValuePair> type in types) + { + StringBuilder sb = new StringBuilder(); + sb.Append($"{type.Key}|"); + + List extensionStrings = new List(); + foreach (string extension in type.Value) + { + extensionStrings.Add($"*.{extension}"); + } + + sb.Append(string.Join(';', extensionStrings)); + + typeStrings.Add(sb.ToString()); + } + + if (allFiles) + { + typeStrings.Add($"{Resources.Global_AllFiles}|*.*"); + } + + return string.Join(" | ", typeStrings); + } + + #endregion + } +} diff --git a/TimetableDesigner/Services/FileDialog/IFileDialogService.cs b/TimetableDesigner/Services/FileDialog/IFileDialogService.cs new file mode 100644 index 0000000..a3b299a --- /dev/null +++ b/TimetableDesigner/Services/FileDialog/IFileDialogService.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TimetableDesigner.Services.FileDialog +{ + public interface IFileDialogService : IService + { + string? OpenFile(); + string? OpenFile(IDictionary> types, bool allowAllFiles = false); + + string? SaveFile(); + string? SaveFile(IDictionary> types, bool allowAllFiles = false); + } +} diff --git a/TimetableDesigner/Services/MessageBox/IMessageBoxService.cs b/TimetableDesigner/Services/MessageBox/IMessageBoxService.cs index 4a9ef90..e9b0754 100644 --- a/TimetableDesigner/Services/MessageBox/IMessageBoxService.cs +++ b/TimetableDesigner/Services/MessageBox/IMessageBoxService.cs @@ -16,6 +16,12 @@ namespace TimetableDesigner.Services.MessageBox MessageBoxQuestionResult ShowQuestion(string message, bool hideCancelButton = false); MessageBoxQuestionResult ShowQuestion(string message, string title, bool hideCancelButton = false); + void ShowWarning(string message); + void ShowWarning(string message, string title); + + void ShowInformation(string message); + void ShowInformation(string message, string title); + #endregion } } diff --git a/TimetableDesigner/Services/MessageBox/MessageBoxService.cs b/TimetableDesigner/Services/MessageBox/MessageBoxService.cs index 440f730..0c1faf7 100644 --- a/TimetableDesigner/Services/MessageBox/MessageBoxService.cs +++ b/TimetableDesigner/Services/MessageBox/MessageBoxService.cs @@ -34,6 +34,12 @@ namespace TimetableDesigner.Services.MessageBox } } + public void ShowWarning(string message) => ShowWarning(message, Resources.MessageBox_Warning); + public void ShowWarning(string message, string title) => System.Windows.Forms.MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Warning); + + public void ShowInformation(string message) => ShowInformation(message, Resources.MessageBox_Information); + public void ShowInformation(string message, string title) => System.Windows.Forms.MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Information); + #endregion } } diff --git a/TimetableDesigner/Services/Project/IProjectService.cs b/TimetableDesigner/Services/Project/IProjectService.cs index d445589..cd1d94a 100644 --- a/TimetableDesigner/Services/Project/IProjectService.cs +++ b/TimetableDesigner/Services/Project/IProjectService.cs @@ -1,8 +1,11 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; +using TimetableDesigner.Customs; using TimetableDesigner.ViewModels.Models; namespace TimetableDesigner.Services.Project @@ -12,7 +15,12 @@ namespace TimetableDesigner.Services.Project #region PROPERTIES Core.Project? Project { get; } - ProjectViewModel? ProjectViewModel { get; } + ProjectVM? ProjectViewModel { get; } + + ObservableCollection Errors { get; } + + string? SavePath { get; } + ObservableDictionary RecentProjects { get; } #endregion @@ -21,11 +29,14 @@ namespace TimetableDesigner.Services.Project #region METHODS void New(); - void Load(string path); - void Save(string path); + void RefreshErrors(); + + void LoadRecentProjectsList(); + void SaveRecentProjectsList(); + #endregion } } diff --git a/TimetableDesigner/Services/Project/ProjectError.cs b/TimetableDesigner/Services/Project/ProjectError.cs new file mode 100644 index 0000000..45992b3 --- /dev/null +++ b/TimetableDesigner/Services/Project/ProjectError.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TimetableDesigner.Customs; +using TimetableDesigner.ViewModels.Models; +using TimetableDesigner.ViewModels.Models.Base; + +namespace TimetableDesigner.Services.Project +{ + public class ProjectError : ObservableObject + { + #region FIELDS + + private ProjectErrorType _type; + private IUnitVM _unit; + private ClassVM _class; + private string _description; + + #endregion + + + + #region PROPERTIES + + public ProjectErrorType Type + { + get => _type; + set + { + if (_type != value) + { + _type = value; + NotifyPropertyChanged(nameof(Type)); + } + } + } + public IUnitVM Unit + { + get => _unit; + set + { + if (_unit != value) + { + _unit = value; + NotifyPropertyChanged(nameof(Unit)); + } + } + } + public ClassVM Class + { + get => _class; + set + { + if (value != _class) + { + _class = value; + NotifyPropertyChanged(nameof(Class)); + } + } + } + public string Description + { + get => _description; + set + { + if (value != _description) + { + _description = value; + NotifyPropertyChanged(nameof(Description)); + } + } + } + + #endregion + + + + #region CONSTRUCTORS + + public ProjectError(ProjectErrorType type, IUnitVM unit, ClassVM @class, string description) + { + Type = type; + Unit = unit; + Class = @class; + Description = description; + } + + #endregion + } +} diff --git a/TimetableDesigner/Services/Project/ProjectErrorType.cs b/TimetableDesigner/Services/Project/ProjectErrorType.cs new file mode 100644 index 0000000..7f112c3 --- /dev/null +++ b/TimetableDesigner/Services/Project/ProjectErrorType.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TimetableDesigner.Services.Project +{ + public enum ProjectErrorType + { + Error, + Warning, + Information + } +} diff --git a/TimetableDesigner/Services/Project/ProjectService.cs b/TimetableDesigner/Services/Project/ProjectService.cs index fad1fa0..1f72e48 100644 --- a/TimetableDesigner/Services/Project/ProjectService.cs +++ b/TimetableDesigner/Services/Project/ProjectService.cs @@ -1,12 +1,20 @@ -using System; +using Microsoft.VisualBasic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Data; +using System.Diagnostics; +using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Windows.Input; using TimetableDesigner.Core; using TimetableDesigner.Customs; using TimetableDesigner.Properties; using TimetableDesigner.ViewModels.Models; +using TimetableDesigner.ViewModels.Models.Base; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace TimetableDesigner.Services.Project { @@ -15,7 +23,19 @@ namespace TimetableDesigner.Services.Project #region FIELDS private Core.Project? _project; - private ProjectViewModel? _projectViewModel; + private ProjectVM? _projectViewModel; + + private string? _savePath; + private ObservableDictionary _recentProjects; + + private ObservableCollection _errors; + + private static readonly JsonSerializerSettings _serializationSettings = new JsonSerializerSettings() + { + Formatting = Formatting.Indented, + PreserveReferencesHandling = PreserveReferencesHandling.All, + NullValueHandling = NullValueHandling.Include, + }; #endregion @@ -35,7 +55,7 @@ namespace TimetableDesigner.Services.Project } } } - public ProjectViewModel? ProjectViewModel + public ProjectVM? ProjectViewModel { get => _projectViewModel; private set @@ -48,6 +68,44 @@ namespace TimetableDesigner.Services.Project } } + public string? SavePath + { + get => _savePath; + private set + { + if (_savePath != value) + { + _savePath = value; + NotifyPropertyChanged(nameof(SavePath)); + } + } + } + public ObservableDictionary RecentProjects + { + get => _recentProjects; + private set + { + if (_recentProjects != value) + { + _recentProjects = value; + NotifyPropertyChanged(nameof(RecentProjects)); + } + } + } + + public ObservableCollection Errors + { + get => _errors; + set + { + if (_errors != value) + { + _errors = value; + NotifyPropertyChanged(nameof(Errors)); + } + } + } + #endregion @@ -56,8 +114,13 @@ namespace TimetableDesigner.Services.Project public ProjectService() { - _project = null; - _projectViewModel = null; + Project = null; + ProjectViewModel = null; + + SavePath = null; + RecentProjects = new ObservableDictionary(); + + Errors = new ObservableCollection(); } #endregion @@ -72,17 +135,234 @@ namespace TimetableDesigner.Services.Project { Name = Resources.Global_DefaultProjectName, }; - ProjectViewModel = new ProjectViewModel(Project); + ProjectViewModel = new ProjectVM(Project); + SavePath = null; + RefreshErrors(); } - public void Load(string path) + public async void Load(string path) { + using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read)) + using (StreamReader reader = new StreamReader(stream)) + { + string content = reader.ReadToEnd(); + Project = JsonConvert.DeserializeObject(content, _serializationSettings); + ProjectViewModel = new ProjectVM(Project); + } + SavePath = path; + RefreshErrors(); } public void Save(string path) { + using (FileStream stream = File.Open(path, FileMode.Create, FileAccess.Write)) + using (StreamWriter writer = new StreamWriter(stream)) + { + string content = JsonConvert.SerializeObject(Project, _serializationSettings); + writer.Write(content); + } + SavePath = path; + + if (!RecentProjects.ContainsKey(Project.Guid)) + { + if (RecentProjects.Count == Settings.Default.RecentProjectsCount) + { + IEnumerable> kvp = RecentProjects.Cast>(); + RecentProjects.Remove(kvp.Where(x => x.Value.SaveDate <= kvp.Min(y => y.Value.SaveDate)).FirstOrDefault()); + } + RecentProjects.Add(Project.Guid, new(Project.Name, DateTime.Now, SavePath)); + } + else + { + RecentProjects[Project.Guid] = new(Project.Name, DateTime.Now, SavePath); + } + SaveRecentProjectsList(); + } + + + public void RefreshErrors() + { + Errors.Clear(); + + foreach (ClassVM class1 in ProjectViewModel.Classes) + { + foreach (ClassVM class2 in ProjectViewModel.Classes) + { + if (class1.Equals(class2)) + { + continue; + } + + if (class1.Day is not null + && + class2.Day is not null + && + class1.Day.Equals(class2.Day) + && + class1.Slot is not null + && + class2.Slot is not null + && + class1.Slot.Equals(class2.Slot)) + { + // same classroom at the same time + bool classroomCondition = class1.Classroom is not null + && + class2.Classroom is not null + && + class1.Classroom.Equals(class2.Classroom); + if (classroomCondition) + { + ProjectError newEntry = new ProjectError(ProjectErrorType.Error, class2.Classroom, class2, Resources.Errors_ClassroomBusy); + Errors.Add(newEntry); + } + + // same teacher at the same time + bool teacherCondition = class1.Teacher is not null + && + class2.Teacher is not null + && + class1.Teacher.Equals(class2.Teacher); + if (teacherCondition) + { + ProjectError newEntry = new ProjectError(ProjectErrorType.Error, class2.Teacher, class2, Resources.Errors_TeacherBusy); + Errors.Add(newEntry); + } + + // same group at the same time + if (class1.Group is not null && class2.Group is not null) + { + if (class1.Group.Equals(class2.Group)) + { + ProjectError newEntry = new ProjectError(ProjectErrorType.Error, class2.Group, class2, Resources.Errors_GroupBusy); + Errors.Add(newEntry); + } + else if (class1.Group is GroupVM group1 && class2.Group is SubgroupVM subgroup2 && group1.AssignedSubgroups.Contains(subgroup2)) + { + ProjectError newEntry = new ProjectError(ProjectErrorType.Error, class2.Group, class2, Resources.Errors_GroupPartBusy); + Errors.Add(newEntry); + } + else if (class1.Group is SubgroupVM subgroup1 && class2.Group is GroupVM group2 && group2.AssignedSubgroups.Contains(subgroup1)) + { + ProjectError newEntry = new ProjectError(ProjectErrorType.Error, class2.Group, class2, Resources.Errors_GroupAllBusy); + Errors.Add(newEntry); + } + } + } + } + + bool teacherHoursCondition = class1.Teacher is not null + && + class1.Day is not null + && + class1.Slot is not null + && + ( + !class1.Teacher.AvailabilityHours.ContainsKey(class1.Day) + || + ( + class1.Teacher.AvailabilityHours.ContainsKey(class1.Day) + && + !class1.Teacher.AvailabilityHours[class1.Day].Any(x => x.CheckCollision(class1.Slot) == TimetableSpanCollision.CheckedSlotIn) + ) + ); + if (teacherHoursCondition) + { + ProjectError newEntry = new ProjectError(ProjectErrorType.Warning, class1.Teacher, class1, Resources.Errors_TeacherNotAvailable); + Errors.Add(newEntry); + } + + bool teacherAssigned = class1.Teacher is not null; + bool classroomAssigned = class1.Classroom is not null; + bool groupAssigned = class1.Group is not null; + + List assigned = new List(); + if (groupAssigned) + { + assigned.Add(class1.Group); + } + if (teacherAssigned) + { + assigned.Add(class1.Teacher); + } + if (classroomAssigned) + { + assigned.Add(class1.Classroom); + } + IUnitVM unit = assigned.FirstOrDefault(); + + if (!teacherAssigned) + { + ProjectError newEntry = new ProjectError(ProjectErrorType.Warning, unit, class1, Resources.Errors_TeacherNotAssigned); + Errors.Add(newEntry); + } + if (!classroomAssigned) + { + ProjectError newEntry = new ProjectError(ProjectErrorType.Warning, unit, class1, Resources.Errors_ClassroomNotAssigned); + Errors.Add(newEntry); + } + if (!groupAssigned) + { + ProjectError newEntry = new ProjectError(ProjectErrorType.Warning, unit, class1, Resources.Errors_GroupNotAssigned); + Errors.Add(newEntry); + } + + if (class1.Slot is null || class1.Day is null) + { + ProjectError newEntry = new ProjectError(ProjectErrorType.Information, unit, class1, Resources.Errors_SlotNotAssigned); + Errors.Add(newEntry); + } + } + } + + + public void LoadRecentProjectsList() + { + RecentProjects.Clear(); + + if (!File.Exists(Globals.Path.RecentProjectsFile)) + { + return; + } + + string[] projects; + using (FileStream stream = File.Open(Globals.Path.RecentProjectsFile, FileMode.Open, FileAccess.Read)) + using (StreamReader reader = new StreamReader(stream)) + { + projects = reader.ReadToEnd().Split('\n'); + } + foreach (string project in projects) + { + string[] projectData = project.Split(" : "); + if (projectData.Length < 4) + { + continue; + } + + string name = projectData[1]; + string path = projectData[3]; + if (Guid.TryParse(projectData[0], out Guid guid) && DateTime.TryParse(projectData[2], out DateTime saveDate) && !string.IsNullOrWhiteSpace(path) && File.Exists(path) && !string.IsNullOrWhiteSpace(name)) + { + RecentProjects.Add(guid, new(name, saveDate, path)); + } + } + } + + public void SaveRecentProjectsList() + { + List lines = new List(); + foreach (ObservableKeyValuePair project in RecentProjects) + { + lines.Add($"{project.Key} : {project.Value.Name} : {project.Value.SaveDate} : {project.Value.Path}"); + } + + using (FileStream stream = File.Open(Globals.Path.RecentProjectsFile, FileMode.Create, FileAccess.Write)) + using (StreamWriter reader = new StreamWriter(stream)) + { + reader.Write(string.Join('\n', lines)); + } } #endregion diff --git a/TimetableDesigner/Services/Project/RecentProjectEntry.cs b/TimetableDesigner/Services/Project/RecentProjectEntry.cs new file mode 100644 index 0000000..7acc315 --- /dev/null +++ b/TimetableDesigner/Services/Project/RecentProjectEntry.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TimetableDesigner.Customs; + +namespace TimetableDesigner.Services.Project +{ + public class RecentProjectEntry : ObservableObject + { + #region FIELDS + + private string _name; + private DateTime _saveDate; + private string _path; + + #endregion + + + + #region PROPERTIES + + public string Name + { + get => _name; + set + { + if (_name != value) + { + _name = value; + NotifyPropertyChanged(nameof(Name)); + } + } + } + public DateTime SaveDate + { + get => _saveDate; + set + { + if (_saveDate != value) + { + _saveDate = value; + NotifyPropertyChanged(nameof(SaveDate)); + } + } + } + public string Path + { + get => _path; + set + { + if (_path != value) + { + _path = value; + NotifyPropertyChanged(nameof(Path)); + } + } + } + + #endregion + + + + #region CONSTRUCTORS + + public RecentProjectEntry(string name, DateTime saveDate, string path) + { + Name = name; + SaveDate = saveDate; + Path = path; + } + + #endregion + } +} diff --git a/TimetableDesigner/Services/Scheduler/ISchedulerService.cs b/TimetableDesigner/Services/Scheduler/ISchedulerService.cs new file mode 100644 index 0000000..502c75f --- /dev/null +++ b/TimetableDesigner/Services/Scheduler/ISchedulerService.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TimetableDesigner.Core; +using TimetableDesigner.ViewModels.Models; + +namespace TimetableDesigner.Services.Scheduler +{ + public interface ISchedulerService : IService + { + #region METHODS + + (TimetableDay? Day, TimetableSpan? Slot) Schedule(ClassVM @class); + + #endregion + } +} diff --git a/TimetableDesigner/Services/Scheduler/SchedulerService.cs b/TimetableDesigner/Services/Scheduler/SchedulerService.cs new file mode 100644 index 0000000..db0c6c8 --- /dev/null +++ b/TimetableDesigner/Services/Scheduler/SchedulerService.cs @@ -0,0 +1,201 @@ +using Microsoft.ML; +using Microsoft.ML.Data; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Data; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using TimetableDesigner.Core; +using TimetableDesigner.Services.Project; +using TimetableDesigner.ViewModels.Models; + +namespace TimetableDesigner.Services.Scheduler +{ + public class SchedulerService : ISchedulerService + { + #region FIELDS + + private IProjectService _projectService; + + #endregion + + + + #region CONSTRUCTORS + + public SchedulerService(IProjectService projectService) + { + _projectService = projectService; + } + + #endregion + + + + #region PUBLIC METHODS + + public (TimetableDay? Day, TimetableSpan? Slot) Schedule(ClassVM @class) + { + IEnumerable<(TimetableDay Day, TimetableSpan Slot)> freeSlots = GetFreeSlots(@class); + + List<(TimetableDay Day, TimetableSpan Slot)> allMidBreaks = new List<(TimetableDay Day, TimetableSpan Slot)>(); + List<(TimetableDay Day, TimetableSpan Slot)> secondTierBreaks = new List<(TimetableDay Day, TimetableSpan Slot)>(); + + IEnumerable days = freeSlots.Select(x => x.Day).Distinct(); + foreach (TimetableDay day in days) + { + IEnumerable unusedIndexes = freeSlots.Where(x => x.Day == day).Select(x => _projectService.ProjectViewModel.TimetableTemplate.Slots.IndexOf(x.Slot)); + Debug.WriteLine(day.Name); + foreach (int unusedIndex in unusedIndexes) + { + Debug.WriteLine(unusedIndex); + } + Debug.WriteLine(""); + + IEnumerable<(int Start, int End)> breaks = FindBreaks(unusedIndexes); + int last = _projectService.ProjectViewModel.TimetableTemplate.Slots.Count - 1; + + IEnumerable<(int Start, int End)> midBreaks = breaks.Where(x => x.Start != 0 && x.End != last); + if (midBreaks.Any()) + { + allMidBreaks.Add((day, _projectService.ProjectViewModel.TimetableTemplate.Slots.ElementAt(midBreaks.First().Start))); + } + else + { + (int start, int end) = breaks.First(); + int selIndex = start == 0 && end != last ? end : start; + secondTierBreaks.Add((day, _projectService.ProjectViewModel.TimetableTemplate.Slots.ElementAt(selIndex))); + } + } + + if (allMidBreaks.Any()) + { + return allMidBreaks.First(); + } + + if (secondTierBreaks.Any()) + { + Random rd = new Random(); + return secondTierBreaks.ElementAt(rd.Next(secondTierBreaks.Count)); + } + + return (null, null); + } + + #endregion + + + + #region PRIVATE METHODS + + private IEnumerable<(int Start, int End)> FindBreaks(IEnumerable unusedIndexes) + { + unusedIndexes = unusedIndexes.Order(); + + int prev = unusedIndexes.ElementAt(0); + int first = unusedIndexes.ElementAt(0); + List<(int Start, int End)> breaks = new List<(int Start, int End)>(); + foreach (int index in unusedIndexes.Skip(1)) + { + if (prev + 1 != index) + { + breaks.Add((first, prev)); + first = index; + } + prev = index; + } + breaks.Add((first, prev)); + return breaks; + } + + private Dictionary> CreateSlotsDictionary() + { + Dictionary> slotsDictionary = new Dictionary>(); + foreach (TimetableDay day in _projectService.ProjectViewModel.TimetableTemplate.Days) + { + slotsDictionary[day] = new List(); + foreach (TimetableSpan slot in _projectService.ProjectViewModel.TimetableTemplate.Slots) + { + slotsDictionary[day].Add(slot); + } + } + return slotsDictionary; + } + + private IEnumerable<(TimetableDay Day, TimetableSpan Slot)> GetFreeSlots(ClassVM @class) + { + Dictionary> classroomFreeSlotsDict = CreateSlotsDictionary(); + if (@class.Classroom is not null) + { + IEnumerable classroomClasses = _projectService.ProjectViewModel.Classes.Where(x => x.Classroom == @class.Classroom).Where(x => x.Day is not null && x.Slot is not null); + foreach (ClassVM classroomClass in classroomClasses) + { + classroomFreeSlotsDict[classroomClass.Day].Remove(classroomClass.Slot); + } + } + ICollection<(TimetableDay Day, TimetableSpan Slot)> classroomFreeSlots = new List<(TimetableDay Day, TimetableSpan Slot)>(); + foreach (KeyValuePair> pair in classroomFreeSlotsDict) + { + foreach (TimetableSpan slot in pair.Value) + { + classroomFreeSlots.Add((pair.Key, slot)); + } + } + + Dictionary> teacherFreeSlotsDict = CreateSlotsDictionary(); + if (@class.Teacher is not null) + { + IEnumerable teacherClasses = _projectService.ProjectViewModel.Classes.Where(x => x.Teacher == @class.Teacher).Where(x => x.Day is not null && x.Slot is not null); + foreach (ClassVM teacherClass in teacherClasses) + { + teacherFreeSlotsDict[teacherClass.Day].Remove(teacherClass.Slot); + } + } + ICollection<(TimetableDay Day, TimetableSpan Slot)> teacherFreeSlots = new List<(TimetableDay Day, TimetableSpan Slot)>(); + foreach (KeyValuePair> pair in teacherFreeSlotsDict) + { + foreach (TimetableSpan slot in pair.Value) + { + teacherFreeSlots.Add((pair.Key, slot)); + } + } + + Dictionary> groupFreeSlotsDict = CreateSlotsDictionary(); + if (@class.Group is not null) + { + IEnumerable groupClasses; + if (@class.Group is SubgroupVM subgroup) + { + IEnumerable groups = _projectService.ProjectViewModel.Groups.Where(x => x.AssignedSubgroups.Contains(subgroup)); + groupClasses = _projectService.ProjectViewModel.Classes.Where(x => (x.Group is GroupVM group && groups.Contains(group)) || x.Group == subgroup); + } + else + { + GroupVM group = @class.Group as GroupVM; + groupClasses = _projectService.ProjectViewModel.Classes.Where(x => x.Group == group || (x.Group is SubgroupVM subgroup && group.AssignedSubgroups.Contains(subgroup))); + } + groupClasses = groupClasses.Where(x => x.Day is not null && x.Slot is not null); + foreach (ClassVM groupClass in groupClasses) + { + groupFreeSlotsDict[groupClass.Day].Remove(groupClass.Slot); + } + } + ICollection<(TimetableDay Day, TimetableSpan Slot)> groupFreeSlots = new List<(TimetableDay Day, TimetableSpan Slot)>(); + foreach (KeyValuePair> pair in groupFreeSlotsDict) + { + foreach (TimetableSpan slot in pair.Value) + { + groupFreeSlots.Add((pair.Key, slot)); + } + } + + return classroomFreeSlots.Intersect(teacherFreeSlots).Intersect(groupFreeSlots); + } + + #endregion + } +} diff --git a/TimetableDesigner/Services/TabNavigation/TabItem.cs b/TimetableDesigner/Services/TabNavigation/TabItem.cs index a1ffebf..6aa93c7 100644 --- a/TimetableDesigner/Services/TabNavigation/TabItem.cs +++ b/TimetableDesigner/Services/TabNavigation/TabItem.cs @@ -21,7 +21,7 @@ namespace TimetableDesigner.Services.TabNavigation private ImageSource _image; private string _title; private bool _isClosable; - private BaseViewViewModel _viewModel; + private IViewVM _viewModel; #endregion @@ -65,7 +65,7 @@ namespace TimetableDesigner.Services.TabNavigation } } } - public BaseViewViewModel ViewModel + public IViewVM ViewModel { get => _viewModel; set diff --git a/TimetableDesigner/Services/TabNavigation/TabNavigationService.cs b/TimetableDesigner/Services/TabNavigation/TabNavigationService.cs index b29f838..08aa3b0 100644 --- a/TimetableDesigner/Services/TabNavigation/TabNavigationService.cs +++ b/TimetableDesigner/Services/TabNavigation/TabNavigationService.cs @@ -73,7 +73,7 @@ namespace TimetableDesigner.Services.TabNavigation public void Close(TabItem item) => Close(new List() { item }); public void Close(IEnumerable items) { - TabItem selected = SelectedTab; + TabItem? selected = SelectedTab; while (items.Contains(selected) && selected != null) { int nextIndex = Tabs.IndexOf(selected) + 1; @@ -102,7 +102,7 @@ namespace TimetableDesigner.Services.TabNavigation } } } - foreach (TabItem item in items) + foreach (TabItem item in items.ToList()) { Tabs.Remove(item); } diff --git a/TimetableDesigner/Settings.cs b/TimetableDesigner/Settings.cs new file mode 100644 index 0000000..b8c4698 --- /dev/null +++ b/TimetableDesigner/Settings.cs @@ -0,0 +1,28 @@ +namespace TimetableDesigner.Properties { + + + // This class allows you to handle specific events on the settings class: + // The SettingChanging event is raised before a setting's value is changed. + // The PropertyChanged event is raised after a setting's value is changed. + // The SettingsLoaded event is raised after the setting values are loaded. + // The SettingsSaving event is raised before the setting values are saved. + internal sealed partial class Settings { + + public Settings() { + // // To add event handlers for saving and changing settings, uncomment the lines below: + // + // this.SettingChanging += this.SettingChangingEventHandler; + // + // this.SettingsSaving += this.SettingsSavingEventHandler; + // + } + + private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { + // Add code to handle the SettingChangingEvent event here. + } + + private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { + // Add code to handle the SettingsSaving event here. + } + } +} diff --git a/TimetableDesigner/TimetableDesigner.csproj b/TimetableDesigner/TimetableDesigner.csproj index 3e0ee7c..8b189fe 100644 --- a/TimetableDesigner/TimetableDesigner.csproj +++ b/TimetableDesigner/TimetableDesigner.csproj @@ -16,29 +16,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always @@ -70,7 +127,10 @@ Always - + + Always + + Always @@ -88,11 +148,23 @@ Always + + Always + + + Always + + + Always + + + + @@ -117,6 +189,11 @@ True Resources.resx + + True + True + Settings.settings + @@ -130,4 +207,11 @@ + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + diff --git a/TimetableDesigner/ViewModels/BaseViewViewModel.cs b/TimetableDesigner/ViewModels/IModelVM.cs similarity index 64% rename from TimetableDesigner/ViewModels/BaseViewViewModel.cs rename to TimetableDesigner/ViewModels/IModelVM.cs index d86ea09..4dc32c5 100644 --- a/TimetableDesigner/ViewModels/BaseViewViewModel.cs +++ b/TimetableDesigner/ViewModels/IModelVM.cs @@ -3,11 +3,10 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using TimetableDesigner.Customs; namespace TimetableDesigner.ViewModels { - public abstract class BaseViewViewModel : ObservableObject + public interface IModelVM { } } diff --git a/TimetableDesigner/ViewModels/IRemovableVM.cs b/TimetableDesigner/ViewModels/IRemovableVM.cs new file mode 100644 index 0000000..3d68afe --- /dev/null +++ b/TimetableDesigner/ViewModels/IRemovableVM.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TimetableDesigner.ViewModels.Models.Base +{ + public interface IRemovableVM : IUnitVM + { + } +} diff --git a/TimetableDesigner/ViewModels/IUnitEditorViewVM.cs b/TimetableDesigner/ViewModels/IUnitEditorViewVM.cs new file mode 100644 index 0000000..555636a --- /dev/null +++ b/TimetableDesigner/ViewModels/IUnitEditorViewVM.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TimetableDesigner.ViewModels.Models.Base; + +namespace TimetableDesigner.ViewModels +{ + internal interface IUnitEditorViewVM : IViewVM + { + #region PROPERTIES + + IUnitVM Unit { get; } + + #endregion + } +} diff --git a/TimetableDesigner/ViewModels/Models/IGroupViewModel.cs b/TimetableDesigner/ViewModels/IUnitVM.cs similarity index 65% rename from TimetableDesigner/ViewModels/Models/IGroupViewModel.cs rename to TimetableDesigner/ViewModels/IUnitVM.cs index 2b054d9..2df389b 100644 --- a/TimetableDesigner/ViewModels/Models/IGroupViewModel.cs +++ b/TimetableDesigner/ViewModels/IUnitVM.cs @@ -5,13 +5,13 @@ using System.Text; using System.Threading.Tasks; using TimetableDesigner.Core; -namespace TimetableDesigner.ViewModels.Models +namespace TimetableDesigner.ViewModels.Models.Base { - public interface IGroupViewModel + public interface IUnitVM : IModelVM { #region PROPERTIES - - IGroup Group { get; } + + IUnit Unit { get; } string Name { get; } #endregion diff --git a/TimetableDesigner/ViewModels/BaseModelViewModel.cs b/TimetableDesigner/ViewModels/IViewVM.cs similarity index 58% rename from TimetableDesigner/ViewModels/BaseModelViewModel.cs rename to TimetableDesigner/ViewModels/IViewVM.cs index 37898b9..ece4fe4 100644 --- a/TimetableDesigner/ViewModels/BaseModelViewModel.cs +++ b/TimetableDesigner/ViewModels/IViewVM.cs @@ -1,14 +1,12 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; -using TimetableDesigner.Customs; namespace TimetableDesigner.ViewModels { - public abstract class BaseModelViewModel : ObservableObject + public interface IViewVM { } } diff --git a/TimetableDesigner/ViewModels/Models/BaseGroupVM.cs b/TimetableDesigner/ViewModels/Models/BaseGroupVM.cs new file mode 100644 index 0000000..5dc5e68 --- /dev/null +++ b/TimetableDesigner/ViewModels/Models/BaseGroupVM.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TimetableDesigner.Core; +using TimetableDesigner.Customs; +using TimetableDesigner.ViewModels.Models.Base; + +namespace TimetableDesigner.ViewModels.Models +{ + public abstract class BaseGroupVM : ObservableObject, IModelVM, IUnitVM + { + #region FIELDS + + protected BaseGroup _baseGroup; + + #endregion + + + + #region PROPERTIES + + IUnit IUnitVM.Unit => _baseGroup; + public BaseGroup BaseGroup => _baseGroup; + + public string Name + { + get => _baseGroup.Name; + set + { + if (_baseGroup.Name != value) + { + _baseGroup.Name = value; + NotifyPropertyChanged(nameof(Name)); + } + } + } + public string ShortName + { + get => _baseGroup.ShortName; + set + { + if (_baseGroup.ShortName != value) + { + _baseGroup.ShortName = value; + NotifyPropertyChanged(nameof(ShortName)); + } + } + } + + #endregion + + + + #region CONSTRUCTORS + + public BaseGroupVM(BaseGroup baseGroup) + { + _baseGroup = baseGroup; + } + + #endregion + } +} diff --git a/TimetableDesigner/ViewModels/Models/ClassViewModel.cs b/TimetableDesigner/ViewModels/Models/ClassVM.cs similarity index 59% rename from TimetableDesigner/ViewModels/Models/ClassViewModel.cs rename to TimetableDesigner/ViewModels/Models/ClassVM.cs index 1fdfc03..69e7624 100644 --- a/TimetableDesigner/ViewModels/Models/ClassViewModel.cs +++ b/TimetableDesigner/ViewModels/Models/ClassVM.cs @@ -1,16 +1,20 @@ using System; using System.Collections.Generic; using System.ComponentModel.Design; +using System.Diagnostics; +using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using TimetableDesigner.Core; +using TimetableDesigner.Customs; using TimetableDesigner.Services; using TimetableDesigner.Services.Project; +using TimetableDesigner.ViewModels.Models.Base; namespace TimetableDesigner.ViewModels.Models { - public class ClassViewModel : BaseModelViewModel + public class ClassVM : ObservableObject, IModelVM { #region FIELDS @@ -38,7 +42,7 @@ namespace TimetableDesigner.ViewModels.Models } } } - public TeacherViewModel? Teacher + public TeacherVM? Teacher { get => _projectService.ProjectViewModel?.Teachers.Where(vm => vm.Teacher == _class.Teacher).FirstOrDefault(); set @@ -47,18 +51,22 @@ namespace TimetableDesigner.ViewModels.Models { _class.Teacher = value?.Teacher; NotifyPropertyChanged(nameof(Teacher)); + + //REFRESH: Errors + _projectService.RefreshErrors(); } } } - public IGroupViewModel? Group + public BaseGroupVM? Group { get { - if (_class.Group?.GetType() == typeof(GroupViewModel)) + + if (_class.Group?.GetType() == typeof(Group)) { return _projectService.ProjectViewModel?.Groups.Where(vm => vm.Group == _class.Group).FirstOrDefault(); } - else if (_class.Group?.GetType() == typeof(SubgroupViewModel)) + else if (_class.Group?.GetType() == typeof(Subgroup)) { return _projectService.ProjectViewModel?.Subgroups.Where(vm => vm.Subgroup == _class.Group).FirstOrDefault(); } @@ -69,14 +77,17 @@ namespace TimetableDesigner.ViewModels.Models } set { - if (_class.Group != value?.Group) + if (_class.Group != value?.BaseGroup) { - _class.Group = value?.Group; + _class.Group = value?.BaseGroup; NotifyPropertyChanged(nameof(Group)); + + //REFRESH: Errors + _projectService.RefreshErrors(); } } } - public ClassroomViewModel? Classroom + public ClassroomVM? Classroom { get => _projectService.ProjectViewModel?.Classrooms.Where(vm => vm.Classroom == _class.Classroom).FirstOrDefault(); set @@ -85,6 +96,45 @@ namespace TimetableDesigner.ViewModels.Models { _class.Classroom = value?.Classroom; NotifyPropertyChanged(nameof(Classroom)); + + //REFRESH: Errors + _projectService.RefreshErrors(); + } + } + } + public TimetableDay? Day + { + get => _class.Day; + set + { + if (value != _class.Day) + { + _class.Day = value; + NotifyPropertyChanged(nameof(Day)); + } + } + } + public TimetableSpan? Slot + { + get => _class.Slot; + set + { + if (value != _class.Slot) + { + _class.Slot = value; + NotifyPropertyChanged(nameof(Slot)); + } + } + } + public byte[] Color + { + get => _class.Color; + set + { + if (value != _class.Color) + { + _class.Color = value; + NotifyPropertyChanged(nameof(Color)); } } } @@ -95,7 +145,7 @@ namespace TimetableDesigner.ViewModels.Models #region CONSTRUCTORS - public ClassViewModel(Class @class) + public ClassVM(Class @class) { _projectService = ServiceProvider.Instance.GetService(); diff --git a/TimetableDesigner/ViewModels/Models/ClassroomViewModel.cs b/TimetableDesigner/ViewModels/Models/ClassroomVM.cs similarity index 88% rename from TimetableDesigner/ViewModels/Models/ClassroomViewModel.cs rename to TimetableDesigner/ViewModels/Models/ClassroomVM.cs index 337405e..16d516b 100644 --- a/TimetableDesigner/ViewModels/Models/ClassroomViewModel.cs +++ b/TimetableDesigner/ViewModels/Models/ClassroomVM.cs @@ -4,10 +4,12 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using TimetableDesigner.Core; +using TimetableDesigner.Customs; +using TimetableDesigner.ViewModels.Models.Base; namespace TimetableDesigner.ViewModels.Models { - public class ClassroomViewModel : BaseModelViewModel + public class ClassroomVM : ObservableObject, IModelVM, IUnitVM, IRemovableVM { #region FIELDS @@ -19,6 +21,7 @@ namespace TimetableDesigner.ViewModels.Models #region PROPERTIES + IUnit IUnitVM.Unit => _classroom; public Classroom Classroom => _classroom; public string Name @@ -76,7 +79,7 @@ namespace TimetableDesigner.ViewModels.Models #region CONSTRUCTORS - public ClassroomViewModel(Classroom classroom) + public ClassroomVM(Classroom classroom) { _classroom = classroom; } diff --git a/TimetableDesigner/ViewModels/Models/GroupViewModel.cs b/TimetableDesigner/ViewModels/Models/GroupVM.cs similarity index 53% rename from TimetableDesigner/ViewModels/Models/GroupViewModel.cs rename to TimetableDesigner/ViewModels/Models/GroupVM.cs index a64c978..73cb8b5 100644 --- a/TimetableDesigner/ViewModels/Models/GroupViewModel.cs +++ b/TimetableDesigner/ViewModels/Models/GroupVM.cs @@ -5,53 +5,40 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using TimetableDesigner.Core; +using TimetableDesigner.Customs; using TimetableDesigner.Services; using TimetableDesigner.Services.Project; +using TimetableDesigner.ViewModels.Models.Base; namespace TimetableDesigner.ViewModels.Models { - public class GroupViewModel : BaseModelViewModel, IGroupViewModel + public class GroupVM : BaseGroupVM, IRemovableVM { #region FIELDS private IProjectService _projectService; - private Group _group; - #endregion #region PROPERTIES - IGroup IGroupViewModel.Group => _group; - public Group Group => _group; + public Group Group => (Group)_baseGroup; - public string Name - { - get => _group.Name; - set - { - if (_group.Name != value) - { - _group.Name = value; - NotifyPropertyChanged(nameof(Name)); - } - } - } public string Description { - get => _group.Description; + get => Group.Description; set { - if (_group.Description != value) + if (Group.Description != value) { - _group.Description = value; + Group.Description = value; NotifyPropertyChanged(nameof(Description)); } } } - public ObservableCollection AssignedSubgroups => new ObservableCollection(_projectService.ProjectViewModel.Subgroups.Where(vm => Group.AssignedSubgroups.Contains(vm.Subgroup))); + public ObservableCollection AssignedSubgroups => new ObservableCollection(_projectService.ProjectViewModel.Subgroups.Where(vm => Group.AssignedSubgroups.Contains(vm.Subgroup))); #endregion @@ -59,10 +46,9 @@ namespace TimetableDesigner.ViewModels.Models #region CONSTRUCTORS - public GroupViewModel(Group group) + public GroupVM(Group group) : base(group) { _projectService = ServiceProvider.Instance.GetService(); - _group = group; } #endregion @@ -71,16 +57,22 @@ namespace TimetableDesigner.ViewModels.Models #region PUBLIC METHODS - public void AddSubgroup(SubgroupViewModel subgroup) + public void AddSubgroup(SubgroupVM subgroup) { Group.AssignedSubgroups.Add(subgroup.Subgroup); NotifyPropertyChanged(nameof(AssignedSubgroups)); + + //REFRESH: Errors + _projectService.RefreshErrors(); } - public void RemoveSubgroup(SubgroupViewModel subgroup) + public void RemoveSubgroup(SubgroupVM subgroup) { Group.AssignedSubgroups.Remove(subgroup.Subgroup); NotifyPropertyChanged(nameof(AssignedSubgroups)); + + //REFRESH: Errors + _projectService.RefreshErrors(); } #endregion diff --git a/TimetableDesigner/ViewModels/Models/ProjectVM.cs b/TimetableDesigner/ViewModels/Models/ProjectVM.cs new file mode 100644 index 0000000..462ee6b --- /dev/null +++ b/TimetableDesigner/ViewModels/Models/ProjectVM.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TimetableDesigner.Core; +using TimetableDesigner.Customs; +using TimetableDesigner.Services.Project; + +namespace TimetableDesigner.ViewModels.Models +{ + public class ProjectVM : ObservableObject, IModelVM + { + #region FIELDS + + private Project _project; + + #endregion + + + + #region PROPERTIES + + public Project Project => _project; + + public string Name + { + get => Project.Name; + set + { + if (Project.Name != value) + { + Project.Name = value; + NotifyPropertyChanged(nameof(Name)); + } + } + } + public string Author + { + get => Project.Author; + set + { + if (Project.Author != value) + { + Project.Author = value; + NotifyPropertyChanged(nameof(Author)); + } + } + } + public string Description + { + get => Project.Description; + set + { + if (Project.Description != value) + { + Project.Description = value; + NotifyPropertyChanged(nameof(Description)); + } + } + } + public TimetableTemplateVM TimetableTemplate { get; set; } + public ObservableCollection Classrooms { get; set; } + public ObservableCollection Teachers { get; set; } + public ObservableCollection Groups { get; set; } + public ObservableCollection Subgroups { get; set; } + public ObservableCollection Classes { get; set; } + + #endregion + + + + #region CONSTRUCTORS + + public ProjectVM(Project project) + { + _project = project; + + TimetableTemplate = new TimetableTemplateVM(_project.TimetableTemplate); + + Classrooms = new ObservableCollection(_project.Classrooms.Select(item => new ClassroomVM(item))); + Classrooms.CollectionChanged += Classrooms_CollectionChanged; + + Teachers = new ObservableCollection(_project.Teachers.Select(item => new TeacherVM(item))); + Teachers.CollectionChanged += Teachers_CollectionChanged; + + Groups = new ObservableCollection(_project.Groups.Select(item => new GroupVM(item))); + Groups.CollectionChanged += Groups_CollectionChanged; + + Subgroups = new ObservableCollection(_project.Subgroups.Select(item => new SubgroupVM(item))); + Subgroups.CollectionChanged += Subgroups_CollectionChanged; + + Classes = new ObservableCollection(_project.Classes.Select(item => new ClassVM(item))); + Classes.CollectionChanged += Classes_CollectionChanged; + } + + #endregion + + + + #region PRIVATE METHODS + + private void Classrooms_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + if (e.OldItems is not null) + { + foreach (ClassroomVM vm in e.OldItems) + { + foreach (ClassVM cvm in Classes.Where(x => x.Classroom == vm)) + { + cvm.Classroom = null; + } + _project.Classrooms.Remove(vm.Classroom); + } + } + + if (e.NewItems is not null) + { + foreach (ClassroomVM vm in e.NewItems) + { + _project.Classrooms.Add(vm.Classroom); + } + } + } + + private void Teachers_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + if (e.OldItems is not null) + { + foreach (TeacherVM vm in e.OldItems) + { + foreach (ClassVM cvm in Classes.Where(x => x.Teacher == vm)) + { + cvm.Teacher = null; + } + _project.Teachers.Remove(vm.Teacher); + } + } + + if (e.NewItems is not null) + { + foreach (TeacherVM vm in e.NewItems) + { + _project.Teachers.Add(vm.Teacher); + } + } + } + + private void Groups_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + if (e.OldItems is not null) + { + foreach (GroupVM vm in e.OldItems) + { + foreach (ClassVM cvm in Classes.Where(x => x.Group == vm)) + { + cvm.Group = null; + } + _project.Groups.Remove(vm.Group); + } + } + + if (e.NewItems is not null) + { + foreach (GroupVM vm in e.NewItems) + { + _project.Groups.Add(vm.Group); + } + } + } + + private void Subgroups_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + if (e.OldItems is not null) + { + foreach (SubgroupVM vm in e.OldItems) + { + foreach (ClassVM cvm in Classes.Where(x => x.Group == vm)) + { + cvm.Group = null; + } + foreach (GroupVM gvm in Groups.Where(x => x.AssignedSubgroups.Contains(vm))) + { + gvm.RemoveSubgroup(vm); + } + _project.Subgroups.Remove(vm.Subgroup); + } + } + + if (e.NewItems is not null) + { + foreach (SubgroupVM vm in e.NewItems) + { + _project.Subgroups.Add(vm.Subgroup); + } + } + } + + private void Classes_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + if (e.OldItems is not null) + { + foreach (ClassVM vm in e.OldItems) + { + _project.Classes.Remove(vm.Class); + } + } + + if (e.NewItems is not null) + { + foreach (ClassVM vm in e.NewItems) + { + _project.Classes.Add(vm.Class); + } + } + } + + #endregion + } +} diff --git a/TimetableDesigner/ViewModels/Models/ProjectViewModel.cs b/TimetableDesigner/ViewModels/Models/ProjectViewModel.cs deleted file mode 100644 index 5046821..0000000 --- a/TimetableDesigner/ViewModels/Models/ProjectViewModel.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using TimetableDesigner.Core; - -namespace TimetableDesigner.ViewModels.Models -{ - public class ProjectViewModel : BaseModelViewModel - { - #region FIELDS - - private Project _project; - - #endregion - - - - #region PROPERTIES - - public Project Project => _project; - - public string Name - { - get => Project.Name; - set - { - if (Project.Name != value) - { - Project.Name = value; - NotifyPropertyChanged(nameof(Name)); - } - } - } - public string Author - { - get => Project.Author; - set - { - if (Project.Author != value) - { - Project.Author = value; - NotifyPropertyChanged(nameof(Author)); - } - } - } - public string Description - { - get => Project.Description; - set - { - if (Project.Description != value) - { - Project.Description = value; - NotifyPropertyChanged(nameof(Description)); - } - } - } - public TimetableTemplateViewModel TimetableTemplate { get; set; } - public ObservableCollection Classrooms { get; set; } - public ObservableCollection Teachers { get; set; } - public ObservableCollection Groups { get; set; } - public ObservableCollection Subgroups { get; set; } - - #endregion - - - - #region CONSTRUCTORS - - public ProjectViewModel(Project project) - { - _project = project; - - TimetableTemplate = new TimetableTemplateViewModel(_project.TimetableTemplate); - - Classrooms = new ObservableCollection(); - Classrooms.CollectionChanged += Classrooms_CollectionChanged; - - Teachers = new ObservableCollection(); - Teachers.CollectionChanged += Teachers_CollectionChanged; - - Groups = new ObservableCollection(); - Groups.CollectionChanged += Groups_CollectionChanged; - - Subgroups = new ObservableCollection(); - Subgroups.CollectionChanged += Subgroups_CollectionChanged; - } - - #endregion - - - - #region PRIVATE METHODS - - private void Classrooms_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) - { - IList? added = e.NewItems as IList; - IList? removed = e.OldItems as IList; - - if (removed is not null) - { - foreach (ClassroomViewModel vm in removed) - { - _project.Classrooms.Remove(vm.Classroom); - } - } - - if (added is not null) - { - foreach (ClassroomViewModel vm in added) - { - _project.Classrooms.Add(vm.Classroom); - } - } - } - - private void Teachers_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) - { - IList? added = e.NewItems as IList; - IList? removed = e.OldItems as IList; - - if (removed is not null) - { - foreach (TeacherViewModel vm in removed) - { - _project.Teachers.Remove(vm.Teacher); - } - } - - if (added is not null) - { - foreach (TeacherViewModel vm in added) - { - _project.Teachers.Add(vm.Teacher); - } - } - } - - private void Groups_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) - { - IList? added = e.NewItems as IList; - IList? removed = e.OldItems as IList; - - if (removed is not null) - { - foreach (GroupViewModel vm in removed) - { - _project.Groups.Remove(vm.Group); - } - } - - if (added is not null) - { - foreach (GroupViewModel vm in added) - { - _project.Groups.Add(vm.Group); - } - } - } - - private void Subgroups_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) - { - IList? added = e.NewItems as IList; - IList? removed = e.OldItems as IList; - - if (removed is not null) - { - foreach (SubgroupViewModel vm in removed) - { - _project.Subgroups.Remove(vm.Subgroup); - } - } - - if (added is not null) - { - foreach (SubgroupViewModel vm in added) - { - _project.Subgroups.Add(vm.Subgroup); - } - } - } - - #endregion - } -} diff --git a/TimetableDesigner/ViewModels/Models/SubgroupVM.cs b/TimetableDesigner/ViewModels/Models/SubgroupVM.cs new file mode 100644 index 0000000..91c649a --- /dev/null +++ b/TimetableDesigner/ViewModels/Models/SubgroupVM.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TimetableDesigner.Core; +using TimetableDesigner.Customs; +using TimetableDesigner.ViewModels.Models.Base; + +namespace TimetableDesigner.ViewModels.Models +{ + public class SubgroupVM : BaseGroupVM, IRemovableVM + { + #region PROPERTIES + + public Subgroup Subgroup => (Subgroup)_baseGroup; + + #endregion + + + + #region CONSTRUCTORS + + public SubgroupVM(Subgroup subgroup) : base(subgroup) + { } + + #endregion + } +} diff --git a/TimetableDesigner/ViewModels/Models/SubgroupViewModel.cs b/TimetableDesigner/ViewModels/Models/SubgroupViewModel.cs deleted file mode 100644 index 3f6ac8e..0000000 --- a/TimetableDesigner/ViewModels/Models/SubgroupViewModel.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using TimetableDesigner.Core; - -namespace TimetableDesigner.ViewModels.Models -{ - public class SubgroupViewModel : BaseModelViewModel, IGroupViewModel - { - #region FIELDS - - private Subgroup _subgroup; - - #endregion - - - - #region PROPERTIES - - IGroup IGroupViewModel.Group => _subgroup; - public Subgroup Subgroup => _subgroup; - - public string Name - { - get => _subgroup.Name; - set - { - if (_subgroup.Name != value) - { - _subgroup.Name = value; - NotifyPropertyChanged(nameof(Name)); - } - } - } - - #endregion - - - - #region CONSTRUCTORS - - public SubgroupViewModel(Subgroup subgroup) - { - _subgroup = subgroup; - } - - #endregion - } -} diff --git a/TimetableDesigner/ViewModels/Models/TeacherViewModel.cs b/TimetableDesigner/ViewModels/Models/TeacherVM.cs similarity index 78% rename from TimetableDesigner/ViewModels/Models/TeacherViewModel.cs rename to TimetableDesigner/ViewModels/Models/TeacherVM.cs index 77e207d..e7a8bf5 100644 --- a/TimetableDesigner/ViewModels/Models/TeacherViewModel.cs +++ b/TimetableDesigner/ViewModels/Models/TeacherVM.cs @@ -6,13 +6,16 @@ using System.Text; using System.Threading.Tasks; using TimetableDesigner.Core; using TimetableDesigner.Customs; +using TimetableDesigner.Services.Project; +using TimetableDesigner.ViewModels.Models.Base; namespace TimetableDesigner.ViewModels.Models { - public class TeacherViewModel : BaseModelViewModel + public class TeacherVM : ObservableObject, IModelVM, IUnitVM, IRemovableVM { #region FIELDS + private IProjectService _projectService; private Teacher _teacher; #endregion @@ -21,6 +24,7 @@ namespace TimetableDesigner.ViewModels.Models #region PROPERTIES + IUnit IUnitVM.Unit => _teacher; public Teacher Teacher => _teacher; public string Name @@ -55,8 +59,9 @@ namespace TimetableDesigner.ViewModels.Models #region CONSTRUCTORS - public TeacherViewModel(Teacher teacher) + public TeacherVM(Teacher teacher) { + _projectService = ServiceProvider.Instance.GetService(); _teacher = teacher; } @@ -72,6 +77,9 @@ namespace TimetableDesigner.ViewModels.Models { Teacher.AvailabilityHours.Add(day, new TimetableSpanCollection()); NotifyPropertyChanged(nameof(AvailabilityHours)); + + //REFRESH: Errors + _projectService.RefreshErrors(); } } @@ -79,6 +87,9 @@ namespace TimetableDesigner.ViewModels.Models { Teacher.AvailabilityHours.Remove(day); NotifyPropertyChanged(nameof(AvailabilityHours)); + + //REFRESH: Errors + _projectService.RefreshErrors(); } public void AddHours(TimetableDay day, TimetableSpan hours) @@ -87,6 +98,9 @@ namespace TimetableDesigner.ViewModels.Models { Teacher.AvailabilityHours[day].Add(hours); NotifyPropertyChanged(nameof(AvailabilityHours)); + + //REFRESH: Errors + _projectService.RefreshErrors(); } } @@ -96,6 +110,9 @@ namespace TimetableDesigner.ViewModels.Models { Teacher.AvailabilityHours[day].Remove(hours); NotifyPropertyChanged(nameof(AvailabilityHours)); + + //REFRESH: Errors + _projectService.RefreshErrors(); } } diff --git a/TimetableDesigner/ViewModels/Models/TimetableTemplateViewModel.cs b/TimetableDesigner/ViewModels/Models/TimetableTemplateVM.cs similarity index 79% rename from TimetableDesigner/ViewModels/Models/TimetableTemplateViewModel.cs rename to TimetableDesigner/ViewModels/Models/TimetableTemplateVM.cs index 4e14e68..e82f4c2 100644 --- a/TimetableDesigner/ViewModels/Models/TimetableTemplateViewModel.cs +++ b/TimetableDesigner/ViewModels/Models/TimetableTemplateVM.cs @@ -5,10 +5,11 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using TimetableDesigner.Core; +using TimetableDesigner.Customs; namespace TimetableDesigner.ViewModels.Models { - public class TimetableTemplateViewModel : BaseModelViewModel + public class TimetableTemplateVM : ObservableObject, IModelVM { #region FIELDS @@ -31,7 +32,7 @@ namespace TimetableDesigner.ViewModels.Models #region CONSTRUCTORS - public TimetableTemplateViewModel(TimetableTemplate timetableTemplate) + public TimetableTemplateVM(TimetableTemplate timetableTemplate) { _timetableTemplate = timetableTemplate; } @@ -44,25 +45,25 @@ namespace TimetableDesigner.ViewModels.Models public void AddDay(TimetableDay day) { - _timetableTemplate.AddDay(day); + _timetableTemplate.Days.Add(day); NotifyPropertyChanged(nameof(Days)); } public void RemoveDay(TimetableDay day) { - _timetableTemplate.RemoveDay(day); + _timetableTemplate.Days.Remove(day); NotifyPropertyChanged(nameof(Days)); } public void AddSlot(TimetableSpan slot) { - _timetableTemplate.AddSlot(slot); + _timetableTemplate.Slots.Add(slot); NotifyPropertyChanged(nameof(Slots)); } public void RemoveSlot(TimetableSpan slot) { - _timetableTemplate.RemoveSlot(slot); + _timetableTemplate.Slots.Remove(slot); NotifyPropertyChanged(nameof(Slots)); } diff --git a/TimetableDesigner/ViewModels/Views/ClassroomEditViewModel.cs b/TimetableDesigner/ViewModels/Views/ClassroomEditorViewVM.cs similarity index 79% rename from TimetableDesigner/ViewModels/Views/ClassroomEditViewModel.cs rename to TimetableDesigner/ViewModels/Views/ClassroomEditorViewVM.cs index 9e761ef..670052a 100644 --- a/TimetableDesigner/ViewModels/Views/ClassroomEditViewModel.cs +++ b/TimetableDesigner/ViewModels/Views/ClassroomEditorViewVM.cs @@ -9,14 +9,15 @@ using TimetableDesigner.Properties; using TimetableDesigner.Services; using TimetableDesigner.Services.TabNavigation; using TimetableDesigner.ViewModels.Models; +using TimetableDesigner.ViewModels.Models.Base; namespace TimetableDesigner.ViewModels.Views { - public class ClassroomEditViewModel : BaseViewViewModel + public class ClassroomEditorViewVM : ObservableObject, IViewVM, IUnitEditorViewVM { #region FIELDS - private ClassroomViewModel _classroom; + private ClassroomVM _classroom; #endregion @@ -24,7 +25,8 @@ namespace TimetableDesigner.ViewModels.Views #region PROPERTIES - public ClassroomViewModel Classroom + IUnitVM IUnitEditorViewVM.Unit => Classroom; + public ClassroomVM Classroom { get => _classroom; set @@ -62,10 +64,10 @@ namespace TimetableDesigner.ViewModels.Views #region CONSTRUCTORS - public ClassroomEditViewModel() : this(new ClassroomViewModel(new Classroom())) + public ClassroomEditorViewVM() : this(new ClassroomVM(new Classroom())) { } - public ClassroomEditViewModel(ClassroomViewModel classroom) + public ClassroomEditorViewVM(ClassroomVM classroom) { _classroom = classroom; } diff --git a/TimetableDesigner/ViewModels/Views/GroupEditViewModel.cs b/TimetableDesigner/ViewModels/Views/GroupEditorViewVM.cs similarity index 80% rename from TimetableDesigner/ViewModels/Views/GroupEditViewModel.cs rename to TimetableDesigner/ViewModels/Views/GroupEditorViewVM.cs index 16e06f0..cd2dba6 100644 --- a/TimetableDesigner/ViewModels/Views/GroupEditViewModel.cs +++ b/TimetableDesigner/ViewModels/Views/GroupEditorViewVM.cs @@ -14,17 +14,18 @@ using System.Windows.Input; using TimetableDesigner.Commands; using System.Diagnostics; using TimetableDesigner.Services.MessageBox; +using TimetableDesigner.ViewModels.Models.Base; namespace TimetableDesigner.ViewModels.Views { - public class GroupEditViewModel : BaseViewViewModel + public class GroupEditorViewVM : ObservableObject, IViewVM, IUnitEditorViewVM { #region FIELDS private IProjectService _projectService; private IMessageBoxService _messageBoxService; - private GroupViewModel _group; + private GroupVM _group; private string _newSubgroupName; @@ -34,7 +35,8 @@ namespace TimetableDesigner.ViewModels.Views #region PROPERTIES - public GroupViewModel Group + IUnitVM IUnitEditorViewVM.Unit => Group; + public GroupVM Group { get => _group; set @@ -66,7 +68,7 @@ namespace TimetableDesigner.ViewModels.Views } } - public ObservableDictionary Subgroups => new ObservableDictionary(_projectService.ProjectViewModel.Subgroups.ToDictionary(sg => sg, Group.AssignedSubgroups.Contains)); + public ObservableDictionary Subgroups => new ObservableDictionary(_projectService.ProjectViewModel.Subgroups.ToDictionary(sg => sg, Group.AssignedSubgroups.Contains)); public string NewSubgroupName { @@ -91,10 +93,10 @@ namespace TimetableDesigner.ViewModels.Views #region CONSTRUCTORS - public GroupEditViewModel() : this(new GroupViewModel(new Group())) + public GroupEditorViewVM() : this(new GroupVM(new Group())) { } - public GroupEditViewModel(GroupViewModel group) + public GroupEditorViewVM(GroupVM group) { _projectService = ServiceProvider.Instance.GetService(); _messageBoxService = ServiceProvider.Instance.GetService(); @@ -103,8 +105,8 @@ namespace TimetableDesigner.ViewModels.Views _newSubgroupName = string.Empty; AddSubgroupCommand = new RelayCommand(args => AddSubgroup()); - EditSubgroupAssignmentCommand = new RelayCommand(EditSubgroupAssignment); - DeleteSubgroupCommand = new RelayCommand(DeleteSubgroup); + EditSubgroupAssignmentCommand = new RelayCommand(EditSubgroupAssignment); + DeleteSubgroupCommand = new RelayCommand(DeleteSubgroup); } #endregion @@ -119,7 +121,7 @@ namespace TimetableDesigner.ViewModels.Views { Name = NewSubgroupName }; - SubgroupViewModel subgroupViewModel = new SubgroupViewModel(subgroup); + SubgroupVM subgroupViewModel = new SubgroupVM(subgroup); _projectService.ProjectViewModel.Subgroups.Add(subgroupViewModel); Group.AddSubgroup(subgroupViewModel); @@ -128,7 +130,7 @@ namespace TimetableDesigner.ViewModels.Views NewSubgroupName = string.Empty; } - private void EditSubgroupAssignment(SubgroupViewModel subgroup) + private void EditSubgroupAssignment(SubgroupVM subgroup) { bool assigned = Subgroups[subgroup]; if (assigned) @@ -142,12 +144,12 @@ namespace TimetableDesigner.ViewModels.Views NotifyPropertyChanged(nameof(Subgroups)); } - private void DeleteSubgroup(SubgroupViewModel subgroup) + private void DeleteSubgroup(SubgroupVM subgroup) { MessageBoxQuestionResult result = _messageBoxService.ShowQuestion(Resources.GroupEdit_Message_SubgroupDelete, true); if (result == MessageBoxQuestionResult.Yes) { - foreach (GroupViewModel group in _projectService.ProjectViewModel.Groups) + foreach (GroupVM group in _projectService.ProjectViewModel.Groups) { group.RemoveSubgroup(subgroup); } diff --git a/TimetableDesigner/ViewModels/Views/MainViewModel.cs b/TimetableDesigner/ViewModels/Views/MainViewModel.cs deleted file mode 100644 index a443db7..0000000 --- a/TimetableDesigner/ViewModels/Views/MainViewModel.cs +++ /dev/null @@ -1,271 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Input; -using TimetableDesigner.Commands; -using System.Windows.Navigation; -using TimetableDesigner.Core; -using System.Windows; -using TimetableDesigner.Properties; -using System.Reflection; -using TimetableDesigner.ViewModels.Models; -using System.Windows.Data; -using TimetableDesigner.Services.MessageBox; -using System.ComponentModel.Design; -using TimetableDesigner.Services; -using TimetableDesigner.Services.TabNavigation; -using TimetableDesigner.Services.Project; -using System.Drawing; - -namespace TimetableDesigner.ViewModels.Views -{ - class MainViewModel : BaseViewViewModel - { - #region FIELDS - - private IMessageBoxService _messageBoxService; - private ITabNavigationService _tabNavigationService; - private IProjectService _projectService; - - #endregion - - - - #region PROPERTIES - - // Observable services - public IProjectService ProjectService => _projectService; - public ITabNavigationService TabNavigationService => _tabNavigationService; - - // Tabs - public ObservableCollection Tabs => _tabNavigationService.Tabs; - public TabItem SelectedTab => _tabNavigationService.SelectedTab; - - // Commands - public ICommand NewProjectCommand { get; set; } - public ICommand OpenProjectCommand { get; set; } - public ICommand ProjectSettingsCommand { get; set; } - public ICommand NewClassroomCommand { get; set; } - public ICommand EditClassroomCommand { get; set; } - public ICommand RemoveClassroomCommand { get; set; } - public ICommand NewTeacherCommand { get; set; } - public ICommand EditTeacherCommand { get; set; } - public ICommand RemoveTeacherCommand { get; set; } - public ICommand NewGroupCommand { get; set; } - public ICommand EditGroupCommand { get; set; } - public ICommand RemoveGroupCommand { get; set; } - public ICommand RemoveSubgroupCommand { get; set; } - - // Others - public string Version { get; set; } - - #endregion - - - - #region CONSTRUCTORS - - public MainViewModel() - { - _messageBoxService = ServiceProvider.Instance.GetService(); - _tabNavigationService = ServiceProvider.Instance.GetService(); - _projectService = ServiceProvider.Instance.GetService(); - - NewProjectCommand = new RelayCommand(param => NewProject()); - OpenProjectCommand = new RelayCommand(param => OpenProject()); - ProjectSettingsCommand = new RelayCommand(param => ProjectSettings()); - NewClassroomCommand = new RelayCommand(param => NewClassroom()); - EditClassroomCommand = new RelayCommand(EditClassroom); - RemoveClassroomCommand = new RelayCommand(DeleteClassroom); - NewTeacherCommand = new RelayCommand(param => NewTeacher()); - EditTeacherCommand = new RelayCommand(EditTeacher); - RemoveTeacherCommand = new RelayCommand(DeleteTeacher); - NewGroupCommand = new RelayCommand(param => NewGroup()); - EditGroupCommand = new RelayCommand(EditGroup); - RemoveGroupCommand = new RelayCommand(DeleteGroup); - RemoveSubgroupCommand = new RelayCommand(DeleteSubgroup); - - Version = Assembly.GetExecutingAssembly().GetName().Version.ToString(); - - TabItem welcomeTab = new TabItem() - { - Title = Resources.Tabs_Welcome, - ViewModel = new WelcomeViewModel() - }; - _tabNavigationService.AddAndActivate(welcomeTab); - } - - #endregion - - - - #region PRIVATE METHODS - - private void OpenProject() - { - } - - private void NewProject() - { - if (ProjectService.ProjectViewModel is not null) - { - MessageBoxQuestionResult result = _messageBoxService.ShowQuestion(Resources.Main_Message_SaveCurrentProject); - - switch (result) - { - case MessageBoxQuestionResult.Yes: break; - case MessageBoxQuestionResult.No: break; - default: return; - } - } - _tabNavigationService.CloseAll(); - _projectService.New(); - ProjectSettings(); - } - - private void ProjectSettings() - { - if (ProjectService.ProjectViewModel is not null) - { - TabItem projectSettingsTab = new TabItem() - { - Title = Resources.Tabs_ProjectSettings, - IsClosable = true, - ViewModel = new ProjectSettingsViewModel() - }; - _tabNavigationService.AddAndActivate(projectSettingsTab); - } - } - - private void NewClassroom() - { - if (ProjectService.ProjectViewModel is not null) - { - Classroom classroom = new Classroom() - { - Name = Resources.Global_DefaultClassroomName - }; - ClassroomViewModel classroomVM = new ClassroomViewModel(classroom); - ProjectService.ProjectViewModel.Classrooms.Add(classroomVM); - EditClassroom(classroomVM); - } - } - - private void EditClassroom(ClassroomViewModel classroomViewModel) - { - if (ProjectService.ProjectViewModel is not null) - { - TabItem classroomEditTab = new TabItem() - { - Title = $"{Resources.Tabs_ClassroomEdit}: {classroomViewModel.Name}", - IsClosable = true, - ViewModel = new ClassroomEditViewModel(classroomViewModel) - }; - _tabNavigationService.AddAndActivate(classroomEditTab); - } - } - - private void DeleteClassroom(ClassroomViewModel classroomViewModel) - { - if (ProjectService.ProjectViewModel is not null) - { - ProjectService.ProjectViewModel.Classrooms.Remove(classroomViewModel); - } - } - - private void NewTeacher() - { - if (ProjectService.ProjectViewModel is not null) - { - Teacher teacher = new Teacher() - { - Name = Resources.Global_DefaultTeacherName - }; - TeacherViewModel teacherVM = new TeacherViewModel(teacher); - ProjectService.ProjectViewModel.Teachers.Add(teacherVM); - EditTeacher(teacherVM); - } - } - - private void EditTeacher(TeacherViewModel teacherViewModel) - { - if (ProjectService.ProjectViewModel is not null) - { - TabItem teacherEditTab = new TabItem() - { - Title = $"{Resources.Tabs_TeacherEdit}: {teacherViewModel.Name}", - IsClosable = true, - ViewModel = new TeacherEditViewModel(teacherViewModel) - }; - _tabNavigationService.AddAndActivate(teacherEditTab); - } - } - - private void DeleteTeacher(TeacherViewModel teacherViewModel) - { - if (ProjectService.ProjectViewModel is not null) - { - ProjectService.ProjectViewModel.Teachers.Remove(teacherViewModel); - } - } - - private void NewGroup() - { - if (ProjectService.ProjectViewModel is not null) - { - Group group = new Group() - { - Name = Resources.Global_DefaultGroupName - }; - GroupViewModel groupVM = new GroupViewModel(group); - ProjectService.ProjectViewModel.Groups.Add(groupVM); - EditGroup(groupVM); - } - } - - private void EditGroup(GroupViewModel groupViewModel) - { - if (ProjectService.ProjectViewModel is not null) - { - TabItem groupEditTab = new TabItem() - { - Title = $"{Resources.Tabs_GroupEdit}: {groupViewModel.Name}", - IsClosable = true, - ViewModel = new GroupEditViewModel(groupViewModel) - }; - _tabNavigationService.AddAndActivate(groupEditTab); - } - } - - private void DeleteGroup(GroupViewModel groupViewModel) - { - if (ProjectService.ProjectViewModel is not null) - { - ProjectService.ProjectViewModel.Groups.Remove(groupViewModel); - } - } - - private void DeleteSubgroup(SubgroupViewModel subgroupViewModel) - { - if (ProjectService.ProjectViewModel is not null) - { - MessageBoxQuestionResult result = _messageBoxService.ShowQuestion(Resources.Main_Treeview_Subgroups_Message_Remove, true); - if (result == MessageBoxQuestionResult.Yes) - { - foreach (GroupViewModel group in ProjectService.ProjectViewModel.Groups) - { - group.RemoveSubgroup(subgroupViewModel); - } - _projectService.ProjectViewModel.Subgroups.Remove(subgroupViewModel); - } - } - } - - #endregion - } -} diff --git a/TimetableDesigner/ViewModels/Views/MainWindowVM.cs b/TimetableDesigner/ViewModels/Views/MainWindowVM.cs new file mode 100644 index 0000000..e9fd846 --- /dev/null +++ b/TimetableDesigner/ViewModels/Views/MainWindowVM.cs @@ -0,0 +1,387 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; +using TimetableDesigner.Commands; +using System.Windows.Navigation; +using TimetableDesigner.Core; +using System.Windows; +using TimetableDesigner.Properties; +using System.Reflection; +using TimetableDesigner.ViewModels.Models; +using System.Windows.Data; +using TimetableDesigner.Services.MessageBox; +using System.ComponentModel.Design; +using TimetableDesigner.Services; +using TimetableDesigner.Services.TabNavigation; +using TimetableDesigner.Services.Project; +using System.Drawing; +using TimetableDesigner.ViewModels.Models.Base; +using System.Collections; +using TimetableDesigner.Customs; +using Microsoft.Win32; +using System.Windows.Forms; +using TimetableDesigner.Services.FileDialog; +using TimetableDesigner.Services.Scheduler; +using static TimetableDesigner.ViewModels.Views.TimetableEditorViewVM; + +namespace TimetableDesigner.ViewModels.Views +{ + class MainWindowVM : ObservableObject, IViewVM + { + #region FIELDS + + private IMessageBoxService _messageBoxService; + private IFileDialogService _fileDialogService; + private ITabNavigationService _tabNavigationService; + private IProjectService _projectService; + private ISchedulerService _schedulerService; + + private IDictionary> _projectFileTypes = new Dictionary>() + { + { Resources.Global_ProjectFiletypeDescription, new List() { "ttdp" } } + }; + + #endregion + + + + #region PROPERTIES + + // Observable services + public IProjectService ProjectService => _projectService; + public ITabNavigationService TabNavigationService => _tabNavigationService; + + // Tabs + public ObservableCollection Tabs => _tabNavigationService.Tabs; + public TabItem SelectedTab => _tabNavigationService.SelectedTab; + + // Commands + public ICommand NewProjectCommand { get; set; } + public ICommand OpenProjectCommand { get; set; } + public ICommand OpenRecentProjectCommand { get; set; } + public ICommand SaveProjectCommand { get; set; } + public ICommand SaveAsProjectCommand { get; set; } + public ICommand ProjectSettingsCommand { get; set; } + public ICommand NewClassroomCommand { get; set; } + public ICommand NewTeacherCommand { get; set; } + public ICommand NewGroupCommand { get; set; } + public ICommand EditClassroomCommand { get; set; } + public ICommand EditTeacherCommand { get; set; } + public ICommand EditGroupCommand { get; set; } + public ICommand RemoveClassroomCommand { get; set; } + public ICommand RemoveTeacherCommand { get; set; } + public ICommand RemoveGroupCommand { get; set; } + public ICommand RemoveSubgroupCommand { get; set; } + public ICommand EditTimetableCommand { get; set; } + public ICommand AutoScheduleCommand { get; set; } + + // Others + public string? Version { get; set; } + + #endregion + + + + #region CONSTRUCTORS + + public MainWindowVM() + { + _messageBoxService = ServiceProvider.Instance.GetService(); + _fileDialogService = ServiceProvider.Instance.GetService(); + _tabNavigationService = ServiceProvider.Instance.GetService(); + _projectService = ServiceProvider.Instance.GetService(); + _schedulerService = ServiceProvider.Instance.GetService(); + + NewProjectCommand = new RelayCommand(param => NewProject()); + OpenProjectCommand = new RelayCommand(param => OpenProject()); + OpenRecentProjectCommand = new RelayCommand(OpenProject); + SaveProjectCommand = new RelayCommand(param => SaveProject()); + SaveAsProjectCommand = new RelayCommand(param => SaveAsProject()); + ProjectSettingsCommand = new RelayCommand(param => ProjectSettings()); + NewClassroomCommand = new RelayCommand(param => NewClassroom()); + NewTeacherCommand = new RelayCommand(param => NewTeacher()); + NewGroupCommand = new RelayCommand(param => NewGroup()); + EditClassroomCommand = new RelayCommand(EditClassroom); + EditTeacherCommand = new RelayCommand(EditTeacher); + EditGroupCommand = new RelayCommand(EditGroup); + RemoveClassroomCommand = new RelayCommand(RemoveClassroom); + RemoveTeacherCommand = new RelayCommand(RemoveTeacher); + RemoveGroupCommand = new RelayCommand(RemoveGroup); + RemoveSubgroupCommand = new RelayCommand(RemoveSubgroup); + EditTimetableCommand = new RelayCommand(EditTimetable); + AutoScheduleCommand = new RelayCommand(param => AutoSchedule()); + + Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString(); + } + + #endregion + + + + #region PRIVATE METHODS + + private void NewProject() + { + if (ProjectService.ProjectViewModel is not null) + { + MessageBoxQuestionResult result = _messageBoxService.ShowQuestion(Resources.Main_Message_SaveCurrentProject); + + switch (result) + { + case MessageBoxQuestionResult.Yes: SaveProject(); break; + case MessageBoxQuestionResult.No: break; + default: return; + } + } + + _tabNavigationService.CloseAll(); + _projectService.New(); + ProjectSettings(); + } + + private void OpenProject() + { + string? path = _fileDialogService.OpenFile(_projectFileTypes); + if (path is not null) + { + OpenProject(path); + } + } + + private void OpenProject(string path) + { + if (ProjectService.ProjectViewModel is not null) + { + MessageBoxQuestionResult result = _messageBoxService.ShowQuestion(Resources.Main_Message_SaveCurrentProject); + + switch (result) + { + case MessageBoxQuestionResult.Yes: SaveProject(); break; + case MessageBoxQuestionResult.No: break; + default: return; + } + } + + _projectService.Load(path); + _tabNavigationService.CloseAll(); + } + + private void SaveProject() + { + string? path = _projectService.SavePath; + if (_projectService.SavePath is null) + { + path = _fileDialogService.SaveFile(_projectFileTypes); + } + + if (path is not null) + { + _projectService.Save(path); + } + } + private void SaveAsProject() + { + string? path = _fileDialogService.SaveFile(_projectFileTypes); + if (path is not null) + { + _projectService.Save(path); + } + } + + private void ProjectSettings() + { + if (ProjectService.ProjectViewModel is not null) + { + TabItem projectSettingsTab = new TabItem() + { + Title = Resources.Tabs_ProjectSettings, + IsClosable = true, + ViewModel = new ProjectSettingsViewVM() + }; + _tabNavigationService.AddAndActivate(projectSettingsTab); + } + } + + private void NewClassroom() + { + if (ProjectService.ProjectViewModel is not null) + { + Classroom classroom = new Classroom() + { + Name = Resources.Global_DefaultClassroomName + }; + ClassroomVM classroomVM = new ClassroomVM(classroom); + ProjectService.ProjectViewModel.Classrooms.Add(classroomVM); + EditClassroom(classroomVM); + } + } + private void NewTeacher() + { + if (ProjectService.ProjectViewModel is not null) + { + Teacher teacher = new Teacher() + { + Name = Resources.Global_DefaultTeacherName + }; + TeacherVM teacherVM = new TeacherVM(teacher); + ProjectService.ProjectViewModel.Teachers.Add(teacherVM); + EditTeacher(teacherVM); + } + } + private void NewGroup() + { + if (ProjectService.ProjectViewModel is not null) + { + Group group = new Group() + { + Name = Resources.Global_DefaultGroupName + }; + GroupVM groupVM = new GroupVM(group); + ProjectService.ProjectViewModel.Groups.Add(groupVM); + EditGroup(groupVM); + } + } + + private void EditClassroom(ClassroomVM classroom) => EditUnit(new ClassroomEditorViewVM(classroom), Resources.Tabs_ClassroomEdit); + private void EditTeacher(TeacherVM teacher) => EditUnit(new TeacherEditorViewVM(teacher), Resources.Tabs_TeacherEdit); + private void EditGroup(GroupVM group) => EditUnit(new GroupEditorViewVM(group), Resources.Tabs_GroupEdit); + private void EditUnit(IUnitEditorViewVM edit, string tabNamePrefix) + { + if (ProjectService.ProjectViewModel is not null) + { + TabItem groupEditTab = new TabItem() + { + Title = $"{tabNamePrefix}: {edit.Unit.Name}", + IsClosable = true, + ViewModel = edit + }; + _tabNavigationService.AddAndActivate(groupEditTab); + } + } + + private void RemoveClassroom(ClassroomVM classroom) => RemoveUnit(classroom, ProjectService.ProjectViewModel?.Classrooms, Resources.Main_Treeview_Classrooms_Message_Remove); + private void RemoveTeacher(TeacherVM teacher) => RemoveUnit(teacher, ProjectService.ProjectViewModel?.Teachers, Resources.Main_Treeview_Teachers_Message_Remove); + private void RemoveGroup(GroupVM group) => RemoveUnit(group, ProjectService.ProjectViewModel?.Groups, Resources.Main_Treeview_Groups_Message_Remove); + private void RemoveSubgroup(SubgroupVM subgroup) => RemoveUnit(subgroup, ProjectService.ProjectViewModel?.Subgroups, Resources.Main_Treeview_Subgroups_Message_Remove); + private void RemoveUnit(IRemovableVM unit, IList? collection, string questionMessage) + { + if (collection is not null) + { + MessageBoxQuestionResult result = _messageBoxService.ShowQuestion(questionMessage, true); + if (result == MessageBoxQuestionResult.Yes) + { + collection.Remove(unit); + _tabNavigationService.Close(_tabNavigationService.Tabs.Where(x => x.ViewModel is IUnitEditorViewVM model && model.Unit == unit)); + } + } + } + + private void EditTimetable(IUnitVM classUnit) + { + if (ProjectService.ProjectViewModel is not null) + { + TabItem timetableEditTab = new TabItem() + { + Title = $"{Resources.Tabs_TimetableEdit}: {classUnit.Name}", + IsClosable = true, + ViewModel = new TimetableEditorViewVM(classUnit) + }; + _tabNavigationService.AddAndActivate(timetableEditTab); + } + } + + private void AutoSchedule() + { + IEnumerable unscheduledClasses = _projectService.ProjectViewModel.Classes.Where(x => x.Day is null || x.Slot is null); + if (unscheduledClasses.Any()) + { + List errors = new List(); + int successCount = 0; + foreach (ClassVM @class in unscheduledClasses) + { + (TimetableDay? day, TimetableSpan? slot) = _schedulerService.Schedule(@class); + if (day is not null && slot is not null) + { + @class.Day = day; + @class.Slot = slot; + successCount++; + } + else + { + errors.Add(@class); + } + } + + //REFRESH: All editors & Errors + foreach (TimetableEditorViewVM editor in _tabNavigationService.Tabs.Select(x => x.ViewModel).OfType().Distinct()) + { + editor.RefreshClasses(RefreshMode.Scheduled | RefreshMode.Unscheduled); + } + _projectService.RefreshErrors(); + + StringBuilder sb = new StringBuilder(); + sb.Append($"{Resources.Main_Ribbon_Edit_Timetable_Autoschedule_Message_SuccessfullyScheduled}: {successCount}"); + if (errors.Any()) + { + sb.AppendLine(); + sb.AppendLine(); + sb.Append($"{Resources.Main_Ribbon_Edit_Timetable_Autoschedule_Message_FollowingClassesCouldNotBeScheduled}:"); + sb.AppendLine(); + int deleted = 0; + for (int i = 0; i < errors.Count && i < 5; i++) + { + ClassVM errorClass = errors[i]; + string unit; + string unitName; + if (errorClass.Group is not null) + { + unit = Resources.Main_Ribbon_Edit_Timetable_Autoschedule_Message_UnitGroup; + unitName = errorClass.Group.Name; + } + else if (errorClass.Teacher is not null) + { + unit = Resources.Main_Ribbon_Edit_Timetable_Autoschedule_Message_UnitTeacher; + unitName = errorClass.Teacher.Name; + } + else if (errorClass.Classroom is not null) + { + unit = Resources.Main_Ribbon_Edit_Timetable_Autoschedule_Message_UnitClassroom; + unitName = errorClass.Classroom.Name; + } + else + { + deleted++; + i--; + _projectService.ProjectViewModel.Classes.Remove(errorClass); + continue; + } + sb.Append($"- {errorClass.Name} ({unit}: {unitName})"); + sb.AppendLine(); + } + if (errors.Count - deleted > 5) + { + sb.Append($"+ {errors.Count - deleted - 5} {Resources.Main_Ribbon_Edit_Timetable_Autoschedule_Message_More}"); + } + + _messageBoxService.ShowWarning(sb.ToString()); + } + else + { + _messageBoxService.ShowInformation(sb.ToString()); + } + } + else + { + _messageBoxService.ShowInformation(Resources.Main_Ribbon_Edit_Timetable_Autoschedule_Message_NoUnscheduledClasses); + } + } + + #endregion + } +} diff --git a/TimetableDesigner/ViewModels/Views/ProjectSettingsViewModel.cs b/TimetableDesigner/ViewModels/Views/ProjectSettingsViewVM.cs similarity index 87% rename from TimetableDesigner/ViewModels/Views/ProjectSettingsViewModel.cs rename to TimetableDesigner/ViewModels/Views/ProjectSettingsViewVM.cs index 249154e..146089d 100644 --- a/TimetableDesigner/ViewModels/Views/ProjectSettingsViewModel.cs +++ b/TimetableDesigner/ViewModels/Views/ProjectSettingsViewVM.cs @@ -19,7 +19,7 @@ using TimetableDesigner.ViewModels.Models; namespace TimetableDesigner.ViewModels.Views { - public class ProjectSettingsViewModel : BaseViewViewModel + public class ProjectSettingsViewVM : ObservableObject, IViewVM { #region FIELDS @@ -37,7 +37,7 @@ namespace TimetableDesigner.ViewModels.Views #region PROPERTIES // Project - public ProjectViewModel? Project => _projectService.ProjectViewModel; + public ProjectVM? Project => _projectService.ProjectViewModel; // Fields public string NewDayName @@ -81,7 +81,7 @@ namespace TimetableDesigner.ViewModels.Views #region CONSTRUCTORS - public ProjectSettingsViewModel() + public ProjectSettingsViewVM() { _messageBoxService = ServiceProvider.Instance.GetService(); _projectService = ServiceProvider.Instance.GetService(); @@ -115,7 +115,7 @@ namespace TimetableDesigner.ViewModels.Views { if (Project is not null) { - foreach (TeacherViewModel teacher in Project.Teachers) + foreach (TeacherVM teacher in Project.Teachers) { teacher.RemoveDay(day); } @@ -137,11 +137,19 @@ namespace TimetableDesigner.ViewModels.Views try { + TimetableSpan? lastSlot = Project.TimetableTemplate.Slots.LastOrDefault(); + Project.TimetableTemplate.AddSlot(new TimetableSpan(from, to)); + double offset = 0; + if (lastSlot != null) + { + offset = (from - lastSlot.To).TotalMinutes; + } + double delta = (to - from).TotalMinutes; - DateTime newFrom = NewSlotTo.Value; - DateTime newTo = NewSlotTo.Value.AddMinutes(delta); + DateTime newFrom = NewSlotTo.Value.AddMinutes(offset); + DateTime newTo = NewSlotTo.Value.AddMinutes(offset + delta); NewSlotFrom = newFrom; NewSlotTo = newTo; } diff --git a/TimetableDesigner/ViewModels/Views/TeacherEditViewModel.cs b/TimetableDesigner/ViewModels/Views/TeacherEditorViewVM.cs similarity index 90% rename from TimetableDesigner/ViewModels/Views/TeacherEditViewModel.cs rename to TimetableDesigner/ViewModels/Views/TeacherEditorViewVM.cs index 7711d00..63ad600 100644 --- a/TimetableDesigner/ViewModels/Views/TeacherEditViewModel.cs +++ b/TimetableDesigner/ViewModels/Views/TeacherEditorViewVM.cs @@ -14,17 +14,18 @@ using TimetableDesigner.Services.MessageBox; using TimetableDesigner.Services.Project; using TimetableDesigner.Services.TabNavigation; using TimetableDesigner.ViewModels.Models; +using TimetableDesigner.ViewModels.Models.Base; namespace TimetableDesigner.ViewModels.Views { - public class TeacherEditViewModel : BaseViewViewModel + public class TeacherEditorViewVM : ObservableObject, IViewVM, IUnitEditorViewVM { #region FIELDS private IMessageBoxService _messageBoxService; private IProjectService _projectService; - private TeacherViewModel _teacher; + private TeacherVM _teacher; private TimetableDay _selectedDay; @@ -38,7 +39,8 @@ namespace TimetableDesigner.ViewModels.Views #region PROPERTIES - public TeacherViewModel Teacher + IUnitVM IUnitEditorViewVM.Unit => Teacher; + public TeacherVM Teacher { get => _teacher; set @@ -50,7 +52,7 @@ namespace TimetableDesigner.ViewModels.Views } } } - public TimetableTemplateViewModel? TimetableTemplate => _projectService.ProjectViewModel?.TimetableTemplate; + public TimetableTemplateVM? TimetableTemplate => _projectService.ProjectViewModel?.TimetableTemplate; public string Name { @@ -65,7 +67,7 @@ namespace TimetableDesigner.ViewModels.Views TabItem? tab = ServiceProvider.Instance.GetService().Tabs.Where(tab => tab.ViewModel == this).FirstOrDefault(); if (tab != null) { - tab.Title = $"{Resources.Tabs_ClassroomEdit}: {_teacher.Name}"; + tab.Title = $"{Resources.Tabs_TeacherEdit}: {_teacher.Name}"; } } } @@ -130,10 +132,10 @@ namespace TimetableDesigner.ViewModels.Views #region CONSTRUCTORS - public TeacherEditViewModel() : this(new TeacherViewModel(new Teacher())) + public TeacherEditorViewVM() : this(new TeacherVM(new Teacher())) { } - public TeacherEditViewModel(TeacherViewModel teacher) + public TeacherEditorViewVM(TeacherVM teacher) { _messageBoxService = ServiceProvider.Instance.GetService(); _projectService = ServiceProvider.Instance.GetService(); diff --git a/TimetableDesigner/ViewModels/Views/TimetableEditorViewVM.cs b/TimetableDesigner/ViewModels/Views/TimetableEditorViewVM.cs new file mode 100644 index 0000000..1c1abcf --- /dev/null +++ b/TimetableDesigner/ViewModels/Views/TimetableEditorViewVM.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TimetableDesigner.Services; +using TimetableDesigner.Services.Project; +using TimetableDesigner.ViewModels.Models.Base; +using TimetableDesigner.ViewModels.Models; +using TimetableDesigner.Customs; +using System.Collections.ObjectModel; +using System.Windows.Input; +using TimetableDesigner.Commands; +using System.Windows; +using System.Diagnostics; +using TimetableDesigner.Core; +using System.Xml.Linq; +using TimetableDesigner.Services.TabNavigation; +using System.Security.Policy; +using TimetableDesigner.Converters; +using System.Data; +using static TimetableDesigner.ViewModels.Views.TimetableEditorViewVM; +using TimetableDesigner.Services.Scheduler; + +namespace TimetableDesigner.ViewModels.Views +{ + public class TimetableEditorViewVM : ObservableObject, IViewVM + { + #region FIELDS + + private IProjectService _projectService; + private ITabNavigationService _tabNavigationService; + private ISchedulerService _schedulerService; + + private IUnitVM? _unit; + + #endregion + + + + #region PROPERTIES + + public TimetableTemplateVM TimetableTemplate => _projectService.ProjectViewModel.TimetableTemplate; + + public IUnitVM? Unit + { + get => _unit; + set + { + if (value != _unit) + { + _unit = value; + NotifyPropertyChanged(nameof(Unit)); + } + } + } + + public IEnumerable AssignedClasses => _projectService.ProjectViewModel.Classes.Where(c => c.Classroom == Unit || c.Teacher == Unit || c.Group == Unit || (Unit is GroupVM g && c.Group is SubgroupVM s && g.AssignedSubgroups.Contains(s))); + public IEnumerable ScheduledClasses => AssignedClasses.Where(c => c.Slot is not null && c.Day is not null); + public IEnumerable UnscheduledClasses => AssignedClasses.Where(c => c.Slot is null || c.Day is null); + + public ICommand AddClassCommand { get; set; } + public ICommand AutoScheduleCommand { get; set; } + public ICommand RemoveClassCommand { get; set; } + public ICommand CloneClassCommand { get; set; } + public ICommand RefreshScheduledClassesCommand { get; set; } + public ICommand RefreshUnscheduledClassesCommand { get; set; } + public ICommand RefreshAllClassesCommand { get; set; } + + #endregion + + + + #region CONSTRUCTORS + + public TimetableEditorViewVM() : this(null) + { } + + public TimetableEditorViewVM(IUnitVM? unit) + { + _projectService = ServiceProvider.Instance.GetService(); + _tabNavigationService = ServiceProvider.Instance.GetService(); + _schedulerService = ServiceProvider.Instance.GetService(); + + _unit = unit; + + AddClassCommand = new RelayCommand(arg => AddClass()); + AutoScheduleCommand = new RelayCommand(arg => AutoSchedule()); + RemoveClassCommand = new RelayCommand(RemoveClass); + CloneClassCommand = new RelayCommand(CloneClass); + RefreshScheduledClassesCommand = new RelayCommand(arg => RefreshClassesAndErrors(RefreshMode.Scheduled)); + RefreshUnscheduledClassesCommand = new RelayCommand(arg => RefreshClassesAndErrors(RefreshMode.Unscheduled)); + RefreshAllClassesCommand = new RelayCommand(arg => RefreshClassesAndErrors(RefreshMode.Scheduled | RefreshMode.Unscheduled)); + } + + #endregion + + + + #region PUBLIC METHODS + + public void RefreshClasses(RefreshMode mode) + { + if ((mode & RefreshMode.Scheduled) == RefreshMode.Scheduled) + NotifyPropertyChanged(nameof(ScheduledClasses)); + if ((mode & RefreshMode.Unscheduled) == RefreshMode.Unscheduled) + NotifyPropertyChanged(nameof(UnscheduledClasses)); + } + + #endregion + + + + #region PRIVATE METHODS + + private void AddClass() + { + Class @class = new Class() + { + Name = "New class", + Teacher = _unit is TeacherVM t ? t.Teacher : null, + Classroom = _unit is ClassroomVM c ? c.Classroom : null, + Group = _unit is BaseGroupVM g ? g.BaseGroup : null, + }; + ClassVM classVM = new ClassVM(@class); + _projectService.ProjectViewModel.Classes.Add(classVM); + + //REFRESH: Unassigned classes of this unit & Errors + RefreshClasses(RefreshMode.Unscheduled); + _projectService.RefreshErrors(); + } + + private void AutoSchedule() + { + foreach (ClassVM @class in UnscheduledClasses) + { + (TimetableDay? day, TimetableSpan? slot) = _schedulerService.Schedule(@class); + @class.Day = day; + @class.Slot = slot; + } + + //REFRESH: Scheduled & Errors + foreach (TimetableEditorViewVM editors in _tabNavigationService.Tabs.Select(x => x.ViewModel).OfType().Distinct()) + { + editors.RefreshClasses(RefreshMode.Scheduled | RefreshMode.Unscheduled); + } + _projectService.RefreshErrors(); + } + + private void RemoveClass(ClassVM @class) + { + Debug.WriteLine(@class.Name); + List units = new List(); + if (@class.Teacher is not null) + { + units.Add(@class.Teacher); + } + if (@class.Classroom is not null) + { + units.Add(@class.Classroom); + } + if (@class.Group is not null) + { + units.Add(@class.Group); + if (@class.Group is SubgroupVM sg) + { + units.AddRange(_projectService.ProjectViewModel.Groups.Where(x => x.AssignedSubgroups.Contains(sg))); + } + } + + RefreshMode refreshMode = RefreshMode.Unscheduled; + if (@class.Day is not null && @class.Slot is not null) + { + refreshMode = RefreshMode.Scheduled; + } + + _projectService.ProjectViewModel.Classes.Remove(@class); + + //REFRESH: Assigned classes of units assigned to class & Errors + foreach (TimetableEditorViewVM editors in _tabNavigationService.Tabs.Select(x => x.ViewModel).OfType().Where(x => units.Contains(x.Unit)).Distinct()) + { + editors.RefreshClasses(refreshMode); + } + _projectService.RefreshErrors(); + } + + private void CloneClass(ClassVM @class) + { + List units = new List(); + if (@class.Teacher is not null) + { + units.Add(@class.Teacher); + } + if (@class.Classroom is not null) + { + units.Add(@class.Classroom); + } + if (@class.Group is not null) + { + units.Add(@class.Group); + if (@class.Group is SubgroupVM sg) + { + units.AddRange(_projectService.ProjectViewModel.Groups.Where(x => x.AssignedSubgroups.Contains(sg))); + } + } + + RefreshMode refreshMode = RefreshMode.Unscheduled; + if (@class.Day is not null && @class.Slot is not null) + { + refreshMode = RefreshMode.Scheduled; + } + + Class newClass = new Class() + { + Name = @class.Name, + Teacher = @class.Teacher?.Teacher, + Classroom = @class.Classroom?.Classroom, + Group = @class.Group?.BaseGroup, + Day = @class.Day, + Slot = @class.Slot, + Color = @class.Color, + }; + ClassVM classVM = new ClassVM(newClass); + _projectService.ProjectViewModel.Classes.Add(classVM); + + //REFRESH: Assigned classes of units assigned to class & Errors + foreach (TimetableEditorViewVM editors in _tabNavigationService.Tabs.Select(x => x.ViewModel).OfType().Where(x => units.Contains(x.Unit)).Distinct()) + { + editors.RefreshClasses(refreshMode); + } + _projectService.RefreshErrors(); + } + + private void RefreshClassesAndErrors(RefreshMode mode) + { + RefreshClasses(mode); + _projectService.RefreshErrors(); + } + + #endregion + + + + #region ENUMS + + [Flags] + public enum RefreshMode + { + Scheduled, + Unscheduled + } + + #endregion + } +} diff --git a/TimetableDesigner/ViewModels/Views/WelcomeViewModel.cs b/TimetableDesigner/ViewModels/Views/WelcomeViewVM.cs similarity index 75% rename from TimetableDesigner/ViewModels/Views/WelcomeViewModel.cs rename to TimetableDesigner/ViewModels/Views/WelcomeViewVM.cs index 809b737..cf55536 100644 --- a/TimetableDesigner/ViewModels/Views/WelcomeViewModel.cs +++ b/TimetableDesigner/ViewModels/Views/WelcomeViewVM.cs @@ -7,11 +7,11 @@ using TimetableDesigner.Customs; namespace TimetableDesigner.ViewModels.Views { - public class WelcomeViewModel : BaseViewViewModel + public class WelcomeViewVM : ObservableObject, IViewVM { #region CONSTRUCTORS - public WelcomeViewModel() + public WelcomeViewVM() { } #endregion diff --git a/TimetableDesigner/Views/ClassroomEditView.xaml b/TimetableDesigner/Views/ClassroomEditorView.xaml similarity index 96% rename from TimetableDesigner/Views/ClassroomEditView.xaml rename to TimetableDesigner/Views/ClassroomEditorView.xaml index aee07bc..87f3359 100644 --- a/TimetableDesigner/Views/ClassroomEditView.xaml +++ b/TimetableDesigner/Views/ClassroomEditorView.xaml @@ -1,4 +1,4 @@ - - + diff --git a/TimetableDesigner/Views/ClassroomEditView.xaml.cs b/TimetableDesigner/Views/ClassroomEditorView.xaml.cs similarity index 83% rename from TimetableDesigner/Views/ClassroomEditView.xaml.cs rename to TimetableDesigner/Views/ClassroomEditorView.xaml.cs index 82bbe7f..535d4dd 100644 --- a/TimetableDesigner/Views/ClassroomEditView.xaml.cs +++ b/TimetableDesigner/Views/ClassroomEditorView.xaml.cs @@ -15,9 +15,9 @@ using System.Windows.Shapes; namespace TimetableDesigner.Views { - public partial class ClassroomEditView : UserControl + public partial class ClassroomEditorView : UserControl { - public ClassroomEditView() + public ClassroomEditorView() { InitializeComponent(); } diff --git a/TimetableDesigner/Views/GroupEditView.xaml b/TimetableDesigner/Views/GroupEditorView.xaml similarity index 97% rename from TimetableDesigner/Views/GroupEditView.xaml rename to TimetableDesigner/Views/GroupEditorView.xaml index 9f91fec..f2baabd 100644 --- a/TimetableDesigner/Views/GroupEditView.xaml +++ b/TimetableDesigner/Views/GroupEditorView.xaml @@ -1,4 +1,4 @@ - - + diff --git a/TimetableDesigner/Views/GroupEditView.xaml.cs b/TimetableDesigner/Views/GroupEditorView.xaml.cs similarity index 84% rename from TimetableDesigner/Views/GroupEditView.xaml.cs rename to TimetableDesigner/Views/GroupEditorView.xaml.cs index 6cc425e..a850677 100644 --- a/TimetableDesigner/Views/GroupEditView.xaml.cs +++ b/TimetableDesigner/Views/GroupEditorView.xaml.cs @@ -15,9 +15,9 @@ using System.Windows.Shapes; namespace TimetableDesigner.Views { - public partial class GroupEditView : UserControl + public partial class GroupEditorView : UserControl { - public GroupEditView() + public GroupEditorView() { InitializeComponent(); } diff --git a/TimetableDesigner/Views/MainWindow.xaml b/TimetableDesigner/Views/MainWindow.xaml index 824ae8a..f27c78b 100644 --- a/TimetableDesigner/Views/MainWindow.xaml +++ b/TimetableDesigner/Views/MainWindow.xaml @@ -13,12 +13,14 @@ - + + + @@ -27,14 +29,30 @@ - - - - + + + + + + + + + + + + + + + - - + + @@ -54,6 +72,13 @@ + + + + + @@ -61,125 +86,134 @@ - - - - - - - - - - - + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + @@ -208,5 +242,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TimetableDesigner/Views/ProjectSettingsView.xaml b/TimetableDesigner/Views/ProjectSettingsView.xaml index 23c0356..f9196fc 100644 --- a/TimetableDesigner/Views/ProjectSettingsView.xaml +++ b/TimetableDesigner/Views/ProjectSettingsView.xaml @@ -9,42 +9,46 @@ mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + - - - + + + diff --git a/TimetableDesigner/Views/TeacherEditView.xaml b/TimetableDesigner/Views/TeacherEditView.xaml deleted file mode 100644 index 8cc4bdf..0000000 --- a/TimetableDesigner/Views/TeacherEditView.xaml +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TimetableDesigner/Views/TeacherEditorView.xaml b/TimetableDesigner/Views/TeacherEditorView.xaml new file mode 100644 index 0000000..8c81168 --- /dev/null +++ b/TimetableDesigner/Views/TeacherEditorView.xaml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TimetableDesigner/Views/TeacherEditView.xaml.cs b/TimetableDesigner/Views/TeacherEditorView.xaml.cs similarity index 84% rename from TimetableDesigner/Views/TeacherEditView.xaml.cs rename to TimetableDesigner/Views/TeacherEditorView.xaml.cs index b923b69..fd5ad40 100644 --- a/TimetableDesigner/Views/TeacherEditView.xaml.cs +++ b/TimetableDesigner/Views/TeacherEditorView.xaml.cs @@ -15,9 +15,9 @@ using System.Windows.Shapes; namespace TimetableDesigner.Views { - public partial class TeacherEditView : UserControl + public partial class TeacherEditorView : UserControl { - public TeacherEditView() + public TeacherEditorView() { InitializeComponent(); } diff --git a/TimetableDesigner/Views/TimetableEditorView.xaml b/TimetableDesigner/Views/TimetableEditorView.xaml new file mode 100644 index 0000000..bed766e --- /dev/null +++ b/TimetableDesigner/Views/TimetableEditorView.xaml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TimetableDesigner/Views/TimetableEditorView.xaml.cs b/TimetableDesigner/Views/TimetableEditorView.xaml.cs new file mode 100644 index 0000000..99fae86 --- /dev/null +++ b/TimetableDesigner/Views/TimetableEditorView.xaml.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Xml.Linq; +using TimetableDesigner.Controls; +using TimetableDesigner.Services.Project; +using TimetableDesigner.Services.TabNavigation; +using TimetableDesigner.ViewModels.Models; +using TimetableDesigner.ViewModels.Views; + +namespace TimetableDesigner.Views +{ + public partial class TimetableEditorView : UserControl + { + #region FIELDS + + private ITabNavigationService _tabNavigationService; + private IProjectService _projectService; + + #endregion + + + + #region CONSTRUCTORS + + public TimetableEditorView() + { + _tabNavigationService = ServiceProvider.Instance.GetService(); + _projectService = ServiceProvider.Instance.GetService(); + InitializeComponent(); + } + + #endregion + + private void MouseMoveEvent(object sender, MouseEventArgs e) + { + if (e.LeftButton == MouseButtonState.Pressed && sender is ClassControl control && !control.EditButton.IsChecked.Value) + { + DragDrop.DoDragDrop((DependencyObject)sender, new DataObject(DataFormats.Serializable, sender), DragDropEffects.Move); + } + } + + private void ItemsControl_Drop(object sender, DragEventArgs e) + { + object item = e.Data.GetData(DataFormats.Serializable); + if (item is UIElement element) + { + DynamicGrid.SetRow(element, -1); + DynamicGrid.SetColumn(element, -1); + } + } + } +} diff --git a/TimetableDesigner/Views/WelcomeView.xaml b/TimetableDesigner/Views/WelcomeView.xaml index 484390f..2b51a1d 100644 --- a/TimetableDesigner/Views/WelcomeView.xaml +++ b/TimetableDesigner/Views/WelcomeView.xaml @@ -4,13 +4,11 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:vm="clr-namespace:TimetableDesigner.ViewModels.Views" - xmlns:c="clr-namespace:TimetableDesigner.Controls" mc:Ignorable="d"> - +