Project creation, basics

This commit is contained in:
2023-03-12 12:32:26 +01:00
Unverified
commit 95364c8a31
68 changed files with 3517 additions and 0 deletions

135
.gitignore vendored Normal file
View File

@@ -0,0 +1,135 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
x64/
[Bb]in/
[Oo]bj/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.svclog
*.scc
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# Click-Once directory
publish/
# Publish Web Output
*.Publish.xml
*.pubxml
*.azurePubxml
# NuGet Packages Directory
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
packages/
## TODO: If the tool you use requires repositories.config, also uncomment the next line
!packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
![Ss]tyle[Cc]op.targets
~$*
*~
*.dbmdl
*.[Pp]ublish.xml
*.publishsettings
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
App_Data/*.mdf
App_Data/*.ldf
# =========================
# Windows detritus
# =========================
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Mac desktop service store files
.DS_Store
_NCrunch*
.vs/

0
README.md Normal file
View File

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Core
{
public class Class
{
#region PROPERTIES
public string Name { get; set; }
public string Description { get; set; }
public Teacher Teacher { get; set; }
public Subgroup Subgroup { get; set; }
public Classroom Classroom { get; set; }
#endregion
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Core
{
public class Classroom
{
#region PROPERTIES
public string Name { get; set; }
public string Description { get; set; }
public bool IsCapacityLimited { get; set; }
public uint Capacity { get; set; }
#endregion
#region CONSTRUCTORS
public Classroom()
{
Name = string.Empty;
Description = string.Empty;
IsCapacityLimited = false;
Capacity = 1;
}
#endregion
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Core
{
public class Group
{
#region PROPERTIES
public string Name { get; set; }
public string Description { get; set; }
public Subgroup MainSubgroup { get; set; }
public ICollection<Subgroup> AssignedSubgroups { get; set; }
#endregion
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Core
{
[Serializable]
public class Project
{
#region PROPERTIES
public string Name { get; set; }
public string Author { get; set; }
public string Description { get; set; }
public TimetableTemplate TimetableTemplate { get; set; }
public ICollection<Classroom> Classrooms { get; set; }
public ICollection<Teacher> Teachers { get; set; }
public ICollection<Group> Groups { get; set; }
public ICollection<Subgroup> Subgroups { get; set; }
public ICollection<Class> Classes { get; set; }
#endregion
#region CONSTRUCTORS
public Project()
{
Name = string.Empty;
Author = string.Empty;
Description = string.Empty;
TimetableTemplate = new TimetableTemplate();
Classrooms = new List<Classroom>();
Teachers = new List<Teacher>();
}
#endregion
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Core
{
public class Subgroup
{
#region PROPERTIES
public string Name { get; set; }
public string Description { get; set; }
#endregion
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Core
{
public class Teacher
{
#region PROPERTIES
public string Name { get; set; }
public string Description { get; set; }
#endregion
}
}

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Core
{
public struct TimetableDay
{
#region PROPERTIES
public string Name { get; set; }
#endregion
#region CONSTRUCTORS
public TimetableDay(string name)
{
Name = name;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,75 @@
using System;
namespace TimetableDesigner.Core
{
[Serializable]
public struct TimetableSlot
{
#region PROPERTIES
public TimeOnly From { get; private set; }
public TimeOnly To { get; private set; }
#endregion
#region CONSTRUCTORS
public TimetableSlot(TimeOnly from, TimeOnly to)
{
if (to <= from)
{
throw new ArgumentException("\"to\" cannot be less or equal to \"from\"");
}
From = from;
To = to;
}
#endregion
#region PUBLIC METHODS
internal TimetableSlotsCollision CheckCollision(TimetableSlot slot)
{
if (slot.To <= this.From)
{
return TimetableSlotsCollision.CheckedSlotBefore;
}
else if (this.To <= slot.From)
{
return TimetableSlotsCollision.CheckedSlotAfter;
}
else
{
if (this.From < slot.From && slot.To < this.To)
{
return TimetableSlotsCollision.CheckedSlotIn;
}
else if (this.From < slot.From && slot.From < this.To && this.To < slot.To)
{
return TimetableSlotsCollision.CheckedSlotFromIn;
}
else if (slot.From < this.From && this.From < slot.To && slot.To < this.To)
{
return TimetableSlotsCollision.CheckedSlotToIn;
}
else
{
throw new ArgumentException("Unknown collision");
}
}
}
public override bool Equals(object? obj) => obj is TimetableSlot slot && From == slot.From && To == slot.To;
public override int GetHashCode() => HashCode.Combine(From, To);
public override string? ToString() => $"{From}-{To}";
#endregion
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Core
{
internal enum TimetableSlotsCollision
{
CheckedSlotBefore,
CheckedSlotAfter,
CheckedSlotIn,
CheckedSlotFromIn,
CheckedSlotToIn
}
}

View File

@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Core
{
[Serializable]
public class TimetableTemplate
{
#region FIELDS
private List<TimetableDay> _days;
private List<TimetableSlot> _slots;
#endregion
#region PROPERTIES
public IEnumerable<TimetableDay> Days => _days;
public IEnumerable<TimetableSlot> Slots => _slots;
#endregion
#region CONSTRUCTORS
public TimetableTemplate()
{
_days = new List<TimetableDay>();
_slots = new List<TimetableSlot>();
}
#endregion
#region PUBLIC METHODS
public void AddDay(TimetableDay name)
{
_days.Add(name);
}
public bool RemoveDay(TimetableDay day)
{
return _days.Remove(day);
}
public void AddSlot(TimetableSlot 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)
{
return _slots.Remove(slot);
}
#endregion
}
}

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Versioning;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using TimetableDesigner.MessageBox.Properties;
namespace TimetableDesigner.MessageBox
{
public static class MessageBoxService
{
#region PUBLIC METHODS
public static void ShowError(string message) => System.Windows.MessageBox.Show(message, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
public static MessageBoxYesNoCancelResult ShowYesNoCancelQuestion(string message)
{
MessageBoxResult result = System.Windows.MessageBox.Show(message, Resources.Error, MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
switch (result)
{
case MessageBoxResult.Yes: return MessageBoxYesNoCancelResult.Yes;
case MessageBoxResult.No: return MessageBoxYesNoCancelResult.No;
case MessageBoxResult.Cancel: return MessageBoxYesNoCancelResult.Cancel;
default: return MessageBoxYesNoCancelResult.None;
}
}
#endregion
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.MessageBox
{
public enum MessageBoxYesNoCancelResult
{
Yes,
No,
Cancel,
None
}
}

View File

@@ -0,0 +1,81 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
namespace TimetableDesigner.MessageBox.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TimetableDesigner.MessageBox.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Error.
/// </summary>
internal static string Error {
get {
return ResourceManager.GetString("Error", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Question.
/// </summary>
internal static string Question {
get {
return ResourceManager.GetString("Question", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Error" xml:space="preserve">
<value>Error</value>
</data>
<data name="Question" xml:space="preserve">
<value>Question</value>
</data>
</root>

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="coverlet.collector" Version="3.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TimetableDesigner.Core\TimetableDesigner.Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,167 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Diagnostics;
using TimetableDesigner.Core;
namespace TimetableDesigner.Tests
{
[TestClass]
public class TimetableTest
{
[TestMethod]
public void CreateValidSlotTest()
{
TimetableSlot slot = new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(9, 0));
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void CreateInvalidSlotTest()
{
TimetableSlot slot = new TimetableSlot(new TimeOnly(9, 0), new TimeOnly(8, 0));
Assert.Fail();
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void CreateSlotWithZeroLengthTest()
{
TimetableSlot slot = new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(8, 0));
Assert.Fail();
}
[TestMethod]
public void CreateModelTest()
{
TimetableTemplate model = new TimetableTemplate();
}
[TestMethod]
public void AddDayTest()
{
TimetableTemplate model = new TimetableTemplate();
TimetableDay day = new TimetableDay
{
Name = "Monday"
};
model.AddDay(day);
Assert.AreEqual(1, model.Days.Count());
Assert.AreEqual(day, model.Days.ToList()[0]);
}
[TestMethod]
public void AddSlotTest()
{
TimetableTemplate model = new TimetableTemplate();
TimetableSlot slot = new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(9, 0));
model.AddSlot(slot);
Assert.AreEqual(1, model.Slots.Count());
Assert.AreEqual(new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(9, 0)), model.Slots.ToList()[0]);
}
[TestMethod]
public void AddNoCollidingSlotsTest()
{
TimetableTemplate model = new TimetableTemplate();
TimetableSlot slot1 = new TimetableSlot(new TimeOnly(8, 15), new TimeOnly(9, 0));
TimetableSlot slot2 = new TimetableSlot(new TimeOnly(9, 15), new TimeOnly(10, 0));
model.AddSlot(slot1);
model.AddSlot(slot2);
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 TimetableSlot(new TimeOnly(9, 15), new TimeOnly(10, 0)), model.Slots.ToList()[1]);
}
[TestMethod]
public void AddSlotsWithoutBreakTest()
{
TimetableTemplate model = new TimetableTemplate();
TimetableSlot slot1 = new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(9, 0));
TimetableSlot slot2 = new TimetableSlot(new TimeOnly(9, 0), new TimeOnly(10, 0));
model.AddSlot(slot1);
model.AddSlot(slot2);
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 TimetableSlot(new TimeOnly(9, 0), new TimeOnly(10, 0)), model.Slots.ToList()[1]);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void AddCollidingSlotsTest()
{
TimetableTemplate model = new TimetableTemplate();
TimetableSlot slot1 = new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(9, 30));
TimetableSlot slot2 = new TimetableSlot(new TimeOnly(8, 30), new TimeOnly(10, 0));
model.AddSlot(slot1);
model.AddSlot(slot2);
Assert.Fail();
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void AddBetweenCollidingSlotsTest()
{
TimetableTemplate model = new TimetableTemplate();
TimetableSlot slot1 = new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(9, 0));
TimetableSlot slot2 = new TimetableSlot(new TimeOnly(10, 0), new TimeOnly(11, 0));
TimetableSlot slot3 = new TimetableSlot(new TimeOnly(8, 59), new TimeOnly(10, 1));
model.AddSlot(slot1);
model.AddSlot(slot2);
model.AddSlot(slot3);
Assert.Fail();
}
[TestMethod]
public void SortSlotsTest()
{
TimetableTemplate model = new TimetableTemplate();
TimetableSlot slot1 = new TimetableSlot(new TimeOnly(12, 0), new TimeOnly(13, 0));
TimetableSlot slot2 = new TimetableSlot(new TimeOnly(8, 0), new TimeOnly(9, 0));
TimetableSlot slot3 = new TimetableSlot(new TimeOnly(10, 0), new TimeOnly(11, 0));
TimetableSlot slot4 = new TimetableSlot(new TimeOnly(14, 0), new TimeOnly(15, 0));
TimetableSlot slot5 = new TimetableSlot(new TimeOnly(13, 0), new TimeOnly(14, 0));
TimetableSlot slot6 = new TimetableSlot(new TimeOnly(9, 0), new TimeOnly(10, 0));
TimetableSlot slot7 = new TimetableSlot(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);
List<TimetableSlot> slots = model.Slots.ToList();
TimetableSlot testSlot = slots[0];
for (int i = 1; i < slots.Count; i++)
{
if (testSlot.To > slots[i].From)
{
Assert.Fail();
}
testSlot = slots[i];
}
}
}
}

61
TimetableDesigner.sln Normal file
View File

@@ -0,0 +1,61 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33213.308
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TimetableDesigner", "TimetableDesigner\TimetableDesigner.csproj", "{739E5B4E-09D0-468C-9FA0-4BB6D89C1FE0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TimetableDesigner.Core", "TimetableDesigner.Core\TimetableDesigner.Core.csproj", "{A00B17BE-238D-4241-BECC-30119F8CBEE0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TimetableDesigner.Tests", "TimetableDesigner.Tests\TimetableDesigner.Tests.csproj", "{A9B4DB89-A007-472A-9C80-B6340458AC1B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimetableDesigner.MessageBox", "TimetableDesigner.MessageBox\TimetableDesigner.MessageBox.csproj", "{95992646-6D81-4FF4-885E-8F0BE2312D54}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{739E5B4E-09D0-468C-9FA0-4BB6D89C1FE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{739E5B4E-09D0-468C-9FA0-4BB6D89C1FE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{739E5B4E-09D0-468C-9FA0-4BB6D89C1FE0}.Debug|x64.ActiveCfg = Debug|x64
{739E5B4E-09D0-468C-9FA0-4BB6D89C1FE0}.Debug|x64.Build.0 = Debug|x64
{739E5B4E-09D0-468C-9FA0-4BB6D89C1FE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{739E5B4E-09D0-468C-9FA0-4BB6D89C1FE0}.Release|Any CPU.Build.0 = Release|Any CPU
{739E5B4E-09D0-468C-9FA0-4BB6D89C1FE0}.Release|x64.ActiveCfg = Release|x64
{739E5B4E-09D0-468C-9FA0-4BB6D89C1FE0}.Release|x64.Build.0 = Release|x64
{A00B17BE-238D-4241-BECC-30119F8CBEE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A00B17BE-238D-4241-BECC-30119F8CBEE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A00B17BE-238D-4241-BECC-30119F8CBEE0}.Debug|x64.ActiveCfg = Debug|Any CPU
{A00B17BE-238D-4241-BECC-30119F8CBEE0}.Debug|x64.Build.0 = Debug|Any CPU
{A00B17BE-238D-4241-BECC-30119F8CBEE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A00B17BE-238D-4241-BECC-30119F8CBEE0}.Release|Any CPU.Build.0 = Release|Any CPU
{A00B17BE-238D-4241-BECC-30119F8CBEE0}.Release|x64.ActiveCfg = Release|Any CPU
{A00B17BE-238D-4241-BECC-30119F8CBEE0}.Release|x64.Build.0 = Release|Any CPU
{A9B4DB89-A007-472A-9C80-B6340458AC1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9B4DB89-A007-472A-9C80-B6340458AC1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9B4DB89-A007-472A-9C80-B6340458AC1B}.Debug|x64.ActiveCfg = Debug|Any CPU
{A9B4DB89-A007-472A-9C80-B6340458AC1B}.Debug|x64.Build.0 = Debug|Any CPU
{A9B4DB89-A007-472A-9C80-B6340458AC1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9B4DB89-A007-472A-9C80-B6340458AC1B}.Release|Any CPU.Build.0 = Release|Any CPU
{A9B4DB89-A007-472A-9C80-B6340458AC1B}.Release|x64.ActiveCfg = Release|Any CPU
{A9B4DB89-A007-472A-9C80-B6340458AC1B}.Release|x64.Build.0 = Release|Any CPU
{95992646-6D81-4FF4-885E-8F0BE2312D54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{95992646-6D81-4FF4-885E-8F0BE2312D54}.Debug|Any CPU.Build.0 = Debug|Any CPU
{95992646-6D81-4FF4-885E-8F0BE2312D54}.Debug|x64.ActiveCfg = Debug|Any CPU
{95992646-6D81-4FF4-885E-8F0BE2312D54}.Debug|x64.Build.0 = Debug|Any CPU
{95992646-6D81-4FF4-885E-8F0BE2312D54}.Release|Any CPU.ActiveCfg = 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.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {948AE685-3476-4374-9ADE-9F57FDE2D8D7}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,15 @@
<Application x:Class="TimetableDesigner.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ShutdownMode="OnMainWindowClose"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Converters.xaml"/>
<ResourceDictionary Source="Resources/Images.xaml"/>
<ResourceDictionary Source="pack://application:,,,/Fluent;Component/Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace TimetableDesigner
{
public partial class App : Application
{
public App()
{
}
}
}

View File

@@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace TimetableDesigner.Commands
{
public class RelayCommand<T> : ICommand
{
#region FIELDS
private readonly Action<T> _execute;
private readonly Predicate<T> _canExecute;
#endregion
#region CONSTRUCTORS
public RelayCommand(Action<T> execute) : this(execute, null) { }
public RelayCommand(Action<T> execute, Predicate<T> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion
#region PUBLIC METHODS
public bool CanExecute(object parameter) => _canExecute == null || _canExecute((T)parameter);
public void Execute(object parameter)
{
_execute((T)parameter);
}
#endregion
#region EVENTS
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
#endregion
}
}

View File

@@ -0,0 +1,24 @@
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
{
public class IsNotNullToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is not null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,24 @@
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
{
public class IsNullToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is null ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,57 @@
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;
using TimetableDesigner.ViewModels;
using TimetableDesigner.ViewModels.Base;
using TimetableDesigner.Views;
namespace TimetableDesigner.Converters
{
public class ViewModelToViewConverter : IValueConverter
{
#region FIELDS
private static readonly Dictionary<Type, Type> _viewModelViewPairs = new Dictionary<Type, Type>
{
{ typeof(MainViewModel), typeof(MainWindow) },
{ typeof(WelcomeTabViewModel), typeof(WelcomeTabView) },
{ typeof(ProjectSettingsTabViewModel), typeof(ProjectSettingsTabView) },
{ typeof(ClassroomEditTabViewModel), typeof(ClassroomEditTabView) },
};
#endregion
#region PUBLIC METHODS
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
BaseViewModel? viewModel = value as BaseViewModel;
if (viewModel is not null)
{
Type view = _viewModelViewPairs[viewModel.GetType()];
FrameworkElement? viewInstance = Activator.CreateInstance(view) as FrameworkElement;
if (viewInstance is not null )
{
viewInstance.DataContext = viewModel;
return viewInstance;
}
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
}

View File

@@ -0,0 +1,441 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
namespace TimetableDesigner.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TimetableDesigner.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Capacity.
/// </summary>
public static string ClassroomEdit_Capacity {
get {
return ResourceManager.GetString("ClassroomEdit.Capacity", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Is limited?.
/// </summary>
public static string ClassroomEdit_CapacityIsLimited {
get {
return ResourceManager.GetString("ClassroomEdit.CapacityIsLimited", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Description.
/// </summary>
public static string ClassroomEdit_Description {
get {
return ResourceManager.GetString("ClassroomEdit.Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Name.
/// </summary>
public static string ClassroomEdit_Name {
get {
return ResourceManager.GetString("ClassroomEdit.Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New classroom.
/// </summary>
public static string Global_DefaultClassroomName {
get {
return ResourceManager.GetString("Global.DefaultClassroomName", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New project.
/// </summary>
public static string Global_DefaultProjectName {
get {
return ResourceManager.GetString("Global.DefaultProjectName", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to no project loaded.
/// </summary>
public static string Global_NoProjectLoadedTitle {
get {
return ResourceManager.GetString("Global.NoProjectLoadedTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Project is loaded. Do you want to save?.
/// </summary>
public static string Main_Message_SaveCurrentProject {
get {
return ResourceManager.GetString("Main.Message.SaveCurrentProject", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to File.
/// </summary>
public static string Main_Ribbon_File {
get {
return ResourceManager.GetString("Main.Ribbon.File", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New/Open.
/// </summary>
public static string Main_Ribbon_File_NewOpen {
get {
return ResourceManager.GetString("Main.Ribbon.File.NewOpen", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New.
/// </summary>
public static string Main_Ribbon_File_NewOpen_New {
get {
return ResourceManager.GetString("Main.Ribbon.File.NewOpen.New", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Open.
/// </summary>
public static string Main_Ribbon_File_NewOpen_Open {
get {
return ResourceManager.GetString("Main.Ribbon.File.NewOpen.Open", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Open recent.
/// </summary>
public static string Main_Ribbon_File_NewOpen_OpenRecent {
get {
return ResourceManager.GetString("Main.Ribbon.File.NewOpen.OpenRecent", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Save.
/// </summary>
public static string Main_Ribbon_File_Save {
get {
return ResourceManager.GetString("Main.Ribbon.File.Save", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Save.
/// </summary>
public static string Main_Ribbon_File_Save_Save {
get {
return ResourceManager.GetString("Main.Ribbon.File.Save.Save", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Save as.
/// </summary>
public static string Main_Ribbon_File_Save_SaveAs {
get {
return ResourceManager.GetString("Main.Ribbon.File.Save.SaveAs", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Project.
/// </summary>
public static string Main_Ribbon_Project {
get {
return ResourceManager.GetString("Main.Ribbon.Project", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New.
/// </summary>
public static string Main_Ribbon_Project_New {
get {
return ResourceManager.GetString("Main.Ribbon.Project.New", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New class.
/// </summary>
public static string Main_Ribbon_Project_New_NewClass {
get {
return ResourceManager.GetString("Main.Ribbon.Project.New.NewClass", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New classroom.
/// </summary>
public static string Main_Ribbon_Project_New_NewClassroom {
get {
return ResourceManager.GetString("Main.Ribbon.Project.New.NewClassroom", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New group.
/// </summary>
public static string Main_Ribbon_Project_New_NewGroup {
get {
return ResourceManager.GetString("Main.Ribbon.Project.New.NewGroup", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New subgroup.
/// </summary>
public static string Main_Ribbon_Project_New_NewSubgroup {
get {
return ResourceManager.GetString("Main.Ribbon.Project.New.NewSubgroup", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New teacher.
/// </summary>
public static string Main_Ribbon_Project_New_NewTeacher {
get {
return ResourceManager.GetString("Main.Ribbon.Project.New.NewTeacher", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Settings.
/// </summary>
public static string Main_Ribbon_Project_Settings {
get {
return ResourceManager.GetString("Main.Ribbon.Project.Settings", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Project settings.
/// </summary>
public static string Main_Ribbon_Project_Settings_ProjectSettings {
get {
return ResourceManager.GetString("Main.Ribbon.Project.Settings.ProjectSettings", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Classrooms.
/// </summary>
public static string Main_Treeview_Classrooms {
get {
return ResourceManager.GetString("Main.Treeview.Classrooms", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit classroom.
/// </summary>
public static string Main_Treeview_Classrooms_ContextMenu_Edit {
get {
return ResourceManager.GetString("Main.Treeview.Classrooms.ContextMenu.Edit", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit timetable.
/// </summary>
public static string Main_Treeview_ContextMenu_EditTimetable {
get {
return ResourceManager.GetString("Main.Treeview.ContextMenu.EditTimetable", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Remove.
/// </summary>
public static string Main_Treeview_ContextMenu_Remove {
get {
return ResourceManager.GetString("Main.Treeview.ContextMenu.Remove", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error.
/// </summary>
public static string MessageBox_Error {
get {
return ResourceManager.GetString("MessageBox.Error", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Author.
/// </summary>
public static string ProjectSettings_Author {
get {
return ResourceManager.GetString("ProjectSettings.Author", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Basics.
/// </summary>
public static string ProjectSettings_Basics {
get {
return ResourceManager.GetString("ProjectSettings.Basics", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Days.
/// </summary>
public static string ProjectSettings_Days {
get {
return ResourceManager.GetString("ProjectSettings.Days", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Description.
/// </summary>
public static string ProjectSettings_Description {
get {
return ResourceManager.GetString("ProjectSettings.Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Hours.
/// </summary>
public static string ProjectSettings_Hours {
get {
return ResourceManager.GetString("ProjectSettings.Hours", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Length of the class have to be higher than 0 minutes.
/// </summary>
public static string ProjectSettings_Message_FromHigherThanTo {
get {
return ResourceManager.GetString("ProjectSettings.Message.FromHigherThanTo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Slot collide with another slot.
/// </summary>
public static string ProjectSettings_Message_SlotCollision {
get {
return ResourceManager.GetString("ProjectSettings.Message.SlotCollision", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Name.
/// </summary>
public static string ProjectSettings_Name {
get {
return ResourceManager.GetString("ProjectSettings.Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Timetable model.
/// </summary>
public static string ProjectSettings_TimetableModel {
get {
return ResourceManager.GetString("ProjectSettings.TimetableModel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Classroom editing.
/// </summary>
public static string Tabs_ClassroomEdit {
get {
return ResourceManager.GetString("Tabs.ClassroomEdit", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Project settings.
/// </summary>
public static string Tabs_ProjectSettings {
get {
return ResourceManager.GetString("Tabs.ProjectSettings", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Welcome.
/// </summary>
public static string Tabs_Welcome {
get {
return ResourceManager.GetString("Tabs.Welcome", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
namespace TimetableDesigner.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources___Copy {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources___Copy() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TimetableDesigner.Properties.Resources - Copy", typeof(Resources___Copy).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,246 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ClassroomEdit.Capacity" xml:space="preserve">
<value>Capacity</value>
</data>
<data name="ClassroomEdit.CapacityIsLimited" xml:space="preserve">
<value>Is limited?</value>
</data>
<data name="ClassroomEdit.Description" xml:space="preserve">
<value>Description</value>
</data>
<data name="ClassroomEdit.Name" xml:space="preserve">
<value>Name</value>
</data>
<data name="Global.DefaultClassroomName" xml:space="preserve">
<value>New classroom</value>
</data>
<data name="Global.DefaultProjectName" xml:space="preserve">
<value>New project</value>
</data>
<data name="Global.NoProjectLoadedTitle" xml:space="preserve">
<value>no project loaded</value>
</data>
<data name="Main.Message.SaveCurrentProject" xml:space="preserve">
<value>Project is loaded. Do you want to save?</value>
</data>
<data name="Main.Ribbon.File" xml:space="preserve">
<value>File</value>
</data>
<data name="Main.Ribbon.File.NewOpen" xml:space="preserve">
<value>New/Open</value>
</data>
<data name="Main.Ribbon.File.NewOpen.New" xml:space="preserve">
<value>New</value>
</data>
<data name="Main.Ribbon.File.NewOpen.Open" xml:space="preserve">
<value>Open</value>
</data>
<data name="Main.Ribbon.File.NewOpen.OpenRecent" xml:space="preserve">
<value>Open recent</value>
</data>
<data name="Main.Ribbon.File.Save" xml:space="preserve">
<value>Save</value>
</data>
<data name="Main.Ribbon.File.Save.Save" xml:space="preserve">
<value>Save</value>
</data>
<data name="Main.Ribbon.File.Save.SaveAs" xml:space="preserve">
<value>Save as</value>
</data>
<data name="Main.Ribbon.Project" xml:space="preserve">
<value>Project</value>
</data>
<data name="Main.Ribbon.Project.New" xml:space="preserve">
<value>New</value>
</data>
<data name="Main.Ribbon.Project.New.NewClass" xml:space="preserve">
<value>New class</value>
</data>
<data name="Main.Ribbon.Project.New.NewClassroom" xml:space="preserve">
<value>New classroom</value>
</data>
<data name="Main.Ribbon.Project.New.NewGroup" xml:space="preserve">
<value>New group</value>
</data>
<data name="Main.Ribbon.Project.New.NewSubgroup" xml:space="preserve">
<value>New subgroup</value>
</data>
<data name="Main.Ribbon.Project.New.NewTeacher" xml:space="preserve">
<value>New teacher</value>
</data>
<data name="Main.Ribbon.Project.Settings" xml:space="preserve">
<value>Settings</value>
</data>
<data name="Main.Ribbon.Project.Settings.ProjectSettings" xml:space="preserve">
<value>Project settings</value>
</data>
<data name="Main.Treeview.Classrooms" xml:space="preserve">
<value>Classrooms</value>
</data>
<data name="Main.Treeview.Classrooms.ContextMenu.Edit" xml:space="preserve">
<value>Edit classroom</value>
</data>
<data name="Main.Treeview.ContextMenu.EditTimetable" xml:space="preserve">
<value>Edit timetable</value>
</data>
<data name="Main.Treeview.ContextMenu.Remove" xml:space="preserve">
<value>Remove</value>
</data>
<data name="MessageBox.Error" xml:space="preserve">
<value>Error</value>
</data>
<data name="ProjectSettings.Author" xml:space="preserve">
<value>Author</value>
</data>
<data name="ProjectSettings.Basics" xml:space="preserve">
<value>Basics</value>
</data>
<data name="ProjectSettings.Days" xml:space="preserve">
<value>Days</value>
</data>
<data name="ProjectSettings.Description" xml:space="preserve">
<value>Description</value>
</data>
<data name="ProjectSettings.Hours" xml:space="preserve">
<value>Hours</value>
</data>
<data name="ProjectSettings.Message.FromHigherThanTo" xml:space="preserve">
<value>Length of the class have to be higher than 0 minutes</value>
</data>
<data name="ProjectSettings.Message.SlotCollision" xml:space="preserve">
<value>Slot collide with another slot</value>
</data>
<data name="ProjectSettings.Name" xml:space="preserve">
<value>Name</value>
</data>
<data name="ProjectSettings.TimetableModel" xml:space="preserve">
<value>Timetable model</value>
</data>
<data name="Tabs.ClassroomEdit" xml:space="preserve">
<value>Classroom editing</value>
</data>
<data name="Tabs.ProjectSettings" xml:space="preserve">
<value>Project settings</value>
</data>
<data name="Tabs.Welcome" xml:space="preserve">
<value>Welcome</value>
</data>
</root>

View File

@@ -0,0 +1,7 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:TimetableDesigner.Converters">
<c:ViewModelToViewConverter x:Key="ViewModelToViewConverter"/>
<c:IsNullToVisibilityConverter x:Key="IsNullToVisibilityConverter"/>
<c:IsNotNullToBooleanConverter x:Key="IsNotNullToBooleanConverter"/>
</ResourceDictionary>

View File

@@ -0,0 +1,16 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<BitmapImage x:Key="CloseImage" UriSource="Images/Close.png"/>
<BitmapImage x:Key="AddImage" UriSource="Images/Add.png"/>
<BitmapImage x:Key="RemoveImage" UriSource="Images/Remove.png"/>
<BitmapImage x:Key="ProjectImage" UriSource="Images/Project.png"/>
<BitmapImage x:Key="ProjectSettingsImage" UriSource="Images/ProjectSettings.png"/>
<BitmapImage x:Key="ClassroomImage" UriSource="Images/Classroom.png"/>
<BitmapImage x:Key="ClassroomAddImage" UriSource="Images/ClassroomAdd.png"/>
<BitmapImage x:Key="OpenImage" UriSource="Images/Open.png"/>
<BitmapImage x:Key="OpenRecentImage" UriSource="Images/OpenRecent.png"/>
<BitmapImage x:Key="SaveImage" UriSource="Images/Save.png"/>
<BitmapImage x:Key="SaveAsImage" UriSource="Images/SaveAs.png"/>
<BitmapImage x:Key="NewImage" UriSource="Images/New.png"/>
</ResourceDictionary>

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,103 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\Images\Add.png" />
<None Remove="Resources\Images\Classroom.png" />
<None Remove="Resources\Images\ClassroomAdd.png" />
<None Remove="Resources\Images\Close.png" />
<None Remove="Resources\Images\CloseButtonImage.png" />
<None Remove="Resources\Images\New.png" />
<None Remove="Resources\Images\Open.png" />
<None Remove="Resources\Images\OpenRecent.png" />
<None Remove="Resources\Images\Project.png" />
<None Remove="Resources\Images\ProjectSettings.png" />
<None Remove="Resources\Images\Remove.png" />
<None Remove="Resources\Images\Save.png" />
<None Remove="Resources\Images\SaveAs.png" />
</ItemGroup>
<ItemGroup>
<Content Include="Resources\Images\Classroom.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Images\ClassroomAdd.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Images\Close.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Images\New.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Images\Open.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Images\OpenRecent.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Images\Project.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Images\ProjectSettings.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Images\Remove.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Images\Save.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Images\SaveAs.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.0" />
<PackageReference Include="Fluent.Ribbon" Version="9.0.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TimetableDesigner.Core\TimetableDesigner.Core.csproj" />
<ProjectReference Include="..\TimetableDesigner.MessageBox\TimetableDesigner.MessageBox.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="Resources\Images\Add.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.pl-PL.Designer.cs">
<DependentUpon>Resources.pl-PL.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.pl-PL.resx">
<LastGenOutput>Resources.pl-PL.Designer.cs</LastGenOutput>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Media;
namespace TimetableDesigner.ViewModels.Base
{
public abstract class BaseTabViewModel : BaseViewModel
{
#region FIELDS
private ImageSource _tabImage;
private string _tabTitle;
private bool _isTabClosable;
#endregion
#region PROPERTIES
public ImageSource TabImage
{
get => _tabImage;
set
{
_tabImage = value;
NotifyPropertyChanged(nameof(TabImage));
}
}
public string TabTitle
{
get => _tabTitle;
set
{
_tabTitle = value;
NotifyPropertyChanged(nameof(TabTitle));
}
}
public bool IsTabClosable
{
get => _isTabClosable;
set
{
_isTabClosable = value;
NotifyPropertyChanged(nameof(IsTabClosable));
}
}
#endregion
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.ViewModels.Base
{
public abstract class BaseViewModel : INotifyPropertyChanged
{
#region PROTECTED METHODS
protected void NotifyPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
#endregion
#region EVENTS
public event PropertyChangedEventHandler? PropertyChanged;
#endregion
}
}

View File

@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.ViewModels.Base;
using TimetableDesigner.ViewModels.Models;
namespace TimetableDesigner.ViewModels
{
public class ClassroomEditTabViewModel : BaseTabViewModel
{
#region FIELDS
private ClassroomViewModel _classroom;
#endregion
#region PROPERTIES
public ClassroomViewModel Classroom
{
get => _classroom;
set
{
if (_classroom != value)
{
_classroom = value;
NotifyPropertyChanged(nameof(Classroom));
}
}
}
#endregion
#region CONSTRUCTORS
public ClassroomEditTabViewModel() : this(new ClassroomViewModel(new Core.Classroom()))
{ }
public ClassroomEditTabViewModel(ClassroomViewModel classroom)
{
_classroom = classroom;
}
#endregion
}
}

View File

@@ -0,0 +1,214 @@
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.Controls;
using System.Windows.Input;
using TimetableDesigner.Commands;
using TimetableDesigner.ViewModels.Base;
using System.Windows.Navigation;
using TimetableDesigner.Core;
using System.Windows;
using TimetableDesigner.Properties;
using TimetableDesigner.MessageBox;
using System.Reflection;
using TimetableDesigner.ViewModels.Models;
using System.Windows.Data;
namespace TimetableDesigner.ViewModels
{
class MainViewModel : BaseViewModel
{
#region FIELDS
private ProjectViewModel? _project;
private ObservableCollection<BaseTabViewModel> _tabs;
private BaseTabViewModel? _selectedTab;
#endregion
#region PROPERTIES
public string Version { get; set; }
public ICommand CloseTabCommand { get; set; }
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 ObservableCollection<BaseTabViewModel> Tabs
{
get => _tabs;
set
{
_tabs = value;
NotifyPropertyChanged(nameof(Tabs));
}
}
public BaseTabViewModel? SelectedTab
{
get => _selectedTab;
set
{
_selectedTab = value;
NotifyPropertyChanged(nameof(SelectedTab));
}
}
public ProjectViewModel? Project
{
get => _project;
set
{
if (value != _project)
{
_project = value;
NotifyPropertyChanged(nameof(Project));
}
}
}
#endregion
#region CONSTRUCTORS
public MainViewModel()
{
Version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
CloseTabCommand = new RelayCommand<BaseTabViewModel>(CloseTab);
NewProjectCommand = new RelayCommand<object>(param => NewProject());
OpenProjectCommand = new RelayCommand<object>(param => OpenProject());
ProjectSettingsCommand = new RelayCommand<object>(param => ProjectSettings());
NewClassroomCommand = new RelayCommand<object>(param => NewClassroom());
EditClassroomCommand = new RelayCommand<ClassroomViewModel>(EditClassroom);
RemoveClassroomCommand = new RelayCommand<ClassroomViewModel>(DeleteClassroom);
_tabs = new ObservableCollection<BaseTabViewModel>
{
new WelcomeTabViewModel() { TabTitle = Resources.Tabs_Welcome }
};
_selectedTab = Tabs.Last();
}
#endregion
#region PUBLIC METHODS
private void CloseTab(BaseTabViewModel tab)
{
if (SelectedTab == tab)
{
int index = Tabs.IndexOf(tab);
int before = index - 1;
int after = index + 1;
BaseTabViewModel newSelectedTab = null;
if (before >= 0)
{
newSelectedTab = Tabs[before];
}
else if (after >= 0 && after < Tabs.Count - 1)
{
newSelectedTab = Tabs[after];
}
SelectedTab = newSelectedTab;
}
Tabs.Remove(tab);
}
private void OpenProject()
{
}
private void NewProject()
{
if (Project is not null)
{
MessageBoxYesNoCancelResult result = MessageBoxService.ShowYesNoCancelQuestion(Resources.Main_Message_SaveCurrentProject);
switch (result)
{
case MessageBoxYesNoCancelResult.Yes: break;
case MessageBoxYesNoCancelResult.No: break;
default: return;
}
}
Tabs.Clear();
Project project = new Project()
{
Name = Resources.Global_DefaultProjectName,
};
Project = new ProjectViewModel(project);
ProjectSettings();
}
private void ProjectSettings()
{
if (Project is not null)
{
ProjectSettingsTabViewModel projectSettingsTabVM = new ProjectSettingsTabViewModel()
{
Project = Project,
TabTitle = Resources.Tabs_ProjectSettings,
IsTabClosable = true,
};
Tabs.Add(projectSettingsTabVM);
SelectedTab = Tabs.Last();
}
}
private void NewClassroom()
{
if (Project is not null)
{
Classroom classroom = new Classroom()
{
Name = Resources.Global_DefaultClassroomName
};
ClassroomViewModel classroomVM = new ClassroomViewModel(classroom);
Project.Classrooms.Add(classroomVM);
EditClassroom(classroomVM);
}
}
private void EditClassroom(ClassroomViewModel classroomViewModel)
{
Debug.WriteLine("textedit");
ClassroomEditTabViewModel classroomEdit = new ClassroomEditTabViewModel()
{
Classroom = classroomViewModel,
TabTitle = $"{Resources.Tabs_ClassroomEdit}: {classroomViewModel.Name}",
IsTabClosable = true
};
Tabs.Add(classroomEdit);
SelectedTab = classroomEdit;
}
private void DeleteClassroom(ClassroomViewModel classroomViewModel)
{
Debug.WriteLine("textdelete");
if (Project is not null)
{
Project.Classrooms.Remove(classroomViewModel);
}
}
#endregion
}
}

View File

@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.Core;
using TimetableDesigner.ViewModels.Base;
namespace TimetableDesigner.ViewModels.Models
{
public class ClassroomViewModel : BaseViewModel
{
#region FIELDS
private Classroom _classroom;
#endregion
#region PROPERTIES
public Classroom Classroom => _classroom;
public string Name
{
get => _classroom.Name;
set
{
if (_classroom.Name != value)
{
_classroom.Name = value;
NotifyPropertyChanged(nameof(Name));
}
}
}
public string Description
{
get => _classroom.Description;
set
{
if (_classroom.Description != value)
{
_classroom.Description = value;
NotifyPropertyChanged(nameof(Description));
}
}
}
public bool IsCapacityLimited
{
get => _classroom.IsCapacityLimited;
set
{
if (_classroom.IsCapacityLimited != value)
{
_classroom.IsCapacityLimited = value;
NotifyPropertyChanged(nameof(IsCapacityLimited));
}
}
}
public uint Capacity
{
get => _classroom.Capacity;
set
{
if (_classroom.Capacity != value)
{
_classroom.Capacity = value;
NotifyPropertyChanged(nameof(Capacity));
}
}
}
#endregion
#region CONSTRUCTORS
public ClassroomViewModel(Classroom classroom)
{
_classroom = classroom;
}
#endregion
}
}

View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.Core;
using TimetableDesigner.ViewModels.Base;
namespace TimetableDesigner.ViewModels.Models
{
public class ProjectViewModel : BaseViewModel
{
#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<ClassroomViewModel> Classrooms { get; set; }
#endregion
#region CONSTRUCTORS
public ProjectViewModel(Project project)
{
_project = project;
TimetableTemplate = new TimetableTemplateViewModel(_project.TimetableTemplate);
Classrooms = new ObservableCollection<ClassroomViewModel>();
Classrooms.CollectionChanged += Classrooms_CollectionChanged;
}
#endregion
#region PRIVATE METHODS
private void Classrooms_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
IList<ClassroomViewModel>? added = e.NewItems as IList<ClassroomViewModel>;
IList<ClassroomViewModel>? removed = e.OldItems as IList<ClassroomViewModel>;
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);
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.Core;
using TimetableDesigner.ViewModels.Base;
namespace TimetableDesigner.ViewModels.Models
{
public class TeacherViewModel : BaseViewModel
{
#region FIELDS
private Teacher _teacher;
#endregion
#region PROPERTIES
public Teacher Teacher => _teacher;
public string Name
{
get => _teacher.Name;
set
{
if (_teacher.Name != value)
{
_teacher.Name = value;
NotifyPropertyChanged(nameof(Name));
}
}
}
public string Description
{
get => _teacher.Description;
set
{
if (_teacher.Description != value)
{
_teacher.Description = value;
NotifyPropertyChanged(nameof(Description));
}
}
}
#endregion
#region CONSTRUCTORS
public TeacherViewModel(Teacher teacher)
{
_teacher = teacher;
}
#endregion
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.Core;
using TimetableDesigner.ViewModels.Base;
namespace TimetableDesigner.ViewModels.Models
{
public class TimetableTemplateViewModel : BaseViewModel
{
#region FIELDS
private TimetableTemplate _timetableTemplate;
#endregion
#region PROPERTIES
public TimetableTemplate TimetableTemplate => _timetableTemplate;
public ObservableCollection<TimetableDay> Days => new ObservableCollection<TimetableDay>(_timetableTemplate.Days);
public ObservableCollection<TimetableSlot> Slots => new ObservableCollection<TimetableSlot>(_timetableTemplate.Slots);
#endregion
#region CONSTRUCTORS
public TimetableTemplateViewModel(TimetableTemplate timetableTemplate)
{
_timetableTemplate = timetableTemplate;
}
#endregion
#region PUBLIC METHODS
public void AddDay(TimetableDay day)
{
_timetableTemplate.AddDay(day);
NotifyPropertyChanged(nameof(Days));
}
public void RemoveDay(TimetableDay day)
{
_timetableTemplate.RemoveDay(day);
NotifyPropertyChanged(nameof(Days));
}
public void AddSlot(TimetableSlot slot)
{
_timetableTemplate.AddSlot(slot);
NotifyPropertyChanged(nameof(Slots));
}
public void RemoveSlot(TimetableSlot slot)
{
_timetableTemplate.RemoveSlot(slot);
NotifyPropertyChanged(nameof(Slots));
}
#endregion
}
}

View File

@@ -0,0 +1,140 @@
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;
using System.Windows.Input;
using TimetableDesigner.Commands;
using TimetableDesigner.Core;
using TimetableDesigner.MessageBox;
using TimetableDesigner.Properties;
using TimetableDesigner.ViewModels.Base;
using TimetableDesigner.ViewModels.Models;
namespace TimetableDesigner.ViewModels
{
public class ProjectSettingsTabViewModel : BaseTabViewModel
{
#region FIELDS
private string _newDayName;
private DateTime? _newSlotFrom;
private DateTime? _newSlotTo;
#endregion
#region PROPERTIES
public ICommand AddDayCommand { get; set; }
public ICommand AddSlotCommand { get; set; }
public ICommand RemoveDayCommand { get; set; }
public ICommand RemoveSlotCommand { get; set; }
public ProjectViewModel? Project { get; set; }
public string NewDayName
{
get => _newDayName;
set
{
_newDayName = value;
NotifyPropertyChanged(nameof(NewDayName));
}
}
public DateTime? NewSlotFrom
{
get => _newSlotFrom;
set
{
_newSlotFrom = value;
NotifyPropertyChanged(nameof(NewSlotFrom));
}
}
public DateTime? NewSlotTo
{
get => _newSlotTo;
set
{
_newSlotTo = value;
NotifyPropertyChanged(nameof(NewSlotTo));
}
}
#endregion
#region CONSTRUCTORS
public ProjectSettingsTabViewModel()
{
Project = new ProjectViewModel(new Project());
AddDayCommand = new RelayCommand<object>(param => AddDay());
AddSlotCommand = new RelayCommand<object>(param => AddSlot());
RemoveDayCommand = new RelayCommand<TimetableDay>(RemoveDay);
RemoveSlotCommand = new RelayCommand<TimetableSlot>(RemoveSlot);
_newDayName = string.Empty;
_newSlotFrom = new DateTime(1, 1, 1, 8, 0, 0);
_newSlotTo = new DateTime(1, 1, 1, 9, 0, 0);
}
#endregion
#region PRIVATE METHODS
private void AddDay()
{
if (!string.IsNullOrWhiteSpace(NewDayName))
{
Project.TimetableTemplate.AddDay(new TimetableDay(NewDayName));
NewDayName = string.Empty;
}
}
private void RemoveDay(TimetableDay day) => Project.TimetableTemplate.RemoveDay(day);
private void AddSlot()
{
if (NewSlotFrom.HasValue && NewSlotTo.HasValue)
{
TimeOnly from = new TimeOnly(NewSlotFrom.Value.Hour, NewSlotFrom.Value.Minute);
TimeOnly to = new TimeOnly(NewSlotTo.Value.Hour, NewSlotTo.Value.Minute);
if (from >= to)
{
MessageBoxService.ShowError(Resources.ProjectSettings_Message_FromHigherThanTo);
return;
}
try
{
Project.TimetableTemplate.AddSlot(new TimetableSlot(from, to));
double delta = (to - from).TotalMinutes;
DateTime newFrom = NewSlotTo.Value;
DateTime newTo = NewSlotTo.Value.AddMinutes(delta);
NewSlotFrom = newFrom;
NewSlotTo = newTo;
}
catch (ArgumentException)
{
MessageBoxService.ShowError(Resources.ProjectSettings_Message_SlotCollision);
}
}
}
private void RemoveSlot(TimetableSlot slot) => Project.TimetableTemplate.RemoveSlot(slot);
#endregion
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.ViewModels.Base;
namespace TimetableDesigner.ViewModels
{
public class TeacherEditTabViewModel : BaseViewModel
{
}
}

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.ViewModels.Base;
namespace TimetableDesigner.ViewModels
{
public class WelcomeTabViewModel : BaseTabViewModel
{
#region CONSTRUCTORS
public WelcomeTabViewModel()
{ }
#endregion
}
}

View File

@@ -0,0 +1,35 @@
<UserControl x:Class="TimetableDesigner.Views.ClassroomEditTabView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
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"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<vm:ClassroomEditTabViewModel/>
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="{x:Static p:Resources.ClassroomEdit_Name}"/>
<TextBox Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="0" Margin="5" Text="{Binding Classroom.Name}" MaxLines="1"/>
<Label Grid.Column="0" Grid.Row="1" Content="{x:Static p:Resources.ClassroomEdit_Description}"/>
<TextBox Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="1" Margin="5" MinLines="3" Text="{Binding Classroom.Description}"/>
<Label Grid.Column="0" Grid.Row="2" Content="{x:Static p:Resources.ClassroomEdit_Capacity}"/>
<xctk:IntegerUpDown Grid.Column="1" Grid.Row="2" Margin="5" Minimum="1" IsEnabled="{Binding Classroom.IsCapacityLimited}" Value="{Binding Classroom.Capacity}"/>
<CheckBox Grid.Column="2" Grid.Row="2" Margin="5" Content="{x:Static p:Resources.ClassroomEdit_CapacityIsLimited}" IsChecked="{Binding Classroom.IsCapacityLimited}" VerticalAlignment="Center"/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
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;
namespace TimetableDesigner.Views
{
/// <summary>
/// Interaction logic for ClassroomEditTabView.xaml
/// </summary>
public partial class ClassroomEditTabView : UserControl
{
public ClassroomEditTabView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,131 @@
<rib:RibbonWindow x:Class="TimetableDesigner.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:TimetableDesigner.ViewModels"
xmlns:vmm="clr-namespace:TimetableDesigner.ViewModels.Models"
xmlns:p = "clr-namespace:TimetableDesigner.Properties"
xmlns:rib="urn:fluent-ribbon"
Height="450"
Width="800">
<rib:RibbonWindow.Title>
<MultiBinding StringFormat="Timetable Designer {0} ({1})">
<Binding Path="Version"/>
<Binding Path="Project.Name" FallbackValue="{x:Static p:Resources.Global_NoProjectLoadedTitle}"/>
</MultiBinding>
</rib:RibbonWindow.Title>
<rib:RibbonWindow.DataContext>
<vm:MainViewModel/>
</rib:RibbonWindow.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<rib:Ribbon Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3">
<rib:RibbonTabItem Header="{x:Static p:Resources.Main_Ribbon_File}">
<rib:RibbonGroupBox Header="{x:Static p:Resources.Main_Ribbon_File_NewOpen}">
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_File_NewOpen_New}" Icon="{StaticResource NewImage}" Command="{Binding NewProjectCommand}"/>
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_File_NewOpen_Open}" Icon="{StaticResource OpenImage}" Command="{Binding OpenProjectCommand}"/>
<rib:DropDownButton Header="{x:Static p:Resources.Main_Ribbon_File_NewOpen_OpenRecent}" Icon="{StaticResource OpenRecentImage}"/>
</rib:RibbonGroupBox>
<rib:RibbonGroupBox Header="{x:Static p:Resources.Main_Ribbon_File_Save}" IsEnabled="{Binding Project, Converter={StaticResource IsNotNullToBooleanConverter}}">
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_File_Save_Save}" Icon="{StaticResource SaveImage}"/>
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_File_Save_SaveAs}" Icon="{StaticResource SaveAsImage}"/>
</rib:RibbonGroupBox>
</rib:RibbonTabItem>
<rib:RibbonTabItem Header="{x:Static p:Resources.Main_Ribbon_Project}" IsEnabled="{Binding Project, Converter={StaticResource IsNotNullToBooleanConverter}}">
<rib:RibbonGroupBox Header="{x:Static p:Resources.Main_Ribbon_Project_Settings}">
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_Settings_ProjectSettings}"
Command="{Binding ProjectSettingsCommand}"
Icon="{StaticResource ProjectSettingsImage}"/>
</rib:RibbonGroupBox>
<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_NewTeacher}"/>
<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_NewClass}"/>
</rib:RibbonGroupBox>
</rib:RibbonTabItem>
</rib:Ribbon>
<TreeView Grid.Row="1" Grid.Column="0" DataContext="{Binding Project}" Visibility="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=rib:RibbonWindow}, Path=DataContext.Project, Converter={StaticResource IsNullToVisibilityConverter}}">
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource ProjectImage}" Width="18" Height="18"/>
<Label Content="{Binding Name}"/>
</StackPanel>
</TreeViewItem.Header>
<TreeViewItem.InputBindings>
<MouseBinding Gesture="LeftDoubleClick"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=rib:RibbonWindow}, Path=DataContext.ProjectSettingsCommand}"/>
</TreeViewItem.InputBindings>
<TreeViewItem ItemsSource="{Binding Classrooms}">
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource ClassroomImage}" Width="18" Height="18"/>
<Label Content="{x:Static p:Resources.Main_Treeview_Classrooms}"/>
</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"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=rib:RibbonWindow}, Path=DataContext.EditClassroomCommand}"
CommandParameter="{Binding}"/>
</TreeViewItem.InputBindings>
<TreeViewItem.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.Main_Treeview_ContextMenu_EditTimetable}"/>
<MenuItem Header="{x:Static p:Resources.Main_Treeview_Classrooms_ContextMenu_Edit}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.Tag.EditClassroomCommand}"
CommandParameter="{Binding}"/>
<Separator/>
<MenuItem Header="{x:Static p:Resources.Main_Treeview_ContextMenu_Remove}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.Tag.RemoveClassroomCommand}"
CommandParameter="{Binding}"/>
</ContextMenu>
</TreeViewItem.ContextMenu>
</TreeViewItem>
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
</TreeViewItem>
</TreeView>
<GridSplitter Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Stretch" ShowsPreview="True" Width="2"/>
<TabControl Grid.Row="1" Grid.Column="2" ItemsSource="{Binding Tabs}" SelectedItem="{Binding SelectedTab}">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding TabImage}"
Visibility="{Binding TabImage, Converter={StaticResource IsNullToVisibilityConverter}}"
Width="15"
Height="15"/>
<TextBlock Text="{Binding TabTitle}"/>
<Button BorderThickness="0"
Background="Transparent"
Margin="5,0,0,0"
Visibility="{Binding IsTabClosable, Converter={StaticResource boolToVisibilityConverter}}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=DataContext.CloseTabCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=DataContext}">
<Image Source="{StaticResource CloseImage}"
Width="15"
Height="15"/>
</Button>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=DataContext.SelectedTab, UpdateSourceTrigger = PropertyChanged, Converter={StaticResource ViewModelToViewConverter}}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</rib:RibbonWindow>

View File

@@ -0,0 +1,26 @@
using Fluent;
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;
namespace TimetableDesigner.Views
{
public partial class MainWindow : RibbonWindow
{
public MainWindow()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,124 @@
<UserControl x:Class="TimetableDesigner.Views.ProjectSettingsTabView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:TimetableDesigner.ViewModels"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:p = "clr-namespace:TimetableDesigner.Properties"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<vm:ProjectSettingsTabViewModel/>
</UserControl.DataContext>
<ScrollViewer>
<StackPanel>
<GroupBox Header="{x:Static p:Resources.ProjectSettings_Basics}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="{x:Static p:Resources.ProjectSettings_Name}"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Project.Name}" VerticalAlignment="Center"/>
<Label Grid.Row="1" Grid.Column="0" Content="{x:Static p:Resources.ProjectSettings_Author}"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Project.Author}" VerticalAlignment="Center"/>
<Label Grid.Row="2" Grid.Column="0" Content="{x:Static p:Resources.ProjectSettings_Description}"/>
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Project.Description}" VerticalAlignment="Center" MinLines="5"/>
</Grid>
</GroupBox>
<GroupBox Header="{x:Static p:Resources.ProjectSettings_TimetableModel}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Content="{x:Static p:Resources.ProjectSettings_Days}"/>
<Border Grid.Row="1" Grid.Column="0" Margin="5" BorderThickness="1" BorderBrush="DarkGray">
<ItemsControl ItemsSource="{Binding Project.TimetableTemplate.Days}" HorizontalContentAlignment="Stretch" MinHeight="100">
<ItemsControl.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>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
<Grid Grid.Row="2" Grid.Column="0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="AddDayTextBox" Grid.Column="0" Margin="5" Text="{Binding NewDayName}" MaxLines="1"/>
<Button Grid.Column="1" Margin="5" Command="{Binding AddDayCommand}" Width="26" Height="26">
<Image Source="{StaticResource AddImage}" Width="20" Height="20"/>
</Button>
</Grid>
<Label Grid.Row="0" Grid.Column="1" Content="{x:Static p:Resources.ProjectSettings_Hours}"/>
<Border Grid.Row="1" Grid.Column="1" Margin="5" BorderThickness="1" BorderBrush="DarkGray">
<ItemsControl ItemsSource="{Binding Project.TimetableTemplate.Slots}" HorizontalContentAlignment="Stretch" MinHeight="100">
<ItemsControl.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.RemoveSlotCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=DataContext}">
<Image Source="{StaticResource RemoveImage}" Width="20" Height="20"/>
</Button>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
<Grid Grid.Row="2" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<xctk:DateTimeUpDown Grid.Column="0" Margin="5" Format="ShortTime" Value="{Binding NewSlotFrom}"/>
<Label Grid.Column="1" Margin="5" Content="-"/>
<xctk:DateTimeUpDown Grid.Column="2" Margin="5" Format="ShortTime" Value="{Binding NewSlotTo}"/>
<Button Grid.Column="3" Margin="5" Command="{Binding AddSlotCommand}" Width="26" Height="26">
<Image Source="{StaticResource AddImage}" Width="20" Height="20"/>
</Button>
</Grid>
</Grid>
</GroupBox>
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,25 @@
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;
namespace TimetableDesigner.Views
{
public partial class ProjectSettingsTabView : UserControl
{
public ProjectSettingsTabView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,12 @@
<UserControl x:Class="TimetableDesigner.Views.TeacherEditTabView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TimetableDesigner.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
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;
namespace TimetableDesigner.Views
{
/// <summary>
/// Interaction logic for TeacherEditTabView.xaml
/// </summary>
public partial class TeacherEditTabView : UserControl
{
public TeacherEditTabView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,14 @@
<UserControl x:Class="TimetableDesigner.Views.WelcomeTabView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:TimetableDesigner.ViewModels"
mc:Ignorable="d">
<UserControl.DataContext>
<vm:WelcomeTabViewModel/>
</UserControl.DataContext>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Label Content="Open or create new project to begin"/>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,28 @@
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;
namespace TimetableDesigner.Views
{
/// <summary>
/// Interaction logic for WelcomeTabView.xaml
/// </summary>
public partial class WelcomeTabView : UserControl
{
public WelcomeTabView()
{
InitializeComponent();
}
}
}