Teacher editor

This commit is contained in:
2023-03-12 17:52:17 +01:00
Unverified
parent 95364c8a31
commit 3cc4ea5b4b
28 changed files with 947 additions and 93 deletions

View File

@@ -12,6 +12,20 @@ namespace TimetableDesigner.Core
public string Name { get; set; } public string Name { get; set; }
public string Description { get; set; } public string Description { get; set; }
public IDictionary<TimetableDay, TimetableSpanCollection> AvailabilityHours { get; set; }
#endregion
#region CONSTRUCTORS
public Teacher()
{
Name = string.Empty;
Description = string.Empty;
AvailabilityHours = new Dictionary<TimetableDay, TimetableSpanCollection>();
}
#endregion #endregion
} }

View File

@@ -6,7 +6,8 @@ using System.Threading.Tasks;
namespace TimetableDesigner.Core namespace TimetableDesigner.Core
{ {
public struct TimetableDay [Serializable]
public class TimetableDay
{ {
#region PROPERTIES #region PROPERTIES

View File

@@ -3,7 +3,7 @@
namespace TimetableDesigner.Core namespace TimetableDesigner.Core
{ {
[Serializable] [Serializable]
public struct TimetableSlot public class TimetableSpan
{ {
#region PROPERTIES #region PROPERTIES
@@ -16,7 +16,7 @@ namespace TimetableDesigner.Core
#region CONSTRUCTORS #region CONSTRUCTORS
public TimetableSlot(TimeOnly from, TimeOnly to) public TimetableSpan(TimeOnly from, TimeOnly to)
{ {
if (to <= from) if (to <= from)
{ {
@@ -33,29 +33,29 @@ namespace TimetableDesigner.Core
#region PUBLIC METHODS #region PUBLIC METHODS
internal TimetableSlotsCollision CheckCollision(TimetableSlot slot) internal TimetableSpansCollision CheckCollision(TimetableSpan slot)
{ {
if (slot.To <= this.From) if (slot.To <= this.From)
{ {
return TimetableSlotsCollision.CheckedSlotBefore; return TimetableSpansCollision.CheckedSlotBefore;
} }
else if (this.To <= slot.From) else if (this.To <= slot.From)
{ {
return TimetableSlotsCollision.CheckedSlotAfter; return TimetableSpansCollision.CheckedSlotAfter;
} }
else else
{ {
if (this.From < slot.From && slot.To < this.To) if (this.From < slot.From && slot.To < this.To)
{ {
return TimetableSlotsCollision.CheckedSlotIn; return TimetableSpansCollision.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 TimetableSlotsCollision.CheckedSlotFromIn; return TimetableSpansCollision.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 TimetableSlotsCollision.CheckedSlotToIn; return TimetableSpansCollision.CheckedSlotToIn;
} }
else else
{ {
@@ -64,7 +64,7 @@ namespace TimetableDesigner.Core
} }
} }
public override bool Equals(object? obj) => obj is TimetableSlot slot && From == slot.From && To == slot.To; 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 int GetHashCode() => HashCode.Combine(From, To);

View File

@@ -0,0 +1,75 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Core
{
public class TimetableSpanCollection : ICollection<TimetableSpan>
{
#region FIELDS
private IList<TimetableSpan> _list;
#endregion
#region PROPERTIES
public int Count => _list.Count;
public bool IsReadOnly => _list.IsReadOnly;
#endregion
#region CONSTRUCTORS
public TimetableSpanCollection()
{
_list = new List<TimetableSpan>();
}
#endregion
#region PUBLIC METHODS
public void Add(TimetableSpan item)
{
int i = 0;
if (Count > 0)
{
bool done = false;
while (i < Count && !done)
{
switch (item.CheckCollision(_list.ElementAt(i)))
{
case TimetableSpansCollision.CheckedSlotBefore: i++; break;
case TimetableSpansCollision.CheckedSlotAfter: done ^= true; break;
default: throw new ArgumentException("Slot collide with another slot");
}
}
}
_list.Insert(i, item);
}
public void Clear() => _list.Clear();
public bool Contains(TimetableSpan item) => _list.Contains(item);
public void CopyTo(TimetableSpan[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);
public IEnumerator<TimetableSpan> GetEnumerator() => _list.GetEnumerator();
public bool Remove(TimetableSpan item) => _list.Remove(item);
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_list).GetEnumerator();
#endregion
}
}

View File

@@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace TimetableDesigner.Core namespace TimetableDesigner.Core
{ {
internal enum TimetableSlotsCollision internal enum TimetableSpansCollision
{ {
CheckedSlotBefore, CheckedSlotBefore,
CheckedSlotAfter, CheckedSlotAfter,

View File

@@ -12,7 +12,7 @@ namespace TimetableDesigner.Core
#region FIELDS #region FIELDS
private List<TimetableDay> _days; private List<TimetableDay> _days;
private List<TimetableSlot> _slots; private TimetableSpanCollection _slots;
#endregion #endregion
@@ -21,7 +21,7 @@ namespace TimetableDesigner.Core
#region PROPERTIES #region PROPERTIES
public IEnumerable<TimetableDay> Days => _days; public IEnumerable<TimetableDay> Days => _days;
public IEnumerable<TimetableSlot> Slots => _slots; public IEnumerable<TimetableSpan> Slots => _slots;
#endregion #endregion
@@ -32,7 +32,7 @@ namespace TimetableDesigner.Core
public TimetableTemplate() public TimetableTemplate()
{ {
_days = new List<TimetableDay>(); _days = new List<TimetableDay>();
_slots = new List<TimetableSlot>(); _slots = new TimetableSpanCollection();
} }
#endregion #endregion
@@ -41,39 +41,13 @@ namespace TimetableDesigner.Core
#region PUBLIC METHODS #region PUBLIC METHODS
public void AddDay(TimetableDay name) public void AddDay(TimetableDay name) => _days.Add(name);
{
_days.Add(name);
}
public bool RemoveDay(TimetableDay day) public bool RemoveDay(TimetableDay day) => _days.Remove(day);
{
return _days.Remove(day);
}
public void AddSlot(TimetableSlot slot) public void AddSlot(TimetableSpan slot) => _slots.Add(slot);
{
int i = 0;
if (_slots.Count > 0)
{
bool done = false;
while (i < _slots.Count && !done)
{
switch (slot.CheckCollision(_slots[i]))
{
case TimetableSlotsCollision.CheckedSlotBefore: i++; break;
case TimetableSlotsCollision.CheckedSlotAfter: done ^= true; break;
default: throw new ArgumentException("Slot collide with another slot");
}
}
}
_slots.Insert(i, slot);
}
public bool RemoveSlot(TimetableSlot slot) public bool RemoveSlot(TimetableSpan slot) => _slots.Remove(slot);
{
return _slots.Remove(slot);
}
#endregion #endregion
} }

View File

@@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Customs.Collections
{
public class ObservableDictionary<TKey, TValue> : ObservableCollection<ObservableKeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue>
{
#region PROPERTIES
public ICollection<TKey> Keys => Items.Select(p => p.Key).ToList();
public ICollection<TValue> Values => Items.Select(p => p.Value).ToList();
public bool IsReadOnly => false;
#endregion
#region INDEXERS
public TValue this[TKey key]
{
get
{
if (!TryGetValue(key, out TValue result))
{
throw new ArgumentException("Key not found");
}
return result;
}
set
{
if (ContainsKey(key))
{
GetKeyValuePairByTheKey(key).Value = value;
}
else
{
Add(key, value);
}
}
}
#endregion
#region CONSTRUCTORS
public ObservableDictionary() : base()
{ }
public ObservableDictionary(IDictionary<TKey, TValue> dictionary) : base()
{
foreach (KeyValuePair<TKey, TValue> pair in dictionary)
{
this.Add(pair);
}
}
#endregion
#region PUBLIC METHODS
public void Add(TKey key, TValue value)
{
if (ContainsKey(key))
{
throw new ArgumentException("The dictionary already contains the key");
}
Add(new ObservableKeyValuePair<TKey, TValue>(key, value));
}
public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);
public bool Contains(KeyValuePair<TKey, TValue> item)
{
ObservableKeyValuePair<TKey, TValue> pair = GetKeyValuePairByTheKey(item.Key);
if (Equals(pair, default(ObservableKeyValuePair<TKey, TValue>)))
{
return false;
}
return Equals(pair.Value, item.Value);
}
public bool ContainsKey(TKey key)
{
ObservableKeyValuePair<TKey, TValue> pair = ((ObservableCollection<ObservableKeyValuePair<TKey, TValue>>)this).FirstOrDefault((i) => Equals(key, i.Key));
return !Equals(default(ObservableKeyValuePair<TKey, TValue>), pair);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public bool Remove(TKey key)
{
List<ObservableKeyValuePair<TKey, TValue>> remove = ((ObservableCollection<ObservableKeyValuePair<TKey, TValue>>)this).Where(pair => Equals(key, pair.Key)).ToList();
foreach (ObservableKeyValuePair<TKey, TValue> pair in remove)
{
Remove(pair);
}
return remove.Count > 0;
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
ObservableKeyValuePair<TKey, TValue> pair = GetKeyValuePairByTheKey(item.Key);
if (Equals(pair, default(ObservableKeyValuePair<TKey, TValue>)))
{
return false;
}
if (!Equals(pair.Value, item.Value))
{
return false;
}
return Remove(pair);
}
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
value = default;
var pair = GetKeyValuePairByTheKey(key);
if (Equals(pair, default(ObservableKeyValuePair<TKey, TValue>)))
{
return false;
}
value = pair.Value;
return true;
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() => ((ObservableCollection<ObservableKeyValuePair<TKey, TValue>>)this).Select(i => new KeyValuePair<TKey, TValue>(i.Key, i.Value)).GetEnumerator();
#endregion
#region PRIVATE METHODS
private ObservableKeyValuePair<TKey, TValue> GetKeyValuePairByTheKey(TKey key) => ((ObservableCollection<ObservableKeyValuePair<TKey, TValue>>)this).FirstOrDefault(i => i.Key.Equals(key));
#endregion
}
}

View File

@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Customs.Collections
{
public class ObservableKeyValuePair<TKey, TValue> : INotifyPropertyChanged
{
#region FIELDS
private TKey _key;
private TValue _value;
#endregion
#region PROPERTIES
public TKey Key
{
get => _key;
set
{
_key = value;
NotifyPropertyChanged(nameof(Key));
}
}
public TValue Value
{
get => _value;
set
{
_value = value;
NotifyPropertyChanged(nameof(Value));
}
}
#endregion
#region CONSTRUCTORS
public ObservableKeyValuePair() : this(default, default)
{ }
public ObservableKeyValuePair(TKey key, TValue value)
{
_key = key;
_value = value;
}
#endregion
#region PRIVATE METHODS
private void NotifyPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
#endregion
#region EVENTS
public event PropertyChangedEventHandler? PropertyChanged;
#endregion
}
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -10,14 +10,14 @@ namespace TimetableDesigner.Tests
[TestMethod] [TestMethod]
public void CreateValidSlotTest() public void CreateValidSlotTest()
{ {
TimetableSlot slot = new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(9, 0)); TimetableSpan slot = new TimetableSpan(new TimeOnly(8, 0), new TimeOnly(9, 0));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))] [ExpectedException(typeof(ArgumentException))]
public void CreateInvalidSlotTest() public void CreateInvalidSlotTest()
{ {
TimetableSlot slot = new TimetableSlot(new TimeOnly(9, 0), new TimeOnly(8, 0)); TimetableSpan slot = new TimetableSpan(new TimeOnly(9, 0), new TimeOnly(8, 0));
Assert.Fail(); Assert.Fail();
} }
@@ -26,7 +26,7 @@ namespace TimetableDesigner.Tests
[ExpectedException(typeof(ArgumentException))] [ExpectedException(typeof(ArgumentException))]
public void CreateSlotWithZeroLengthTest() public void CreateSlotWithZeroLengthTest()
{ {
TimetableSlot slot = new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(8, 0)); TimetableSpan slot = new TimetableSpan(new TimeOnly(8, 0), new TimeOnly(8, 0));
Assert.Fail(); Assert.Fail();
} }
@@ -58,12 +58,12 @@ namespace TimetableDesigner.Tests
{ {
TimetableTemplate model = new TimetableTemplate(); TimetableTemplate model = new TimetableTemplate();
TimetableSlot slot = new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(9, 0)); TimetableSpan slot = new TimetableSpan(new TimeOnly(8, 0), new TimeOnly(9, 0));
model.AddSlot(slot); model.AddSlot(slot);
Assert.AreEqual(1, model.Slots.Count()); Assert.AreEqual(1, model.Slots.Count());
Assert.AreEqual(new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(9, 0)), model.Slots.ToList()[0]); Assert.AreEqual(new TimetableSpan(new TimeOnly(8, 0), new TimeOnly(9, 0)), model.Slots.ToList()[0]);
} }
[TestMethod] [TestMethod]
@@ -71,15 +71,15 @@ namespace TimetableDesigner.Tests
{ {
TimetableTemplate model = new TimetableTemplate(); TimetableTemplate model = new TimetableTemplate();
TimetableSlot slot1 = new TimetableSlot(new TimeOnly(8, 15), new TimeOnly(9, 0)); TimetableSpan slot1 = new TimetableSpan(new TimeOnly(8, 15), new TimeOnly(9, 0));
TimetableSlot slot2 = new TimetableSlot(new TimeOnly(9, 15), new TimeOnly(10, 0)); TimetableSpan slot2 = new TimetableSpan(new TimeOnly(9, 15), new TimeOnly(10, 0));
model.AddSlot(slot1); model.AddSlot(slot1);
model.AddSlot(slot2); model.AddSlot(slot2);
Assert.AreEqual(2, model.Slots.Count()); Assert.AreEqual(2, model.Slots.Count());
Assert.AreEqual(new TimetableSlot(new TimeOnly(8, 15), new TimeOnly(9, 0)), model.Slots.ToList()[0]); Assert.AreEqual(new TimetableSpan(new TimeOnly(8, 15), new TimeOnly(9, 0)), model.Slots.ToList()[0]);
Assert.AreEqual(new TimetableSlot(new TimeOnly(9, 15), new TimeOnly(10, 0)), model.Slots.ToList()[1]); Assert.AreEqual(new TimetableSpan(new TimeOnly(9, 15), new TimeOnly(10, 0)), model.Slots.ToList()[1]);
} }
[TestMethod] [TestMethod]
@@ -87,15 +87,15 @@ namespace TimetableDesigner.Tests
{ {
TimetableTemplate model = new TimetableTemplate(); TimetableTemplate model = new TimetableTemplate();
TimetableSlot slot1 = new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(9, 0)); TimetableSpan slot1 = new TimetableSpan(new TimeOnly(8, 0), new TimeOnly(9, 0));
TimetableSlot slot2 = new TimetableSlot(new TimeOnly(9, 0), new TimeOnly(10, 0)); TimetableSpan slot2 = new TimetableSpan(new TimeOnly(9, 0), new TimeOnly(10, 0));
model.AddSlot(slot1); model.AddSlot(slot1);
model.AddSlot(slot2); model.AddSlot(slot2);
Assert.AreEqual(2, model.Slots.Count()); Assert.AreEqual(2, model.Slots.Count());
Assert.AreEqual(new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(9, 0)), model.Slots.ToList()[0]); Assert.AreEqual(new TimetableSpan(new TimeOnly(8, 0), new TimeOnly(9, 0)), model.Slots.ToList()[0]);
Assert.AreEqual(new TimetableSlot(new TimeOnly(9, 0), new TimeOnly(10, 0)), model.Slots.ToList()[1]); Assert.AreEqual(new TimetableSpan(new TimeOnly(9, 0), new TimeOnly(10, 0)), model.Slots.ToList()[1]);
} }
[TestMethod] [TestMethod]
@@ -104,8 +104,8 @@ namespace TimetableDesigner.Tests
{ {
TimetableTemplate model = new TimetableTemplate(); TimetableTemplate model = new TimetableTemplate();
TimetableSlot slot1 = new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(9, 30)); TimetableSpan slot1 = new TimetableSpan(new TimeOnly(8, 0), new TimeOnly(9, 30));
TimetableSlot slot2 = new TimetableSlot(new TimeOnly(8, 30), new TimeOnly(10, 0)); TimetableSpan slot2 = new TimetableSpan(new TimeOnly(8, 30), new TimeOnly(10, 0));
model.AddSlot(slot1); model.AddSlot(slot1);
model.AddSlot(slot2); model.AddSlot(slot2);
@@ -119,9 +119,9 @@ namespace TimetableDesigner.Tests
{ {
TimetableTemplate model = new TimetableTemplate(); TimetableTemplate model = new TimetableTemplate();
TimetableSlot slot1 = new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(9, 0)); TimetableSpan slot1 = new TimetableSpan(new TimeOnly(8, 0), new TimeOnly(9, 0));
TimetableSlot slot2 = new TimetableSlot(new TimeOnly(10, 0), new TimeOnly(11, 0)); TimetableSpan slot2 = new TimetableSpan(new TimeOnly(10, 0), new TimeOnly(11, 0));
TimetableSlot slot3 = new TimetableSlot(new TimeOnly(8, 59), new TimeOnly(10, 1)); TimetableSpan slot3 = new TimetableSpan(new TimeOnly(8, 59), new TimeOnly(10, 1));
model.AddSlot(slot1); model.AddSlot(slot1);
model.AddSlot(slot2); model.AddSlot(slot2);
@@ -135,13 +135,13 @@ namespace TimetableDesigner.Tests
{ {
TimetableTemplate model = new TimetableTemplate(); TimetableTemplate model = new TimetableTemplate();
TimetableSlot slot1 = new TimetableSlot(new TimeOnly(12, 0), new TimeOnly(13, 0)); TimetableSpan slot1 = new TimetableSpan(new TimeOnly(12, 0), new TimeOnly(13, 0));
TimetableSlot slot2 = new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(9, 0)); TimetableSpan slot2 = new TimetableSpan(new TimeOnly(8, 0), new TimeOnly(9, 0));
TimetableSlot slot3 = new TimetableSlot(new TimeOnly(10, 0), new TimeOnly(11, 0)); TimetableSpan slot3 = new TimetableSpan(new TimeOnly(10, 0), new TimeOnly(11, 0));
TimetableSlot slot4 = new TimetableSlot(new TimeOnly(14, 0), new TimeOnly(15, 0)); TimetableSpan slot4 = new TimetableSpan(new TimeOnly(14, 0), new TimeOnly(15, 0));
TimetableSlot slot5 = new TimetableSlot(new TimeOnly(13, 0), new TimeOnly(14, 0)); TimetableSpan slot5 = new TimetableSpan(new TimeOnly(13, 0), new TimeOnly(14, 0));
TimetableSlot slot6 = new TimetableSlot(new TimeOnly(9, 0), new TimeOnly(10, 0)); TimetableSpan slot6 = new TimetableSpan(new TimeOnly(9, 0), new TimeOnly(10, 0));
TimetableSlot slot7 = new TimetableSlot(new TimeOnly(11, 0), new TimeOnly(12, 0)); TimetableSpan slot7 = new TimetableSpan(new TimeOnly(11, 0), new TimeOnly(12, 0));
model.AddSlot(slot1); model.AddSlot(slot1);
model.AddSlot(slot2); model.AddSlot(slot2);
@@ -151,9 +151,9 @@ namespace TimetableDesigner.Tests
model.AddSlot(slot6); model.AddSlot(slot6);
model.AddSlot(slot7); model.AddSlot(slot7);
List<TimetableSlot> slots = model.Slots.ToList(); List<TimetableSpan> slots = model.Slots.ToList();
TimetableSlot testSlot = slots[0]; TimetableSpan testSlot = slots[0];
for (int i = 1; i < slots.Count; i++) for (int i = 1; i < slots.Count; i++)
{ {
if (testSlot.To > slots[i].From) if (testSlot.To > slots[i].From)

View File

@@ -9,7 +9,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TimetableDesigner.Core", "T
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TimetableDesigner.Tests", "TimetableDesigner.Tests\TimetableDesigner.Tests.csproj", "{A9B4DB89-A007-472A-9C80-B6340458AC1B}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TimetableDesigner.Tests", "TimetableDesigner.Tests\TimetableDesigner.Tests.csproj", "{A9B4DB89-A007-472A-9C80-B6340458AC1B}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimetableDesigner.MessageBox", "TimetableDesigner.MessageBox\TimetableDesigner.MessageBox.csproj", "{95992646-6D81-4FF4-885E-8F0BE2312D54}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TimetableDesigner.MessageBox", "TimetableDesigner.MessageBox\TimetableDesigner.MessageBox.csproj", "{95992646-6D81-4FF4-885E-8F0BE2312D54}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimetableDesigner.Customs", "TimetableDesigner.Customs\TimetableDesigner.Customs.csproj", "{BCA4CD04-FD49-4C28-9743-F4F6C1888E7E}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -51,6 +53,14 @@ Global
{95992646-6D81-4FF4-885E-8F0BE2312D54}.Release|Any CPU.Build.0 = Release|Any CPU {95992646-6D81-4FF4-885E-8F0BE2312D54}.Release|Any CPU.Build.0 = Release|Any CPU
{95992646-6D81-4FF4-885E-8F0BE2312D54}.Release|x64.ActiveCfg = Release|Any CPU {95992646-6D81-4FF4-885E-8F0BE2312D54}.Release|x64.ActiveCfg = Release|Any CPU
{95992646-6D81-4FF4-885E-8F0BE2312D54}.Release|x64.Build.0 = Release|Any CPU {95992646-6D81-4FF4-885E-8F0BE2312D54}.Release|x64.Build.0 = Release|Any CPU
{BCA4CD04-FD49-4C28-9743-F4F6C1888E7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BCA4CD04-FD49-4C28-9743-F4F6C1888E7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BCA4CD04-FD49-4C28-9743-F4F6C1888E7E}.Debug|x64.ActiveCfg = Debug|Any CPU
{BCA4CD04-FD49-4C28-9743-F4F6C1888E7E}.Debug|x64.Build.0 = Debug|Any CPU
{BCA4CD04-FD49-4C28-9743-F4F6C1888E7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BCA4CD04-FD49-4C28-9743-F4F6C1888E7E}.Release|Any CPU.Build.0 = Release|Any CPU
{BCA4CD04-FD49-4C28-9743-F4F6C1888E7E}.Release|x64.ActiveCfg = Release|Any CPU
{BCA4CD04-FD49-4C28-9743-F4F6C1888E7E}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -23,6 +23,7 @@ namespace TimetableDesigner.Converters
{ typeof(WelcomeTabViewModel), typeof(WelcomeTabView) }, { typeof(WelcomeTabViewModel), typeof(WelcomeTabView) },
{ typeof(ProjectSettingsTabViewModel), typeof(ProjectSettingsTabView) }, { typeof(ProjectSettingsTabViewModel), typeof(ProjectSettingsTabView) },
{ typeof(ClassroomEditTabViewModel), typeof(ClassroomEditTabView) }, { typeof(ClassroomEditTabViewModel), typeof(ClassroomEditTabView) },
{ typeof(TeacherEditTabViewModel), typeof(TeacherEditTabView) },
}; };
#endregion #endregion

View File

@@ -114,6 +114,15 @@ namespace TimetableDesigner.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to New teacher.
/// </summary>
public static string Global_DefaultTeacherName {
get {
return ResourceManager.GetString("Global.DefaultTeacherName", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to no project loaded. /// Looks up a localized string similar to no project loaded.
/// </summary> /// </summary>
@@ -321,6 +330,24 @@ namespace TimetableDesigner.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Teachers.
/// </summary>
public static string Main_Treeview_Teachers {
get {
return ResourceManager.GetString("Main.Treeview.Teachers", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit teacher.
/// </summary>
public static string Main_Treeview_Teachers_ContextMenu_Edit {
get {
return ResourceManager.GetString("Main.Treeview.Teachers.ContextMenu.Edit", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Error. /// Looks up a localized string similar to Error.
/// </summary> /// </summary>
@@ -429,6 +456,15 @@ namespace TimetableDesigner.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Teacher editing.
/// </summary>
public static string Tabs_TeacherEdit {
get {
return ResourceManager.GetString("Tabs.TeacherEdit", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Welcome. /// Looks up a localized string similar to Welcome.
/// </summary> /// </summary>
@@ -437,5 +473,50 @@ namespace TimetableDesigner.Properties {
return ResourceManager.GetString("Tabs.Welcome", resourceCulture); return ResourceManager.GetString("Tabs.Welcome", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Availability hours.
/// </summary>
public static string TeacherEdit_AvailabilityHours {
get {
return ResourceManager.GetString("TeacherEdit.AvailabilityHours", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Description.
/// </summary>
public static string TeacherEdit_Description {
get {
return ResourceManager.GetString("TeacherEdit.Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &quot;From&quot; value is higher or equal to &quot;To&quot; value.
/// </summary>
public static string TeacherEdit_Message_FromHigherThanTo {
get {
return ResourceManager.GetString("TeacherEdit.Message.FromHigherThanTo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Hour collide with another hour.
/// </summary>
public static string TeacherEdit_Message_HourCollision {
get {
return ResourceManager.GetString("TeacherEdit.Message.HourCollision", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Name.
/// </summary>
public static string TeacherEdit_Name {
get {
return ResourceManager.GetString("TeacherEdit.Name", resourceCulture);
}
}
} }
} }

View File

@@ -135,6 +135,9 @@
<data name="Global.DefaultProjectName" xml:space="preserve"> <data name="Global.DefaultProjectName" xml:space="preserve">
<value>New project</value> <value>New project</value>
</data> </data>
<data name="Global.DefaultTeacherName" xml:space="preserve">
<value>New teacher</value>
</data>
<data name="Global.NoProjectLoadedTitle" xml:space="preserve"> <data name="Global.NoProjectLoadedTitle" xml:space="preserve">
<value>no project loaded</value> <value>no project loaded</value>
</data> </data>
@@ -204,6 +207,12 @@
<data name="Main.Treeview.ContextMenu.Remove" xml:space="preserve"> <data name="Main.Treeview.ContextMenu.Remove" xml:space="preserve">
<value>Remove</value> <value>Remove</value>
</data> </data>
<data name="Main.Treeview.Teachers" xml:space="preserve">
<value>Teachers</value>
</data>
<data name="Main.Treeview.Teachers.ContextMenu.Edit" xml:space="preserve">
<value>Edit teacher</value>
</data>
<data name="MessageBox.Error" xml:space="preserve"> <data name="MessageBox.Error" xml:space="preserve">
<value>Error</value> <value>Error</value>
</data> </data>
@@ -240,7 +249,25 @@
<data name="Tabs.ProjectSettings" xml:space="preserve"> <data name="Tabs.ProjectSettings" xml:space="preserve">
<value>Project settings</value> <value>Project settings</value>
</data> </data>
<data name="Tabs.TeacherEdit" xml:space="preserve">
<value>Teacher editing</value>
</data>
<data name="Tabs.Welcome" xml:space="preserve"> <data name="Tabs.Welcome" xml:space="preserve">
<value>Welcome</value> <value>Welcome</value>
</data> </data>
<data name="TeacherEdit.AvailabilityHours" xml:space="preserve">
<value>Availability hours</value>
</data>
<data name="TeacherEdit.Description" xml:space="preserve">
<value>Description</value>
</data>
<data name="TeacherEdit.Message.FromHigherThanTo" xml:space="preserve">
<value>"From" value is higher or equal to "To" value</value>
</data>
<data name="TeacherEdit.Message.HourCollision" xml:space="preserve">
<value>Hour collide with another hour</value>
</data>
<data name="TeacherEdit.Name" xml:space="preserve">
<value>Name</value>
</data>
</root> </root>

View File

@@ -8,9 +8,12 @@
<BitmapImage x:Key="ProjectSettingsImage" UriSource="Images/ProjectSettings.png"/> <BitmapImage x:Key="ProjectSettingsImage" UriSource="Images/ProjectSettings.png"/>
<BitmapImage x:Key="ClassroomImage" UriSource="Images/Classroom.png"/> <BitmapImage x:Key="ClassroomImage" UriSource="Images/Classroom.png"/>
<BitmapImage x:Key="ClassroomAddImage" UriSource="Images/ClassroomAdd.png"/> <BitmapImage x:Key="ClassroomAddImage" UriSource="Images/ClassroomAdd.png"/>
<BitmapImage x:Key="TeacherImage" UriSource="Images/Teacher.png"/>
<BitmapImage x:Key="TeacherAddImage" UriSource="Images/TeacherAdd.png"/>
<BitmapImage x:Key="OpenImage" UriSource="Images/Open.png"/> <BitmapImage x:Key="OpenImage" UriSource="Images/Open.png"/>
<BitmapImage x:Key="OpenRecentImage" UriSource="Images/OpenRecent.png"/> <BitmapImage x:Key="OpenRecentImage" UriSource="Images/OpenRecent.png"/>
<BitmapImage x:Key="SaveImage" UriSource="Images/Save.png"/> <BitmapImage x:Key="SaveImage" UriSource="Images/Save.png"/>
<BitmapImage x:Key="SaveAsImage" UriSource="Images/SaveAs.png"/> <BitmapImage x:Key="SaveAsImage" UriSource="Images/SaveAs.png"/>
<BitmapImage x:Key="NewImage" UriSource="Images/New.png"/> <BitmapImage x:Key="NewImage" UriSource="Images/New.png"/>
<BitmapImage x:Key="RightArrowImage" UriSource="Images/RightArrow.png"/>
</ResourceDictionary> </ResourceDictionary>

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -20,8 +20,11 @@
<None Remove="Resources\Images\Project.png" /> <None Remove="Resources\Images\Project.png" />
<None Remove="Resources\Images\ProjectSettings.png" /> <None Remove="Resources\Images\ProjectSettings.png" />
<None Remove="Resources\Images\Remove.png" /> <None Remove="Resources\Images\Remove.png" />
<None Remove="Resources\Images\RightArrow.png" />
<None Remove="Resources\Images\Save.png" /> <None Remove="Resources\Images\Save.png" />
<None Remove="Resources\Images\SaveAs.png" /> <None Remove="Resources\Images\SaveAs.png" />
<None Remove="Resources\Images\Teacher.png" />
<None Remove="Resources\Images\TeacherAdd.png" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -52,12 +55,21 @@
<Content Include="Resources\Images\Remove.png"> <Content Include="Resources\Images\Remove.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Resources\Images\RightArrow.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Images\Save.png"> <Content Include="Resources\Images\Save.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Resources\Images\SaveAs.png"> <Content Include="Resources\Images\SaveAs.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Resources\Images\Teacher.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Images\TeacherAdd.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -67,6 +79,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\TimetableDesigner.Core\TimetableDesigner.Core.csproj" /> <ProjectReference Include="..\TimetableDesigner.Core\TimetableDesigner.Core.csproj" />
<ProjectReference Include="..\TimetableDesigner.Customs\TimetableDesigner.Customs.csproj" />
<ProjectReference Include="..\TimetableDesigner.MessageBox\TimetableDesigner.MessageBox.csproj" /> <ProjectReference Include="..\TimetableDesigner.MessageBox\TimetableDesigner.MessageBox.csproj" />
</ItemGroup> </ItemGroup>

View File

@@ -42,7 +42,7 @@ namespace TimetableDesigner.ViewModels
public ClassroomEditTabViewModel() : this(new ClassroomViewModel(new Core.Classroom())) public ClassroomEditTabViewModel() : this(new ClassroomViewModel(new Core.Classroom()))
{ } { }
public ClassroomEditTabViewModel(ClassroomViewModel classroom) public ClassroomEditTabViewModel(ClassroomViewModel classroom) : base()
{ {
_classroom = classroom; _classroom = classroom;
} }

View File

@@ -45,6 +45,9 @@ namespace TimetableDesigner.ViewModels
public ICommand NewClassroomCommand { get; set; } public ICommand NewClassroomCommand { get; set; }
public ICommand EditClassroomCommand { get; set; } public ICommand EditClassroomCommand { get; set; }
public ICommand RemoveClassroomCommand { get; set; } public ICommand RemoveClassroomCommand { get; set; }
public ICommand NewTeacherCommand { get; set; }
public ICommand EditTeacherCommand { get; set; }
public ICommand RemoveTeacherCommand { get; set; }
public ObservableCollection<BaseTabViewModel> Tabs public ObservableCollection<BaseTabViewModel> Tabs
{ {
@@ -95,6 +98,9 @@ namespace TimetableDesigner.ViewModels
NewClassroomCommand = new RelayCommand<object>(param => NewClassroom()); NewClassroomCommand = new RelayCommand<object>(param => NewClassroom());
EditClassroomCommand = new RelayCommand<ClassroomViewModel>(EditClassroom); EditClassroomCommand = new RelayCommand<ClassroomViewModel>(EditClassroom);
RemoveClassroomCommand = new RelayCommand<ClassroomViewModel>(DeleteClassroom); RemoveClassroomCommand = new RelayCommand<ClassroomViewModel>(DeleteClassroom);
NewTeacherCommand = new RelayCommand<object>(param => NewTeacher());
EditTeacherCommand = new RelayCommand<TeacherViewModel>(EditTeacher);
RemoveTeacherCommand = new RelayCommand<TeacherViewModel>(DeleteTeacher);
_tabs = new ObservableCollection<BaseTabViewModel> _tabs = new ObservableCollection<BaseTabViewModel>
{ {
@@ -189,10 +195,8 @@ namespace TimetableDesigner.ViewModels
private void EditClassroom(ClassroomViewModel classroomViewModel) private void EditClassroom(ClassroomViewModel classroomViewModel)
{ {
Debug.WriteLine("textedit"); ClassroomEditTabViewModel classroomEdit = new ClassroomEditTabViewModel(classroomViewModel)
ClassroomEditTabViewModel classroomEdit = new ClassroomEditTabViewModel()
{ {
Classroom = classroomViewModel,
TabTitle = $"{Resources.Tabs_ClassroomEdit}: {classroomViewModel.Name}", TabTitle = $"{Resources.Tabs_ClassroomEdit}: {classroomViewModel.Name}",
IsTabClosable = true IsTabClosable = true
}; };
@@ -202,13 +206,50 @@ namespace TimetableDesigner.ViewModels
private void DeleteClassroom(ClassroomViewModel classroomViewModel) private void DeleteClassroom(ClassroomViewModel classroomViewModel)
{ {
Debug.WriteLine("textdelete");
if (Project is not null) if (Project is not null)
{ {
Project.Classrooms.Remove(classroomViewModel); Project.Classrooms.Remove(classroomViewModel);
} }
} }
private void NewTeacher()
{
if (Project is not null)
{
Teacher teacher = new Teacher()
{
Name = Resources.Global_DefaultTeacherName
};
TeacherViewModel teacherVM = new TeacherViewModel(teacher);
Project.Teachers.Add(teacherVM);
EditTeacher(teacherVM);
}
}
private void EditTeacher(TeacherViewModel teacherViewModel)
{
if (Project is not null)
{
TeacherEditTabViewModel teacherEdit = new TeacherEditTabViewModel(teacherViewModel, Project.TimetableTemplate)
{
Teacher = teacherViewModel,
TabTitle = $"{Resources.Tabs_TeacherEdit}: {teacherViewModel.Name}",
IsTabClosable = true
};
Tabs.Add(teacherEdit);
SelectedTab = teacherEdit;
}
}
private void DeleteTeacher(TeacherViewModel teacherViewModel)
{
if (Project is not null)
{
Project.Teachers.Remove(teacherViewModel);
}
}
#endregion #endregion
} }
} }

View File

@@ -61,6 +61,7 @@ namespace TimetableDesigner.ViewModels.Models
} }
public TimetableTemplateViewModel TimetableTemplate { get; set; } public TimetableTemplateViewModel TimetableTemplate { get; set; }
public ObservableCollection<ClassroomViewModel> Classrooms { get; set; } public ObservableCollection<ClassroomViewModel> Classrooms { get; set; }
public ObservableCollection<TeacherViewModel> Teachers { get; set; }
#endregion #endregion
@@ -76,6 +77,9 @@ namespace TimetableDesigner.ViewModels.Models
Classrooms = new ObservableCollection<ClassroomViewModel>(); Classrooms = new ObservableCollection<ClassroomViewModel>();
Classrooms.CollectionChanged += Classrooms_CollectionChanged; Classrooms.CollectionChanged += Classrooms_CollectionChanged;
Teachers = new ObservableCollection<TeacherViewModel>();
Teachers.CollectionChanged += Teachers_CollectionChanged;
} }
#endregion #endregion
@@ -106,6 +110,28 @@ namespace TimetableDesigner.ViewModels.Models
} }
} }
private void Teachers_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
IList<TeacherViewModel>? added = e.NewItems as IList<TeacherViewModel>;
IList<TeacherViewModel>? removed = e.OldItems as IList<TeacherViewModel>;
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);
}
}
}
#endregion #endregion
} }
} }

View File

@@ -1,9 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using TimetableDesigner.Core; using TimetableDesigner.Core;
using TimetableDesigner.Customs.Collections;
using TimetableDesigner.ViewModels.Base; using TimetableDesigner.ViewModels.Base;
namespace TimetableDesigner.ViewModels.Models namespace TimetableDesigner.ViewModels.Models
@@ -46,6 +48,7 @@ namespace TimetableDesigner.ViewModels.Models
} }
} }
} }
public ObservableDictionary<TimetableDay, ObservableCollection<TimetableSpan>> AvailabilityHours => new ObservableDictionary<TimetableDay, ObservableCollection<TimetableSpan>>(Teacher.AvailabilityHours.ToDictionary(i => i.Key, i => new ObservableCollection<TimetableSpan>(i.Value)));
#endregion #endregion
@@ -59,5 +62,44 @@ namespace TimetableDesigner.ViewModels.Models
} }
#endregion #endregion
#region PUBLIC METHODS
public void AddDay(TimetableDay day)
{
if (day is not null)
{
Teacher.AvailabilityHours.Add(day, new TimetableSpanCollection());
NotifyPropertyChanged(nameof(AvailabilityHours));
}
}
public void RemoveDay(TimetableDay day)
{
Teacher.AvailabilityHours.Remove(day);
NotifyPropertyChanged(nameof(AvailabilityHours));
}
public void AddHours(TimetableDay day, TimetableSpan hours)
{
if (day is not null && hours is not null)
{
Teacher.AvailabilityHours[day].Add(hours);
NotifyPropertyChanged(nameof(AvailabilityHours));
}
}
public void RemoveHours(TimetableDay day, TimetableSpan hours)
{
if (day is not null)
{
Teacher.AvailabilityHours[day].Remove(hours);
NotifyPropertyChanged(nameof(AvailabilityHours));
}
}
#endregion
} }
} }

View File

@@ -24,7 +24,7 @@ namespace TimetableDesigner.ViewModels.Models
public TimetableTemplate TimetableTemplate => _timetableTemplate; public TimetableTemplate TimetableTemplate => _timetableTemplate;
public ObservableCollection<TimetableDay> Days => new ObservableCollection<TimetableDay>(_timetableTemplate.Days); public ObservableCollection<TimetableDay> Days => new ObservableCollection<TimetableDay>(_timetableTemplate.Days);
public ObservableCollection<TimetableSlot> Slots => new ObservableCollection<TimetableSlot>(_timetableTemplate.Slots); public ObservableCollection<TimetableSpan> Slots => new ObservableCollection<TimetableSpan>(_timetableTemplate.Slots);
#endregion #endregion
@@ -55,13 +55,13 @@ namespace TimetableDesigner.ViewModels.Models
NotifyPropertyChanged(nameof(Days)); NotifyPropertyChanged(nameof(Days));
} }
public void AddSlot(TimetableSlot slot) public void AddSlot(TimetableSpan slot)
{ {
_timetableTemplate.AddSlot(slot); _timetableTemplate.AddSlot(slot);
NotifyPropertyChanged(nameof(Slots)); NotifyPropertyChanged(nameof(Slots));
} }
public void RemoveSlot(TimetableSlot slot) public void RemoveSlot(TimetableSpan slot)
{ {
_timetableTemplate.RemoveSlot(slot); _timetableTemplate.RemoveSlot(slot);
NotifyPropertyChanged(nameof(Slots)); NotifyPropertyChanged(nameof(Slots));

View File

@@ -73,14 +73,14 @@ namespace TimetableDesigner.ViewModels
#region CONSTRUCTORS #region CONSTRUCTORS
public ProjectSettingsTabViewModel() public ProjectSettingsTabViewModel() : base()
{ {
Project = new ProjectViewModel(new Project()); Project = new ProjectViewModel(new Project());
AddDayCommand = new RelayCommand<object>(param => AddDay()); AddDayCommand = new RelayCommand<object>(param => AddDay());
AddSlotCommand = new RelayCommand<object>(param => AddSlot()); AddSlotCommand = new RelayCommand<object>(param => AddSlot());
RemoveDayCommand = new RelayCommand<TimetableDay>(RemoveDay); RemoveDayCommand = new RelayCommand<TimetableDay>(RemoveDay);
RemoveSlotCommand = new RelayCommand<TimetableSlot>(RemoveSlot); RemoveSlotCommand = new RelayCommand<TimetableSpan>(RemoveSlot);
_newDayName = string.Empty; _newDayName = string.Empty;
_newSlotFrom = new DateTime(1, 1, 1, 8, 0, 0); _newSlotFrom = new DateTime(1, 1, 1, 8, 0, 0);
@@ -118,7 +118,7 @@ namespace TimetableDesigner.ViewModels
try try
{ {
Project.TimetableTemplate.AddSlot(new TimetableSlot(from, to)); Project.TimetableTemplate.AddSlot(new TimetableSpan(from, to));
double delta = (to - from).TotalMinutes; double delta = (to - from).TotalMinutes;
DateTime newFrom = NewSlotTo.Value; DateTime newFrom = NewSlotTo.Value;
@@ -133,7 +133,7 @@ namespace TimetableDesigner.ViewModels
} }
} }
private void RemoveSlot(TimetableSlot slot) => Project.TimetableTemplate.RemoveSlot(slot); private void RemoveSlot(TimetableSpan slot) => Project.TimetableTemplate.RemoveSlot(slot);
#endregion #endregion
} }

View File

@@ -1,13 +1,188 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input;
using TimetableDesigner.Commands;
using TimetableDesigner.Core;
using TimetableDesigner.MessageBox;
using TimetableDesigner.Properties;
using TimetableDesigner.ViewModels.Base; using TimetableDesigner.ViewModels.Base;
using TimetableDesigner.ViewModels.Models;
namespace TimetableDesigner.ViewModels namespace TimetableDesigner.ViewModels
{ {
public class TeacherEditTabViewModel : BaseViewModel public class TeacherEditTabViewModel : BaseTabViewModel
{ {
#region FIELDS
private TeacherViewModel _teacher;
private TimetableTemplateViewModel _timetableTemplate;
private TimetableDay _selectedDay;
private TimetableDay _selectedNewDay;
private DateTime? _newHourFrom;
private DateTime? _newHourTo;
#endregion
#region PROPERTIES
public TeacherViewModel Teacher
{
get => _teacher;
set
{
if (value != _teacher)
{
_teacher = value;
NotifyPropertyChanged(nameof(Teacher));
}
}
}
public TimetableTemplateViewModel TimetableTemplate
{
get => _timetableTemplate;
set
{
if (value != _timetableTemplate)
{
_timetableTemplate = value;
NotifyPropertyChanged(nameof(TimetableTemplate));
}
}
}
public ICommand AddDayCommand { get; set; }
public ICommand RemoveDayCommand { get; set; }
public ICommand AddHourCommand { get; set; }
public ICommand RemoveHourCommand { get; set; }
public TimetableDay SelectedDay
{
get => _selectedDay;
set
{
if (_selectedDay != value)
{
_selectedDay = value;
NotifyPropertyChanged(nameof(SelectedDay));
NotifyPropertyChanged(nameof(SelectedDayHours));
}
}
}
public TimetableDay SelectedNewDay
{
get => _selectedNewDay;
set
{
if (_selectedNewDay != value)
{
_selectedNewDay = value;
NotifyPropertyChanged(nameof(SelectedNewDay));
}
}
}
public DateTime? NewHourFrom
{
get => _newHourFrom;
set
{
_newHourFrom = value;
NotifyPropertyChanged(nameof(NewHourFrom));
}
}
public DateTime? NewHourTo
{
get => _newHourTo;
set
{
_newHourTo = value;
NotifyPropertyChanged(nameof(NewHourTo));
}
}
public ObservableCollection<TimetableSpan> SelectedDayHours => SelectedDay is not null ? Teacher.AvailabilityHours[SelectedDay] : new ObservableCollection<TimetableSpan>();
#endregion
#region CONSTRUCTORS
public TeacherEditTabViewModel() : this(new TeacherViewModel(new Core.Teacher()), new TimetableTemplateViewModel(new Core.TimetableTemplate()))
{ }
public TeacherEditTabViewModel(TeacherViewModel teacher, TimetableTemplateViewModel timetableTemplate) : base()
{
_teacher = teacher;
_timetableTemplate = timetableTemplate;
AddDayCommand = new RelayCommand<object>(param => AddDay());
RemoveDayCommand = new RelayCommand<TimetableDay>(RemoveDay);
AddHourCommand = new RelayCommand<object>(param => AddHour());
RemoveHourCommand = new RelayCommand<TimetableSpan>(RemoveHour);
_newHourFrom = new DateTime(1, 1, 1, 8, 0, 0);
_newHourTo = new DateTime(1, 1, 1, 16, 0, 0);
}
#endregion
#region PRIVATE METHODS
private void AddDay()
{
if (!Teacher.AvailabilityHours.ContainsKey(SelectedNewDay))
{
Teacher.AddDay(SelectedNewDay);
}
}
private void RemoveDay(TimetableDay day)
{
Teacher.RemoveDay(day);
}
private void AddHour()
{
if (NewHourFrom.HasValue && NewHourTo.HasValue)
{
TimeOnly from = new TimeOnly(NewHourFrom.Value.Hour, NewHourFrom.Value.Minute);
TimeOnly to = new TimeOnly(NewHourTo.Value.Hour, NewHourTo.Value.Minute);
if (from >= to)
{
MessageBoxService.ShowError(Resources.TeacherEdit_Message_FromHigherThanTo);
return;
}
try
{
Teacher.AddHours(SelectedDay, new TimetableSpan(from, to));
}
catch (ArgumentException)
{
MessageBoxService.ShowError(Resources.TeacherEdit_Message_HourCollision);
}
}
NotifyPropertyChanged(nameof(SelectedDayHours));
}
private void RemoveHour(TimetableSpan hour)
{
Teacher.RemoveHours(SelectedDay, hour);
NotifyPropertyChanged(nameof(SelectedDayHours));
}
#endregion
} }
} }

View File

@@ -46,7 +46,7 @@
</rib:RibbonGroupBox> </rib:RibbonGroupBox>
<rib:RibbonGroupBox Header="{x:Static p:Resources.Main_Ribbon_Project_New}"> <rib:RibbonGroupBox Header="{x:Static p:Resources.Main_Ribbon_Project_New}">
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewClassroom}" Icon="{StaticResource ClassroomAddImage}" Command="{Binding NewClassroomCommand}"/> <rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewClassroom}" Icon="{StaticResource ClassroomAddImage}" Command="{Binding NewClassroomCommand}"/>
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewTeacher}"/> <rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewTeacher}" Icon="{StaticResource TeacherAddImage}" Command="{Binding NewTeacherCommand}"/>
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewGroup}"/> <rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewGroup}"/>
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewSubgroup}"/> <rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewSubgroup}"/>
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewClass}"/> <rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewClass}"/>
@@ -76,9 +76,7 @@
<DataTemplate> <DataTemplate>
<TreeViewItem Header="{Binding Name}" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=rib:RibbonWindow}, Path=DataContext}"> <TreeViewItem Header="{Binding Name}" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=rib:RibbonWindow}, Path=DataContext}">
<TreeViewItem.InputBindings> <TreeViewItem.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" <MouseBinding Gesture="LeftDoubleClick"/>
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=rib:RibbonWindow}, Path=DataContext.EditClassroomCommand}"
CommandParameter="{Binding}"/>
</TreeViewItem.InputBindings> </TreeViewItem.InputBindings>
<TreeViewItem.ContextMenu> <TreeViewItem.ContextMenu>
<ContextMenu> <ContextMenu>
@@ -96,6 +94,35 @@
</DataTemplate> </DataTemplate>
</TreeViewItem.ItemTemplate> </TreeViewItem.ItemTemplate>
</TreeViewItem> </TreeViewItem>
<TreeViewItem ItemsSource="{Binding Teachers}">
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource TeacherImage}" Width="18" Height="18"/>
<Label Content="{x:Static p:Resources.Main_Treeview_Teachers}"/>
</StackPanel>
</TreeViewItem.Header>
<TreeViewItem.ItemTemplate>
<DataTemplate>
<TreeViewItem Header="{Binding Name}" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=rib:RibbonWindow}, Path=DataContext}">
<TreeViewItem.InputBindings>
<MouseBinding Gesture="LeftDoubleClick"/>
</TreeViewItem.InputBindings>
<TreeViewItem.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.Main_Treeview_ContextMenu_EditTimetable}"/>
<MenuItem Header="{x:Static p:Resources.Main_Treeview_Teachers_ContextMenu_Edit}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.Tag.EditTeacherCommand}"
CommandParameter="{Binding}"/>
<Separator/>
<MenuItem Header="{x:Static p:Resources.Main_Treeview_ContextMenu_Remove}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.Tag.RemoveTeacherCommand}"
CommandParameter="{Binding}"/>
</ContextMenu>
</TreeViewItem.ContextMenu>
</TreeViewItem>
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
</TreeViewItem> </TreeViewItem>
</TreeView> </TreeView>
<GridSplitter Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Stretch" ShowsPreview="True" Width="2"/> <GridSplitter Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Stretch" ShowsPreview="True" Width="2"/>

View File

@@ -3,10 +3,117 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TimetableDesigner.Views" xmlns:p = "clr-namespace:TimetableDesigner.Properties"
xmlns:vm="clr-namespace:TimetableDesigner.ViewModels"
xmlns:local="clr-namespace:TimetableDesigner.Views" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<vm:TeacherEditTabViewModel/>
</UserControl.DataContext>
<ScrollViewer>
<StackPanel>
<Grid> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="{x:Static p:Resources.TeacherEdit_Name}"/>
<TextBox Grid.Column="1" Grid.Row="0" Margin="5" Text="{Binding Teacher.Name}" MaxLines="1"/>
<Label Grid.Column="0" Grid.Row="1" Content="{x:Static p:Resources.TeacherEdit_Description}"/>
<TextBox Grid.Column="1" Grid.Row="1" Margin="5" MinLines="3" Text="{Binding Teacher.Description}"/>
</Grid> </Grid>
<StackPanel>
<Label Content="{x:Static p:Resources.TeacherEdit_AvailabilityHours}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Column="0" Grid.Row="0" Margin="5" BorderThickness="1" BorderBrush="DarkGray">
<ListBox MinHeight="100" ItemsSource="{Binding Teacher.AvailabilityHours.Keys}" SelectedItem="{Binding SelectedDay}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" HorizontalAlignment="Stretch" Content="{Binding Name}"/>
<Button Grid.Column="1"
Width="26"
Height="26"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=DataContext.RemoveDayCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=DataContext}">
<Image Source="{StaticResource RemoveImage}" Width="20" Height="20"/>
</Button>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
<Image Grid.Column="1" Grid.Row="0" VerticalAlignment="Center" Source="{StaticResource RightArrowImage}" Width="20" Height="20"/>
<Border Grid.Column="2" Grid.Row="0" Margin="5" BorderThickness="1" BorderBrush="DarkGray">
<ListBox ItemsSource="{Binding SelectedDayHours}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" HorizontalAlignment="Stretch" Orientation="Horizontal">
<Label Content="{Binding From}"/>
<Label Content=" - "/>
<Label Content="{Binding To}"/>
</StackPanel>
<Button Grid.Column="1"
Width="26"
Height="26"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=DataContext.RemoveHourCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=DataContext}">
<Image Source="{StaticResource RemoveImage}" Width="20" Height="20"/>
</Button>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
<Grid Grid.Column="0" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0" Margin="5" ItemsSource="{Binding TimetableTemplate.Days}" SelectedItem="{Binding SelectedNewDay}" DisplayMemberPath="Name"/>
<Button Grid.Column="1" Margin="5" Width="26" Height="26" Command="{Binding AddDayCommand}">
<Image Source="{StaticResource AddImage}" Width="20" Height="20"/>
</Button>
</Grid>
<Grid Grid.Column="2" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<xctk:DateTimeUpDown Grid.Column="0" Margin="5" Format="ShortTime" Value="{Binding NewHourFrom}"/>
<Label Grid.Column="1" Margin="5" Content="-"/>
<xctk:DateTimeUpDown Grid.Column="2" Margin="5" Format="ShortTime" Value="{Binding NewHourTo}"/>
<Button Grid.Column="3" Margin="5" Command="{Binding AddHourCommand}" Width="26" Height="26">
<Image Source="{StaticResource AddImage}" Width="20" Height="20"/>
</Button>
</Grid>
</Grid>
</StackPanel>
</StackPanel>
</ScrollViewer>
</UserControl> </UserControl>