group and subgroup adding/editing, services added

This commit is contained in:
2023-03-26 23:01:58 +02:00
Unverified
parent 3cc4ea5b4b
commit 6e34ed1ee7
70 changed files with 2310 additions and 853 deletions

View File

@@ -11,10 +11,23 @@ namespace TimetableDesigner.Core
#region PROPERTIES #region PROPERTIES
public string Name { get; set; } public string Name { get; set; }
public string Description { get; set; } public Teacher? Teacher { get; set; }
public Teacher Teacher { get; set; } public IGroup? Group { get; set; }
public Subgroup Subgroup { get; set; } public Classroom? Classroom { get; set; }
public Classroom Classroom { get; set; }
#endregion
#region CONSTRUCTORS
public Class()
{
Name = string.Empty;
Teacher = null;
Group = null;
Classroom = null;
}
#endregion #endregion
} }

View File

@@ -6,15 +6,27 @@ using System.Threading.Tasks;
namespace TimetableDesigner.Core namespace TimetableDesigner.Core
{ {
public class Group public class Group : IGroup
{ {
#region PROPERTIES #region PROPERTIES
public string Name { get; set; } public string Name { get; set; }
public string Description { get; set; } public string Description { get; set; }
public Subgroup MainSubgroup { get; set; }
public ICollection<Subgroup> AssignedSubgroups { get; set; } public ICollection<Subgroup> AssignedSubgroups { get; set; }
#endregion #endregion
#region CONSTRUCTORS
public Group()
{
Name = string.Empty;
Description = string.Empty;
AssignedSubgroups = new HashSet<Subgroup>();
}
#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
{
public interface IGroup
{
#region PROPERTIES
string Name { get; }
#endregion
}
}

View File

@@ -33,8 +33,10 @@ namespace TimetableDesigner.Core
Author = string.Empty; Author = string.Empty;
Description = string.Empty; Description = string.Empty;
TimetableTemplate = new TimetableTemplate(); TimetableTemplate = new TimetableTemplate();
Classrooms = new List<Classroom>(); Classrooms = new HashSet<Classroom>();
Teachers = new List<Teacher>(); Teachers = new HashSet<Teacher>();
Groups = new HashSet<Group>();
Subgroups = new HashSet<Subgroup>();
} }
#endregion #endregion

View File

@@ -6,12 +6,22 @@ using System.Threading.Tasks;
namespace TimetableDesigner.Core namespace TimetableDesigner.Core
{ {
public class Subgroup public class Subgroup : IGroup
{ {
#region PROPERTIES #region PROPERTIES
public string Name { get; set; } public string Name { get; set; }
public string Description { get; set; }
#endregion
#region CONSTRUCTORS
public Subgroup()
{
Name = string.Empty;
}
#endregion #endregion
} }

View File

@@ -33,29 +33,29 @@ namespace TimetableDesigner.Core
#region PUBLIC METHODS #region PUBLIC METHODS
internal TimetableSpansCollision CheckCollision(TimetableSpan slot) internal TimetableSpanCollision CheckCollision(TimetableSpan slot)
{ {
if (slot.To <= this.From) if (slot.To <= this.From)
{ {
return TimetableSpansCollision.CheckedSlotBefore; return TimetableSpanCollision.CheckedSlotBefore;
} }
else if (this.To <= slot.From) else if (this.To <= slot.From)
{ {
return TimetableSpansCollision.CheckedSlotAfter; return TimetableSpanCollision.CheckedSlotAfter;
} }
else else
{ {
if (this.From < slot.From && slot.To < this.To) if (this.From < slot.From && slot.To < this.To)
{ {
return TimetableSpansCollision.CheckedSlotIn; return TimetableSpanCollision.CheckedSlotIn;
} }
else if (this.From < slot.From && slot.From < this.To && this.To < slot.To) else if (this.From < slot.From && slot.From < this.To && this.To < slot.To)
{ {
return TimetableSpansCollision.CheckedSlotFromIn; return TimetableSpanCollision.CheckedSlotFromIn;
} }
else if (slot.From < this.From && this.From < slot.To && slot.To < this.To) else if (slot.From < this.From && this.From < slot.To && slot.To < this.To)
{ {
return TimetableSpansCollision.CheckedSlotToIn; return TimetableSpanCollision.CheckedSlotToIn;
} }
else else
{ {

View File

@@ -49,8 +49,8 @@ namespace TimetableDesigner.Core
{ {
switch (item.CheckCollision(_list.ElementAt(i))) switch (item.CheckCollision(_list.ElementAt(i)))
{ {
case TimetableSpansCollision.CheckedSlotBefore: i++; break; case TimetableSpanCollision.CheckedSlotBefore: i++; break;
case TimetableSpansCollision.CheckedSlotAfter: done ^= true; break; case TimetableSpanCollision.CheckedSlotAfter: done ^= true; break;
default: throw new ArgumentException("Slot collide with another slot"); default: throw new ArgumentException("Slot collide with another slot");
} }
} }

View File

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

View File

@@ -7,7 +7,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace TimetableDesigner.Customs.Collections namespace TimetableDesigner.Customs
{ {
public class ObservableDictionary<TKey, TValue> : ObservableCollection<ObservableKeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue> public class ObservableDictionary<TKey, TValue> : ObservableCollection<ObservableKeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue>
{ {
@@ -59,7 +59,7 @@ namespace TimetableDesigner.Customs.Collections
{ {
foreach (KeyValuePair<TKey, TValue> pair in dictionary) foreach (KeyValuePair<TKey, TValue> pair in dictionary)
{ {
this.Add(pair); Add(pair);
} }
} }

View File

@@ -5,7 +5,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace TimetableDesigner.Customs.Collections namespace TimetableDesigner.Customs
{ {
public class ObservableKeyValuePair<TKey, TValue> : INotifyPropertyChanged public class ObservableKeyValuePair<TKey, TValue> : INotifyPropertyChanged
{ {

View File

@@ -5,11 +5,11 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace TimetableDesigner.ViewModels.Base namespace TimetableDesigner.Customs
{ {
public abstract class BaseViewModel : INotifyPropertyChanged public class ObservableObject : INotifyPropertyChanged
{ {
#region PROTECTED METHODS #region PRIVATE METHODS
protected void NotifyPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); protected void NotifyPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

View File

@@ -1,33 +0,0 @@
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

@@ -1,81 +0,0 @@
//------------------------------------------------------------------------------
// <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

@@ -1,126 +0,0 @@
<?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

@@ -1,24 +0,0 @@
<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

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

View File

@@ -1,10 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Configuration; using System.Configuration;
using System.Data; using System.Data;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using TimetableDesigner.Services;
using TimetableDesigner.Services.MessageBox;
using TimetableDesigner.Services.Project;
using TimetableDesigner.Services.TabNavigation;
namespace TimetableDesigner namespace TimetableDesigner
{ {
@@ -12,6 +17,9 @@ namespace TimetableDesigner
{ {
public App() public App()
{ {
ServiceProvider.Instance.AddService<IMessageBoxService>(new MessageBoxService());
ServiceProvider.Instance.AddService<ITabNavigationService>(new TabNavigationService());
ServiceProvider.Instance.AddService<IProjectService>(new ProjectService());
} }
} }
} }

View File

@@ -0,0 +1,34 @@
<UserControl x:Class="TimetableDesigner.Controls.TimetableEditorControl"
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.Controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ItemsControl Grid.Column="0" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=_projectService.ProjectViewModel}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Grid.Column="0"
Rows="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=_projectService.ProjectViewModel.TimetableTemplate.Slots.Count, Converter={StaticResource IntegerAddConstantConverter}, ConverterParameter=1}"
Columns="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=_projectService.ProjectViewModel.TimetableTemplate.Days.Count, Converter={StaticResource IntegerAddConstantConverter}, ConverterParameter=1}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ScrollViewer Grid.Column="1">
<StackPanel>
</StackPanel>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using TimetableDesigner.Services;
using TimetableDesigner.Services.Project;
using TimetableDesigner.ViewModels.Models;
namespace TimetableDesigner.Controls
{
public partial class TimetableEditorControl : UserControl
{
#region FIELDS
private IProjectService _projectService;
#endregion
#region PROPERTIES
public IGroupViewModel Group
{
get => (IGroupViewModel)GetValue(GroupProperty);
set => SetValue(GroupProperty, value);
}
public static readonly DependencyProperty GroupProperty = DependencyProperty.Register("Group", typeof(IGroupViewModel), typeof(TimetableEditorControl));
#endregion
#region CONSTRUCTORS
public TimetableEditorControl()
{
_projectService = ServiceProvider.Instance.GetService<IProjectService>();
InitializeComponent();
}
#endregion
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace TimetableDesigner.Converters
{
public class IntegerAddConstantConverter : IValueConverter
{
#region PUBLIC METHODS
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => (int)value + (int)parameter;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => (int)value - (int)parameter;
#endregion
}
}

View File

@@ -8,7 +8,7 @@ using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Data; using System.Windows.Data;
using TimetableDesigner.ViewModels; using TimetableDesigner.ViewModels;
using TimetableDesigner.ViewModels.Base; using TimetableDesigner.ViewModels.Views;
using TimetableDesigner.Views; using TimetableDesigner.Views;
namespace TimetableDesigner.Converters namespace TimetableDesigner.Converters
@@ -20,10 +20,11 @@ namespace TimetableDesigner.Converters
private static readonly Dictionary<Type, Type> _viewModelViewPairs = new Dictionary<Type, Type> private static readonly Dictionary<Type, Type> _viewModelViewPairs = new Dictionary<Type, Type>
{ {
{ typeof(MainViewModel), typeof(MainWindow) }, { typeof(MainViewModel), typeof(MainWindow) },
{ typeof(WelcomeTabViewModel), typeof(WelcomeTabView) }, { typeof(WelcomeViewModel), typeof(WelcomeView) },
{ typeof(ProjectSettingsTabViewModel), typeof(ProjectSettingsTabView) }, { typeof(ProjectSettingsViewModel), typeof(ProjectSettingsView) },
{ typeof(ClassroomEditTabViewModel), typeof(ClassroomEditTabView) }, { typeof(ClassroomEditViewModel), typeof(ClassroomEditView) },
{ typeof(TeacherEditTabViewModel), typeof(TeacherEditTabView) }, { typeof(TeacherEditViewModel), typeof(TeacherEditView) },
{ typeof(GroupEditViewModel), typeof(GroupEditView) },
}; };
#endregion #endregion
@@ -34,7 +35,7 @@ namespace TimetableDesigner.Converters
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{ {
BaseViewModel? viewModel = value as BaseViewModel; BaseViewViewModel? viewModel = value as BaseViewViewModel;
if (viewModel is not null) if (viewModel is not null)
{ {
Type view = _viewModelViewPairs[viewModel.GetType()]; Type view = _viewModelViewPairs[viewModel.GetType()];

View File

@@ -87,6 +87,24 @@ namespace TimetableDesigner.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Basics.
/// </summary>
public static string ClassroomEdit_Groups_Basics {
get {
return ResourceManager.GetString("ClassroomEdit.Groups.Basics", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Capacity.
/// </summary>
public static string ClassroomEdit_Groups_Capacity {
get {
return ResourceManager.GetString("ClassroomEdit.Groups.Capacity", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Name. /// Looks up a localized string similar to Name.
/// </summary> /// </summary>
@@ -105,6 +123,15 @@ namespace TimetableDesigner.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to New group.
/// </summary>
public static string Global_DefaultGroupName {
get {
return ResourceManager.GetString("Global.DefaultGroupName", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to New project. /// Looks up a localized string similar to New project.
/// </summary> /// </summary>
@@ -132,6 +159,78 @@ namespace TimetableDesigner.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Delete.
/// </summary>
public static string GroupEdit_DeleteSubgroup {
get {
return ResourceManager.GetString("GroupEdit.DeleteSubgroup", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Description.
/// </summary>
public static string GroupEdit_Description {
get {
return ResourceManager.GetString("GroupEdit.Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Basics.
/// </summary>
public static string GroupEdit_Groups_Basics {
get {
return ResourceManager.GetString("GroupEdit.Groups.Basics", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Subgroups.
/// </summary>
public static string GroupEdit_Groups_Subgroups {
get {
return ResourceManager.GetString("GroupEdit.Groups.Subgroups", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Are you sure you want to delete this subgroup. This operation is irreversible. Subgroup will be unassigned for all groups..
/// </summary>
public static string GroupEdit_Message_SubgroupDelete {
get {
return ResourceManager.GetString("GroupEdit.Message.SubgroupDelete", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Name.
/// </summary>
public static string GroupEdit_Name {
get {
return ResourceManager.GetString("GroupEdit.Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New subgroup name.
/// </summary>
public static string GroupEdit_NewSubgroupLabel {
get {
return ResourceManager.GetString("GroupEdit.NewSubgroupLabel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Subgroups.
/// </summary>
public static string GroupEdit_Subgroups {
get {
return ResourceManager.GetString("GroupEdit.Subgroups", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Project is loaded. Do you want to save?. /// Looks up a localized string similar to Project is loaded. Do you want to save?.
/// </summary> /// </summary>
@@ -141,6 +240,42 @@ namespace TimetableDesigner.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Export.
/// </summary>
public static string Main_Ribbon_Export {
get {
return ResourceManager.GetString("Main.Ribbon.Export", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Timetable.
/// </summary>
public static string Main_Ribbon_Export_Timetable {
get {
return ResourceManager.GetString("Main.Ribbon.Export.Timetable", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to PDF.
/// </summary>
public static string Main_Ribbon_Export_Timetable_PDF {
get {
return ResourceManager.GetString("Main.Ribbon.Export.Timetable.PDF", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to XLS.
/// </summary>
public static string Main_Ribbon_Export_Timetable_XLS {
get {
return ResourceManager.GetString("Main.Ribbon.Export.Timetable.XLS", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to File. /// Looks up a localized string similar to File.
/// </summary> /// </summary>
@@ -222,6 +357,42 @@ namespace TimetableDesigner.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Import.
/// </summary>
public static string Main_Ribbon_Project_Import {
get {
return ResourceManager.GetString("Main.Ribbon.Project.Import", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Import classrooms.
/// </summary>
public static string Main_Ribbon_Project_Import_ImportClassrooms {
get {
return ResourceManager.GetString("Main.Ribbon.Project.Import.ImportClassrooms", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Import groups.
/// </summary>
public static string Main_Ribbon_Project_Import_ImportGroups {
get {
return ResourceManager.GetString("Main.Ribbon.Project.Import.ImportGroups", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Import teachers.
/// </summary>
public static string Main_Ribbon_Project_Import_ImportTeachers {
get {
return ResourceManager.GetString("Main.Ribbon.Project.Import.ImportTeachers", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to New. /// Looks up a localized string similar to New.
/// </summary> /// </summary>
@@ -330,6 +501,33 @@ namespace TimetableDesigner.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Groups.
/// </summary>
public static string Main_Treeview_Groups {
get {
return ResourceManager.GetString("Main.Treeview.Groups", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit group.
/// </summary>
public static string Main_Treeview_Groups_ContextMenu_Edit {
get {
return ResourceManager.GetString("Main.Treeview.Groups.ContextMenu.Edit", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Are you sure you want to delete this subgroup. This operation is irreversible. Subgroup will be unassigned for all groups..
/// </summary>
public static string Main_Treeview_Subgroups_Message_Remove {
get {
return ResourceManager.GetString("Main.Treeview.Subgroups.Message.Remove", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Teachers. /// Looks up a localized string similar to Teachers.
/// </summary> /// </summary>
@@ -357,6 +555,15 @@ namespace TimetableDesigner.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Question.
/// </summary>
public static string MessageBox_Question {
get {
return ResourceManager.GetString("MessageBox.Question", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Author. /// Looks up a localized string similar to Author.
/// </summary> /// </summary>
@@ -447,6 +654,15 @@ namespace TimetableDesigner.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Group editing.
/// </summary>
public static string Tabs_GroupEdit {
get {
return ResourceManager.GetString("Tabs.GroupEdit", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Project settings. /// Looks up a localized string similar to Project settings.
/// </summary> /// </summary>
@@ -475,11 +691,11 @@ namespace TimetableDesigner.Properties {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Availability hours. /// Looks up a localized string similar to Days.
/// </summary> /// </summary>
public static string TeacherEdit_AvailabilityHours { public static string TeacherEdit_Days {
get { get {
return ResourceManager.GetString("TeacherEdit.AvailabilityHours", resourceCulture); return ResourceManager.GetString("TeacherEdit.Days", resourceCulture);
} }
} }
@@ -492,6 +708,33 @@ namespace TimetableDesigner.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Availability hours.
/// </summary>
public static string TeacherEdit_Groups_AvailabilityHours {
get {
return ResourceManager.GetString("TeacherEdit.Groups.AvailabilityHours", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Basics.
/// </summary>
public static string TeacherEdit_Groups_Basics {
get {
return ResourceManager.GetString("TeacherEdit.Groups.Basics", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Hours.
/// </summary>
public static string TeacherEdit_Hours {
get {
return ResourceManager.GetString("TeacherEdit.Hours", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to &quot;From&quot; value is higher or equal to &quot;To&quot; value. /// Looks up a localized string similar to &quot;From&quot; value is higher or equal to &quot;To&quot; value.
/// </summary> /// </summary>

View File

@@ -126,12 +126,21 @@
<data name="ClassroomEdit.Description" xml:space="preserve"> <data name="ClassroomEdit.Description" xml:space="preserve">
<value>Description</value> <value>Description</value>
</data> </data>
<data name="ClassroomEdit.Groups.Basics" xml:space="preserve">
<value>Basics</value>
</data>
<data name="ClassroomEdit.Groups.Capacity" xml:space="preserve">
<value>Capacity</value>
</data>
<data name="ClassroomEdit.Name" xml:space="preserve"> <data name="ClassroomEdit.Name" xml:space="preserve">
<value>Name</value> <value>Name</value>
</data> </data>
<data name="Global.DefaultClassroomName" xml:space="preserve"> <data name="Global.DefaultClassroomName" xml:space="preserve">
<value>New classroom</value> <value>New classroom</value>
</data> </data>
<data name="Global.DefaultGroupName" xml:space="preserve">
<value>New group</value>
</data>
<data name="Global.DefaultProjectName" xml:space="preserve"> <data name="Global.DefaultProjectName" xml:space="preserve">
<value>New project</value> <value>New project</value>
</data> </data>
@@ -141,9 +150,45 @@
<data name="Global.NoProjectLoadedTitle" xml:space="preserve"> <data name="Global.NoProjectLoadedTitle" xml:space="preserve">
<value>no project loaded</value> <value>no project loaded</value>
</data> </data>
<data name="GroupEdit.DeleteSubgroup" xml:space="preserve">
<value>Delete</value>
</data>
<data name="GroupEdit.Description" xml:space="preserve">
<value>Description</value>
</data>
<data name="GroupEdit.Groups.Basics" xml:space="preserve">
<value>Basics</value>
</data>
<data name="GroupEdit.Groups.Subgroups" xml:space="preserve">
<value>Subgroups</value>
</data>
<data name="GroupEdit.Message.SubgroupDelete" xml:space="preserve">
<value>Are you sure you want to delete this subgroup. This operation is irreversible. Subgroup will be unassigned for all groups.</value>
</data>
<data name="GroupEdit.Name" xml:space="preserve">
<value>Name</value>
</data>
<data name="GroupEdit.NewSubgroupLabel" xml:space="preserve">
<value>New subgroup name</value>
</data>
<data name="GroupEdit.Subgroups" xml:space="preserve">
<value>Subgroups</value>
</data>
<data name="Main.Message.SaveCurrentProject" xml:space="preserve"> <data name="Main.Message.SaveCurrentProject" xml:space="preserve">
<value>Project is loaded. Do you want to save?</value> <value>Project is loaded. Do you want to save?</value>
</data> </data>
<data name="Main.Ribbon.Export" xml:space="preserve">
<value>Export</value>
</data>
<data name="Main.Ribbon.Export.Timetable" xml:space="preserve">
<value>Timetable</value>
</data>
<data name="Main.Ribbon.Export.Timetable.PDF" xml:space="preserve">
<value>PDF</value>
</data>
<data name="Main.Ribbon.Export.Timetable.XLS" xml:space="preserve">
<value>XLS</value>
</data>
<data name="Main.Ribbon.File" xml:space="preserve"> <data name="Main.Ribbon.File" xml:space="preserve">
<value>File</value> <value>File</value>
</data> </data>
@@ -171,6 +216,18 @@
<data name="Main.Ribbon.Project" xml:space="preserve"> <data name="Main.Ribbon.Project" xml:space="preserve">
<value>Project</value> <value>Project</value>
</data> </data>
<data name="Main.Ribbon.Project.Import" xml:space="preserve">
<value>Import</value>
</data>
<data name="Main.Ribbon.Project.Import.ImportClassrooms" xml:space="preserve">
<value>Import classrooms</value>
</data>
<data name="Main.Ribbon.Project.Import.ImportGroups" xml:space="preserve">
<value>Import groups</value>
</data>
<data name="Main.Ribbon.Project.Import.ImportTeachers" xml:space="preserve">
<value>Import teachers</value>
</data>
<data name="Main.Ribbon.Project.New" xml:space="preserve"> <data name="Main.Ribbon.Project.New" xml:space="preserve">
<value>New</value> <value>New</value>
</data> </data>
@@ -207,6 +264,15 @@
<data name="Main.Treeview.ContextMenu.Remove" xml:space="preserve"> <data name="Main.Treeview.ContextMenu.Remove" xml:space="preserve">
<value>Remove</value> <value>Remove</value>
</data> </data>
<data name="Main.Treeview.Groups" xml:space="preserve">
<value>Groups</value>
</data>
<data name="Main.Treeview.Groups.ContextMenu.Edit" xml:space="preserve">
<value>Edit group</value>
</data>
<data name="Main.Treeview.Subgroups.Message.Remove" xml:space="preserve">
<value>Are you sure you want to delete this subgroup. This operation is irreversible. Subgroup will be unassigned for all groups.</value>
</data>
<data name="Main.Treeview.Teachers" xml:space="preserve"> <data name="Main.Treeview.Teachers" xml:space="preserve">
<value>Teachers</value> <value>Teachers</value>
</data> </data>
@@ -216,6 +282,9 @@
<data name="MessageBox.Error" xml:space="preserve"> <data name="MessageBox.Error" xml:space="preserve">
<value>Error</value> <value>Error</value>
</data> </data>
<data name="MessageBox.Question" xml:space="preserve">
<value>Question</value>
</data>
<data name="ProjectSettings.Author" xml:space="preserve"> <data name="ProjectSettings.Author" xml:space="preserve">
<value>Author</value> <value>Author</value>
</data> </data>
@@ -246,6 +315,9 @@
<data name="Tabs.ClassroomEdit" xml:space="preserve"> <data name="Tabs.ClassroomEdit" xml:space="preserve">
<value>Classroom editing</value> <value>Classroom editing</value>
</data> </data>
<data name="Tabs.GroupEdit" xml:space="preserve">
<value>Group editing</value>
</data>
<data name="Tabs.ProjectSettings" xml:space="preserve"> <data name="Tabs.ProjectSettings" xml:space="preserve">
<value>Project settings</value> <value>Project settings</value>
</data> </data>
@@ -255,12 +327,21 @@
<data name="Tabs.Welcome" xml:space="preserve"> <data name="Tabs.Welcome" xml:space="preserve">
<value>Welcome</value> <value>Welcome</value>
</data> </data>
<data name="TeacherEdit.AvailabilityHours" xml:space="preserve"> <data name="TeacherEdit.Days" xml:space="preserve">
<value>Availability hours</value> <value>Days</value>
</data> </data>
<data name="TeacherEdit.Description" xml:space="preserve"> <data name="TeacherEdit.Description" xml:space="preserve">
<value>Description</value> <value>Description</value>
</data> </data>
<data name="TeacherEdit.Groups.AvailabilityHours" xml:space="preserve">
<value>Availability hours</value>
</data>
<data name="TeacherEdit.Groups.Basics" xml:space="preserve">
<value>Basics</value>
</data>
<data name="TeacherEdit.Hours" xml:space="preserve">
<value>Hours</value>
</data>
<data name="TeacherEdit.Message.FromHigherThanTo" xml:space="preserve"> <data name="TeacherEdit.Message.FromHigherThanTo" xml:space="preserve">
<value>"From" value is higher or equal to "To" value</value> <value>"From" value is higher or equal to "To" value</value>
</data> </data>

View File

@@ -4,4 +4,5 @@
<c:ViewModelToViewConverter x:Key="ViewModelToViewConverter"/> <c:ViewModelToViewConverter x:Key="ViewModelToViewConverter"/>
<c:IsNullToVisibilityConverter x:Key="IsNullToVisibilityConverter"/> <c:IsNullToVisibilityConverter x:Key="IsNullToVisibilityConverter"/>
<c:IsNotNullToBooleanConverter x:Key="IsNotNullToBooleanConverter"/> <c:IsNotNullToBooleanConverter x:Key="IsNotNullToBooleanConverter"/>
<c:IntegerAddConstantConverter x:Key="IntegerAddConstantConverter"/>
</ResourceDictionary> </ResourceDictionary>

View File

@@ -16,4 +16,6 @@
<BitmapImage x:Key="SaveAsImage" UriSource="Images/SaveAs.png"/> <BitmapImage x:Key="SaveAsImage" UriSource="Images/SaveAs.png"/>
<BitmapImage x:Key="NewImage" UriSource="Images/New.png"/> <BitmapImage x:Key="NewImage" UriSource="Images/New.png"/>
<BitmapImage x:Key="RightArrowImage" UriSource="Images/RightArrow.png"/> <BitmapImage x:Key="RightArrowImage" UriSource="Images/RightArrow.png"/>
<BitmapImage x:Key="GroupImage" UriSource="Images/Group.png"/>
<BitmapImage x:Key="GroupAddImage" UriSource="Images/GroupAdd.png"/>
</ResourceDictionary> </ResourceDictionary>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Services
{
public interface IService
{
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Services.MessageBox
{
public interface IMessageBoxService : IService
{
#region METHODS
void ShowError(string message);
void ShowError(string message, string title);
MessageBoxQuestionResult ShowQuestion(string message, bool hideCancelButton = false);
MessageBoxQuestionResult ShowQuestion(string message, string title, bool hideCancelButton = false);
#endregion
}
}

View File

@@ -4,13 +4,12 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace TimetableDesigner.MessageBox namespace TimetableDesigner.Services.MessageBox
{ {
public enum MessageBoxYesNoCancelResult public enum MessageBoxQuestionResult
{ {
Yes, Yes,
No, No,
Cancel, Cancel
None
} }
} }

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using TimetableDesigner.Properties;
namespace TimetableDesigner.Services.MessageBox
{
public class MessageBoxService : IMessageBoxService
{
#region PUBLIC METHODS
public void ShowError(string message) => ShowError(message, Resources.MessageBox_Error);
public void ShowError(string message, string title) => System.Windows.Forms.MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Error);
public MessageBoxQuestionResult ShowQuestion(string message, bool hideCancelButton = false) => ShowQuestion(message, Resources.MessageBox_Question, hideCancelButton);
public MessageBoxQuestionResult ShowQuestion(string message, string title, bool hideCancelButton = false)
{
MessageBoxButton buttons = MessageBoxButton.YesNoCancel;
if (hideCancelButton)
{
buttons = MessageBoxButton.YesNo;
}
MessageBoxResult result = System.Windows.MessageBox.Show(message, title, buttons, MessageBoxImage.Question);
switch (result)
{
case MessageBoxResult.Yes: return MessageBoxQuestionResult.Yes;
case MessageBoxResult.No: return MessageBoxQuestionResult.No;
default: return MessageBoxQuestionResult.Cancel;
}
}
#endregion
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.ViewModels.Models;
namespace TimetableDesigner.Services.Project
{
public interface IProjectService : IService
{
#region PROPERTIES
Core.Project? Project { get; }
ProjectViewModel? ProjectViewModel { get; }
#endregion
#region METHODS
void New();
void Load(string path);
void Save(string path);
#endregion
}
}

View File

@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.Core;
using TimetableDesigner.Customs;
using TimetableDesigner.Properties;
using TimetableDesigner.ViewModels.Models;
namespace TimetableDesigner.Services.Project
{
public class ProjectService : ObservableObject, IProjectService
{
#region FIELDS
private Core.Project? _project;
private ProjectViewModel? _projectViewModel;
#endregion
#region PROPERTIES
public Core.Project? Project
{
get => _project;
private set
{
if (_project != value)
{
_project = value;
NotifyPropertyChanged(nameof(Project));
}
}
}
public ProjectViewModel? ProjectViewModel
{
get => _projectViewModel;
private set
{
if (_projectViewModel != value)
{
_projectViewModel = value;
NotifyPropertyChanged(nameof(ProjectViewModel));
}
}
}
#endregion
#region CONSTRUCTORS
public ProjectService()
{
_project = null;
_projectViewModel = null;
}
#endregion
#region PUBLIC METHODS
public void New()
{
Project = new Core.Project()
{
Name = Resources.Global_DefaultProjectName,
};
ProjectViewModel = new ProjectViewModel(Project);
}
public void Load(string path)
{
}
public void Save(string path)
{
}
#endregion
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Services
{
public class ServiceProvider
{
#region FIELDS
private Dictionary<Type, IService> _services;
#endregion
#region CONSTRUCTORS
public static readonly ServiceProvider Instance = new ServiceProvider();
private ServiceProvider()
{
_services = new Dictionary<Type, IService>();
}
#endregion
#region PUBLIC METHODS
public void AddService<T>(T service) where T : IService
{
_services[typeof(T)] = service;
}
public T GetService<T>() where T : IService
{
return (T)_services[typeof(T)];
}
#endregion
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimetableDesigner.Services.TabNavigation
{
public interface ITabNavigationService : IService, INotifyPropertyChanged
{
#region PROPERTIES
ObservableCollection<TabItem> Tabs { get; }
TabItem SelectedTab { get; }
#endregion
#region PUBLIC METHODS
void AddAndActivate(TabItem item);
void Add(TabItem item);
void InsertAndActivate(int index, TabItem item);
void Insert(int index, TabItem item);
void Close(TabItem item);
void Close(IEnumerable<TabItem> item);
void CloseAt(int index);
void CloseAt(IEnumerable<int> indexes);
void CloseAll();
void Activate(TabItem item);
#endregion
}
}

View File

@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Media;
using TimetableDesigner.Commands;
using TimetableDesigner.Customs;
using TimetableDesigner.ViewModels;
namespace TimetableDesigner.Services.TabNavigation
{
public class TabItem : ObservableObject
{
#region FIELDS
private ITabNavigationService _tabNavigationService;
private ImageSource _image;
private string _title;
private bool _isClosable;
private BaseViewViewModel _viewModel;
#endregion
#region PROPERTIES
public ImageSource Image
{
get => _image;
set
{
if (_image != value)
{
_image = value;
NotifyPropertyChanged(nameof(Image));
}
}
}
public string Title
{
get => _title;
set
{
if (_title != value)
{
_title = value;
NotifyPropertyChanged(nameof(Title));
}
}
}
public bool IsClosable
{
get => _isClosable;
set
{
if (_isClosable != value)
{
_isClosable = value;
NotifyPropertyChanged(nameof(IsClosable));
}
}
}
public BaseViewViewModel ViewModel
{
get => _viewModel;
set
{
if (_viewModel != value)
{
_viewModel = value;
NotifyPropertyChanged(nameof(ViewModel));
}
}
}
public ICommand CloseCommand { get; set; }
public ICommand ActivateCommand { get; set; }
#endregion
#region CONSTRUCTORS
public TabItem()
{
_tabNavigationService = ServiceProvider.Instance.GetService<ITabNavigationService>();
CloseCommand = new RelayCommand<object>(args => Close());
ActivateCommand = new RelayCommand<object>(args => Activate());
}
#endregion
#region PUBLIC METHODS
public void Close() => _tabNavigationService.Close(this);
public void Activate() => _tabNavigationService.Activate(this);
#endregion
}
}

View File

@@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using TimetableDesigner.Customs;
namespace TimetableDesigner.Services.TabNavigation
{
public class TabNavigationService : ObservableObject, ITabNavigationService
{
#region FIELDS
private TabItem _selectedTab;
#endregion
#region PROPERTIES
public ObservableCollection<TabItem> Tabs { get; private set; }
public TabItem? SelectedTab
{
get => _selectedTab;
private set
{
if (_selectedTab != value)
{
_selectedTab = value;
NotifyPropertyChanged(nameof(SelectedTab));
}
}
}
#endregion
#region CONSTRUCTORS
public TabNavigationService()
{
Tabs = new ObservableCollection<TabItem>();
SelectedTab = null;
}
#endregion
#region PUBLIC METHODS
public void AddAndActivate(TabItem item)
{
Add(item);
Activate(item);
}
public void Add(TabItem item) => Tabs.Add(item);
public void InsertAndActivate(int index, TabItem item)
{
Insert(index, item);
Activate(item);
}
public void Insert(int index, TabItem item) => Tabs.Insert(index, item);
public void Close(TabItem item) => Close(new List<TabItem>() { item });
public void Close(IEnumerable<TabItem> items)
{
TabItem selected = SelectedTab;
while (items.Contains(selected) && selected != null)
{
int nextIndex = Tabs.IndexOf(selected) + 1;
if (Tabs.Count > nextIndex)
{
selected = Tabs[nextIndex];
}
else
{
selected = null;
}
}
if (selected == null)
{
selected = SelectedTab;
while (items.Contains(selected) && selected != null)
{
int prevIndex = Tabs.IndexOf(selected) - 1;
if (prevIndex >= 0)
{
selected = Tabs[prevIndex];
}
else
{
selected = null;
}
}
}
foreach (TabItem item in items)
{
Tabs.Remove(item);
}
SelectedTab = selected;
}
public void CloseAt(int index) => CloseAt(new List<int>() { index });
public void CloseAt(IEnumerable<int> indexes)
{
List<TabItem> items = new List<TabItem>();
foreach (int index in indexes)
{
if (Tabs.Count > index)
{
items.Add(Tabs[index]);
}
}
Close(items);
}
public void CloseAll()
{
Tabs.Clear();
SelectedTab = null;
}
public void Activate(TabItem item)
{
if (Tabs.Contains(item))
{
SelectedTab = item;
}
}
#endregion
}
}

View File

@@ -8,12 +8,20 @@
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Remove="ViewModels\ProjectSettingsTabViewModel.cs" />
<Compile Remove="ViewModels\TeacherEditTabViewModel.cs" />
<Compile Remove="Views\ProjectSettingsTabView.xaml.cs" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="Resources\Images\Add.png" /> <None Remove="Resources\Images\Add.png" />
<None Remove="Resources\Images\Classroom.png" /> <None Remove="Resources\Images\Classroom.png" />
<None Remove="Resources\Images\ClassroomAdd.png" /> <None Remove="Resources\Images\ClassroomAdd.png" />
<None Remove="Resources\Images\Close.png" /> <None Remove="Resources\Images\Close.png" />
<None Remove="Resources\Images\CloseButtonImage.png" /> <None Remove="Resources\Images\CloseButtonImage.png" />
<None Remove="Resources\Images\Group.png" />
<None Remove="Resources\Images\GroupAdd.png" />
<None Remove="Resources\Images\New.png" /> <None Remove="Resources\Images\New.png" />
<None Remove="Resources\Images\Open.png" /> <None Remove="Resources\Images\Open.png" />
<None Remove="Resources\Images\OpenRecent.png" /> <None Remove="Resources\Images\OpenRecent.png" />
@@ -27,6 +35,10 @@
<None Remove="Resources\Images\TeacherAdd.png" /> <None Remove="Resources\Images\TeacherAdd.png" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Page Remove="Views\ProjectSettingsTabView.xaml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Resources\Images\Classroom.png"> <Content Include="Resources\Images\Classroom.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
@@ -37,6 +49,12 @@
<Content Include="Resources\Images\Close.png"> <Content Include="Resources\Images\Close.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Resources\Images\Group.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Images\GroupAdd.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Images\New.png"> <Content Include="Resources\Images\New.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
@@ -80,7 +98,6 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\TimetableDesigner.Core\TimetableDesigner.Core.csproj" /> <ProjectReference Include="..\TimetableDesigner.Core\TimetableDesigner.Core.csproj" />
<ProjectReference Include="..\TimetableDesigner.Customs\TimetableDesigner.Customs.csproj" /> <ProjectReference Include="..\TimetableDesigner.Customs\TimetableDesigner.Customs.csproj" />
<ProjectReference Include="..\TimetableDesigner.MessageBox\TimetableDesigner.MessageBox.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,56 +0,0 @@
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,14 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.Customs;
namespace TimetableDesigner.ViewModels
{
public abstract class BaseModelViewModel : ObservableObject
{
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.Customs;
namespace TimetableDesigner.ViewModels
{
public abstract class BaseViewViewModel : ObservableObject
{
}
}

View File

@@ -1,52 +0,0 @@
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) : base()
{
_classroom = classroom;
}
#endregion
}
}

View File

@@ -1,255 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.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 ICommand NewTeacherCommand { get; set; }
public ICommand EditTeacherCommand { get; set; }
public ICommand RemoveTeacherCommand { 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);
NewTeacherCommand = new RelayCommand<object>(param => NewTeacher());
EditTeacherCommand = new RelayCommand<TeacherViewModel>(EditTeacher);
RemoveTeacherCommand = new RelayCommand<TeacherViewModel>(DeleteTeacher);
_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)
{
ClassroomEditTabViewModel classroomEdit = new ClassroomEditTabViewModel(classroomViewModel)
{
TabTitle = $"{Resources.Tabs_ClassroomEdit}: {classroomViewModel.Name}",
IsTabClosable = true
};
Tabs.Add(classroomEdit);
SelectedTab = classroomEdit;
}
private void DeleteClassroom(ClassroomViewModel classroomViewModel)
{
if (Project is not null)
{
Project.Classrooms.Remove(classroomViewModel);
}
}
private void NewTeacher()
{
if (Project is not null)
{
Teacher teacher = new Teacher()
{
Name = Resources.Global_DefaultTeacherName
};
TeacherViewModel teacherVM = new TeacherViewModel(teacher);
Project.Teachers.Add(teacherVM);
EditTeacher(teacherVM);
}
}
private void EditTeacher(TeacherViewModel teacherViewModel)
{
if (Project is not null)
{
TeacherEditTabViewModel teacherEdit = new TeacherEditTabViewModel(teacherViewModel, Project.TimetableTemplate)
{
Teacher = teacherViewModel,
TabTitle = $"{Resources.Tabs_TeacherEdit}: {teacherViewModel.Name}",
IsTabClosable = true
};
Tabs.Add(teacherEdit);
SelectedTab = teacherEdit;
}
}
private void DeleteTeacher(TeacherViewModel teacherViewModel)
{
if (Project is not null)
{
Project.Teachers.Remove(teacherViewModel);
}
}
#endregion
}
}

View File

@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.Core;
using TimetableDesigner.Services;
using TimetableDesigner.Services.Project;
namespace TimetableDesigner.ViewModels.Models
{
public class ClassViewModel : BaseModelViewModel
{
#region FIELDS
private IProjectService _projectService;
private Class _class;
#endregion
#region PROPERTIES
public Class Class => _class;
public string Name
{
get => _class.Name;
set
{
if (_class.Name != value)
{
_class.Name = value;
NotifyPropertyChanged(nameof(Name));
}
}
}
public TeacherViewModel? Teacher
{
get => _projectService.ProjectViewModel?.Teachers.Where(vm => vm.Teacher == _class.Teacher).FirstOrDefault();
set
{
if (_class.Teacher != value?.Teacher)
{
_class.Teacher = value?.Teacher;
NotifyPropertyChanged(nameof(Teacher));
}
}
}
public IGroupViewModel? Group
{
get
{
if (_class.Group?.GetType() == typeof(GroupViewModel))
{
return _projectService.ProjectViewModel?.Groups.Where(vm => vm.Group == _class.Group).FirstOrDefault();
}
else if (_class.Group?.GetType() == typeof(SubgroupViewModel))
{
return _projectService.ProjectViewModel?.Subgroups.Where(vm => vm.Subgroup == _class.Group).FirstOrDefault();
}
else
{
return null;
}
}
set
{
if (_class.Group != value?.Group)
{
_class.Group = value?.Group;
NotifyPropertyChanged(nameof(Group));
}
}
}
public ClassroomViewModel? Classroom
{
get => _projectService.ProjectViewModel?.Classrooms.Where(vm => vm.Classroom == _class.Classroom).FirstOrDefault();
set
{
if (_class.Classroom != value?.Classroom)
{
_class.Classroom = value?.Classroom;
NotifyPropertyChanged(nameof(Classroom));
}
}
}
#endregion
#region CONSTRUCTORS
public ClassViewModel(Class @class)
{
_projectService = ServiceProvider.Instance.GetService<IProjectService>();
_class = @class;
}
#endregion
}
}

View File

@@ -4,11 +4,10 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using TimetableDesigner.Core; using TimetableDesigner.Core;
using TimetableDesigner.ViewModels.Base;
namespace TimetableDesigner.ViewModels.Models namespace TimetableDesigner.ViewModels.Models
{ {
public class ClassroomViewModel : BaseViewModel public class ClassroomViewModel : BaseModelViewModel
{ {
#region FIELDS #region FIELDS

View File

@@ -0,0 +1,88 @@
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.Services;
using TimetableDesigner.Services.Project;
namespace TimetableDesigner.ViewModels.Models
{
public class GroupViewModel : BaseModelViewModel, IGroupViewModel
{
#region FIELDS
private IProjectService _projectService;
private Group _group;
#endregion
#region PROPERTIES
IGroup IGroupViewModel.Group => _group;
public Group Group => _group;
public string Name
{
get => _group.Name;
set
{
if (_group.Name != value)
{
_group.Name = value;
NotifyPropertyChanged(nameof(Name));
}
}
}
public string Description
{
get => _group.Description;
set
{
if (_group.Description != value)
{
_group.Description = value;
NotifyPropertyChanged(nameof(Description));
}
}
}
public ObservableCollection<SubgroupViewModel> AssignedSubgroups => new ObservableCollection<SubgroupViewModel>(_projectService.ProjectViewModel.Subgroups.Where(vm => Group.AssignedSubgroups.Contains(vm.Subgroup)));
#endregion
#region CONSTRUCTORS
public GroupViewModel(Group group)
{
_projectService = ServiceProvider.Instance.GetService<IProjectService>();
_group = group;
}
#endregion
#region PUBLIC METHODS
public void AddSubgroup(SubgroupViewModel subgroup)
{
Group.AssignedSubgroups.Add(subgroup.Subgroup);
NotifyPropertyChanged(nameof(AssignedSubgroups));
}
public void RemoveSubgroup(SubgroupViewModel subgroup)
{
Group.AssignedSubgroups.Remove(subgroup.Subgroup);
NotifyPropertyChanged(nameof(AssignedSubgroups));
}
#endregion
}
}

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.Core;
namespace TimetableDesigner.ViewModels.Models
{
public interface IGroupViewModel
{
#region PROPERTIES
IGroup Group { get; }
string Name { get; }
#endregion
}
}

View File

@@ -5,11 +5,10 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using TimetableDesigner.Core; using TimetableDesigner.Core;
using TimetableDesigner.ViewModels.Base;
namespace TimetableDesigner.ViewModels.Models namespace TimetableDesigner.ViewModels.Models
{ {
public class ProjectViewModel : BaseViewModel public class ProjectViewModel : BaseModelViewModel
{ {
#region FIELDS #region FIELDS
@@ -62,6 +61,8 @@ namespace TimetableDesigner.ViewModels.Models
public TimetableTemplateViewModel TimetableTemplate { get; set; } public TimetableTemplateViewModel TimetableTemplate { get; set; }
public ObservableCollection<ClassroomViewModel> Classrooms { get; set; } public ObservableCollection<ClassroomViewModel> Classrooms { get; set; }
public ObservableCollection<TeacherViewModel> Teachers { get; set; } public ObservableCollection<TeacherViewModel> Teachers { get; set; }
public ObservableCollection<GroupViewModel> Groups { get; set; }
public ObservableCollection<SubgroupViewModel> Subgroups { get; set; }
#endregion #endregion
@@ -80,6 +81,12 @@ namespace TimetableDesigner.ViewModels.Models
Teachers = new ObservableCollection<TeacherViewModel>(); Teachers = new ObservableCollection<TeacherViewModel>();
Teachers.CollectionChanged += Teachers_CollectionChanged; Teachers.CollectionChanged += Teachers_CollectionChanged;
Groups = new ObservableCollection<GroupViewModel>();
Groups.CollectionChanged += Groups_CollectionChanged;
Subgroups = new ObservableCollection<SubgroupViewModel>();
Subgroups.CollectionChanged += Subgroups_CollectionChanged;
} }
#endregion #endregion
@@ -132,6 +139,50 @@ namespace TimetableDesigner.ViewModels.Models
} }
} }
private void Groups_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
IList<GroupViewModel>? added = e.NewItems as IList<GroupViewModel>;
IList<GroupViewModel>? removed = e.OldItems as IList<GroupViewModel>;
if (removed is not null)
{
foreach (GroupViewModel vm in removed)
{
_project.Groups.Remove(vm.Group);
}
}
if (added is not null)
{
foreach (GroupViewModel vm in added)
{
_project.Groups.Add(vm.Group);
}
}
}
private void Subgroups_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
IList<SubgroupViewModel>? added = e.NewItems as IList<SubgroupViewModel>;
IList<SubgroupViewModel>? removed = e.OldItems as IList<SubgroupViewModel>;
if (removed is not null)
{
foreach (SubgroupViewModel vm in removed)
{
_project.Subgroups.Remove(vm.Subgroup);
}
}
if (added is not null)
{
foreach (SubgroupViewModel vm in added)
{
_project.Subgroups.Add(vm.Subgroup);
}
}
}
#endregion #endregion
} }
} }

View File

@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.Core;
namespace TimetableDesigner.ViewModels.Models
{
public class SubgroupViewModel : BaseModelViewModel, IGroupViewModel
{
#region FIELDS
private Subgroup _subgroup;
#endregion
#region PROPERTIES
IGroup IGroupViewModel.Group => _subgroup;
public Subgroup Subgroup => _subgroup;
public string Name
{
get => _subgroup.Name;
set
{
if (_subgroup.Name != value)
{
_subgroup.Name = value;
NotifyPropertyChanged(nameof(Name));
}
}
}
#endregion
#region CONSTRUCTORS
public SubgroupViewModel(Subgroup subgroup)
{
_subgroup = subgroup;
}
#endregion
}
}

View File

@@ -5,12 +5,11 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using TimetableDesigner.Core; using TimetableDesigner.Core;
using TimetableDesigner.Customs.Collections; using TimetableDesigner.Customs;
using TimetableDesigner.ViewModels.Base;
namespace TimetableDesigner.ViewModels.Models namespace TimetableDesigner.ViewModels.Models
{ {
public class TeacherViewModel : BaseViewModel public class TeacherViewModel : BaseModelViewModel
{ {
#region FIELDS #region FIELDS

View File

@@ -5,11 +5,10 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using TimetableDesigner.Core; using TimetableDesigner.Core;
using TimetableDesigner.ViewModels.Base;
namespace TimetableDesigner.ViewModels.Models namespace TimetableDesigner.ViewModels.Models
{ {
public class TimetableTemplateViewModel : BaseViewModel public class TimetableTemplateViewModel : BaseModelViewModel
{ {
#region FIELDS #region FIELDS

View File

@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.Core;
using TimetableDesigner.Customs;
using TimetableDesigner.Properties;
using TimetableDesigner.Services;
using TimetableDesigner.Services.TabNavigation;
using TimetableDesigner.ViewModels.Models;
namespace TimetableDesigner.ViewModels.Views
{
public class ClassroomEditViewModel : BaseViewViewModel
{
#region FIELDS
private ClassroomViewModel _classroom;
#endregion
#region PROPERTIES
public ClassroomViewModel Classroom
{
get => _classroom;
set
{
if (_classroom != value)
{
_classroom = value;
NotifyPropertyChanged(nameof(Classroom));
}
}
}
public string Name
{
get => _classroom.Name;
set
{
if (_classroom.Name != value)
{
_classroom.Name = value;
NotifyPropertyChanged(nameof(Name));
TabItem? tab = ServiceProvider.Instance.GetService<ITabNavigationService>().Tabs.Where(tab => tab.ViewModel == this).FirstOrDefault();
if (tab != null)
{
tab.Title = $"{Resources.Tabs_ClassroomEdit}: {_classroom.Name}";
}
}
}
}
#endregion
#region CONSTRUCTORS
public ClassroomEditViewModel() : this(new ClassroomViewModel(new Classroom()))
{ }
public ClassroomEditViewModel(ClassroomViewModel classroom)
{
_classroom = classroom;
}
#endregion
}
}

View File

@@ -0,0 +1,161 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimetableDesigner.Core;
using TimetableDesigner.Services.TabNavigation;
using TimetableDesigner.Services;
using TimetableDesigner.ViewModels.Models;
using TimetableDesigner.Properties;
using TimetableDesigner.Customs;
using TimetableDesigner.Services.Project;
using System.Windows.Input;
using TimetableDesigner.Commands;
using System.Diagnostics;
using TimetableDesigner.Services.MessageBox;
namespace TimetableDesigner.ViewModels.Views
{
public class GroupEditViewModel : BaseViewViewModel
{
#region FIELDS
private IProjectService _projectService;
private IMessageBoxService _messageBoxService;
private GroupViewModel _group;
private string _newSubgroupName;
#endregion
#region PROPERTIES
public GroupViewModel Group
{
get => _group;
set
{
if (_group != value)
{
_group = value;
NotifyPropertyChanged(nameof(Group));
}
}
}
public string Name
{
get => _group.Name;
set
{
if (_group.Name != value)
{
_group.Name = value;
NotifyPropertyChanged(nameof(Name));
TabItem? tab = ServiceProvider.Instance.GetService<ITabNavigationService>().Tabs.Where(tab => tab.ViewModel == this).FirstOrDefault();
if (tab != null)
{
tab.Title = $"{Resources.Tabs_GroupEdit}: {_group.Name}";
}
}
}
}
public ObservableDictionary<SubgroupViewModel, bool> Subgroups => new ObservableDictionary<SubgroupViewModel, bool>(_projectService.ProjectViewModel.Subgroups.ToDictionary(sg => sg, Group.AssignedSubgroups.Contains));
public string NewSubgroupName
{
get => _newSubgroupName;
set
{
if (_newSubgroupName != value)
{
_newSubgroupName = value;
NotifyPropertyChanged(nameof(NewSubgroupName));
}
}
}
public ICommand AddSubgroupCommand { get; set; }
public ICommand EditSubgroupAssignmentCommand { get; set; }
public ICommand DeleteSubgroupCommand { get; set; }
#endregion
#region CONSTRUCTORS
public GroupEditViewModel() : this(new GroupViewModel(new Group()))
{ }
public GroupEditViewModel(GroupViewModel group)
{
_projectService = ServiceProvider.Instance.GetService<IProjectService>();
_messageBoxService = ServiceProvider.Instance.GetService<IMessageBoxService>();
_group = group;
_newSubgroupName = string.Empty;
AddSubgroupCommand = new RelayCommand<object>(args => AddSubgroup());
EditSubgroupAssignmentCommand = new RelayCommand<SubgroupViewModel>(EditSubgroupAssignment);
DeleteSubgroupCommand = new RelayCommand<SubgroupViewModel>(DeleteSubgroup);
}
#endregion
#region PRIVATE METHODS
private void AddSubgroup()
{
Subgroup subgroup = new Subgroup()
{
Name = NewSubgroupName
};
SubgroupViewModel subgroupViewModel = new SubgroupViewModel(subgroup);
_projectService.ProjectViewModel.Subgroups.Add(subgroupViewModel);
Group.AddSubgroup(subgroupViewModel);
NotifyPropertyChanged(nameof(Subgroups));
NewSubgroupName = string.Empty;
}
private void EditSubgroupAssignment(SubgroupViewModel subgroup)
{
bool assigned = Subgroups[subgroup];
if (assigned)
{
Group.RemoveSubgroup(subgroup);
}
else
{
Group.AddSubgroup(subgroup);
}
NotifyPropertyChanged(nameof(Subgroups));
}
private void DeleteSubgroup(SubgroupViewModel subgroup)
{
MessageBoxQuestionResult result = _messageBoxService.ShowQuestion(Resources.GroupEdit_Message_SubgroupDelete, true);
if (result == MessageBoxQuestionResult.Yes)
{
foreach (GroupViewModel group in _projectService.ProjectViewModel.Groups)
{
group.RemoveSubgroup(subgroup);
}
_projectService.ProjectViewModel.Subgroups.Remove(subgroup);
NotifyPropertyChanged(nameof(Subgroups));
}
}
#endregion
}
}

View File

@@ -0,0 +1,271 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using TimetableDesigner.Commands;
using System.Windows.Navigation;
using TimetableDesigner.Core;
using System.Windows;
using TimetableDesigner.Properties;
using System.Reflection;
using TimetableDesigner.ViewModels.Models;
using System.Windows.Data;
using TimetableDesigner.Services.MessageBox;
using System.ComponentModel.Design;
using TimetableDesigner.Services;
using TimetableDesigner.Services.TabNavigation;
using TimetableDesigner.Services.Project;
using System.Drawing;
namespace TimetableDesigner.ViewModels.Views
{
class MainViewModel : BaseViewViewModel
{
#region FIELDS
private IMessageBoxService _messageBoxService;
private ITabNavigationService _tabNavigationService;
private IProjectService _projectService;
#endregion
#region PROPERTIES
// Observable services
public IProjectService ProjectService => _projectService;
public ITabNavigationService TabNavigationService => _tabNavigationService;
// Tabs
public ObservableCollection<TabItem> Tabs => _tabNavigationService.Tabs;
public TabItem SelectedTab => _tabNavigationService.SelectedTab;
// Commands
public ICommand NewProjectCommand { get; set; }
public ICommand OpenProjectCommand { get; set; }
public ICommand ProjectSettingsCommand { get; set; }
public ICommand NewClassroomCommand { get; set; }
public ICommand EditClassroomCommand { get; set; }
public ICommand RemoveClassroomCommand { get; set; }
public ICommand NewTeacherCommand { get; set; }
public ICommand EditTeacherCommand { get; set; }
public ICommand RemoveTeacherCommand { get; set; }
public ICommand NewGroupCommand { get; set; }
public ICommand EditGroupCommand { get; set; }
public ICommand RemoveGroupCommand { get; set; }
public ICommand RemoveSubgroupCommand { get; set; }
// Others
public string Version { get; set; }
#endregion
#region CONSTRUCTORS
public MainViewModel()
{
_messageBoxService = ServiceProvider.Instance.GetService<IMessageBoxService>();
_tabNavigationService = ServiceProvider.Instance.GetService<ITabNavigationService>();
_projectService = ServiceProvider.Instance.GetService<IProjectService>();
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);
NewTeacherCommand = new RelayCommand<object>(param => NewTeacher());
EditTeacherCommand = new RelayCommand<TeacherViewModel>(EditTeacher);
RemoveTeacherCommand = new RelayCommand<TeacherViewModel>(DeleteTeacher);
NewGroupCommand = new RelayCommand<object>(param => NewGroup());
EditGroupCommand = new RelayCommand<GroupViewModel>(EditGroup);
RemoveGroupCommand = new RelayCommand<GroupViewModel>(DeleteGroup);
RemoveSubgroupCommand = new RelayCommand<SubgroupViewModel>(DeleteSubgroup);
Version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
TabItem welcomeTab = new TabItem()
{
Title = Resources.Tabs_Welcome,
ViewModel = new WelcomeViewModel()
};
_tabNavigationService.AddAndActivate(welcomeTab);
}
#endregion
#region PRIVATE METHODS
private void OpenProject()
{
}
private void NewProject()
{
if (ProjectService.ProjectViewModel is not null)
{
MessageBoxQuestionResult result = _messageBoxService.ShowQuestion(Resources.Main_Message_SaveCurrentProject);
switch (result)
{
case MessageBoxQuestionResult.Yes: break;
case MessageBoxQuestionResult.No: break;
default: return;
}
}
_tabNavigationService.CloseAll();
_projectService.New();
ProjectSettings();
}
private void ProjectSettings()
{
if (ProjectService.ProjectViewModel is not null)
{
TabItem projectSettingsTab = new TabItem()
{
Title = Resources.Tabs_ProjectSettings,
IsClosable = true,
ViewModel = new ProjectSettingsViewModel()
};
_tabNavigationService.AddAndActivate(projectSettingsTab);
}
}
private void NewClassroom()
{
if (ProjectService.ProjectViewModel is not null)
{
Classroom classroom = new Classroom()
{
Name = Resources.Global_DefaultClassroomName
};
ClassroomViewModel classroomVM = new ClassroomViewModel(classroom);
ProjectService.ProjectViewModel.Classrooms.Add(classroomVM);
EditClassroom(classroomVM);
}
}
private void EditClassroom(ClassroomViewModel classroomViewModel)
{
if (ProjectService.ProjectViewModel is not null)
{
TabItem classroomEditTab = new TabItem()
{
Title = $"{Resources.Tabs_ClassroomEdit}: {classroomViewModel.Name}",
IsClosable = true,
ViewModel = new ClassroomEditViewModel(classroomViewModel)
};
_tabNavigationService.AddAndActivate(classroomEditTab);
}
}
private void DeleteClassroom(ClassroomViewModel classroomViewModel)
{
if (ProjectService.ProjectViewModel is not null)
{
ProjectService.ProjectViewModel.Classrooms.Remove(classroomViewModel);
}
}
private void NewTeacher()
{
if (ProjectService.ProjectViewModel is not null)
{
Teacher teacher = new Teacher()
{
Name = Resources.Global_DefaultTeacherName
};
TeacherViewModel teacherVM = new TeacherViewModel(teacher);
ProjectService.ProjectViewModel.Teachers.Add(teacherVM);
EditTeacher(teacherVM);
}
}
private void EditTeacher(TeacherViewModel teacherViewModel)
{
if (ProjectService.ProjectViewModel is not null)
{
TabItem teacherEditTab = new TabItem()
{
Title = $"{Resources.Tabs_TeacherEdit}: {teacherViewModel.Name}",
IsClosable = true,
ViewModel = new TeacherEditViewModel(teacherViewModel)
};
_tabNavigationService.AddAndActivate(teacherEditTab);
}
}
private void DeleteTeacher(TeacherViewModel teacherViewModel)
{
if (ProjectService.ProjectViewModel is not null)
{
ProjectService.ProjectViewModel.Teachers.Remove(teacherViewModel);
}
}
private void NewGroup()
{
if (ProjectService.ProjectViewModel is not null)
{
Group group = new Group()
{
Name = Resources.Global_DefaultGroupName
};
GroupViewModel groupVM = new GroupViewModel(group);
ProjectService.ProjectViewModel.Groups.Add(groupVM);
EditGroup(groupVM);
}
}
private void EditGroup(GroupViewModel groupViewModel)
{
if (ProjectService.ProjectViewModel is not null)
{
TabItem groupEditTab = new TabItem()
{
Title = $"{Resources.Tabs_GroupEdit}: {groupViewModel.Name}",
IsClosable = true,
ViewModel = new GroupEditViewModel(groupViewModel)
};
_tabNavigationService.AddAndActivate(groupEditTab);
}
}
private void DeleteGroup(GroupViewModel groupViewModel)
{
if (ProjectService.ProjectViewModel is not null)
{
ProjectService.ProjectViewModel.Groups.Remove(groupViewModel);
}
}
private void DeleteSubgroup(SubgroupViewModel subgroupViewModel)
{
if (ProjectService.ProjectViewModel is not null)
{
MessageBoxQuestionResult result = _messageBoxService.ShowQuestion(Resources.Main_Treeview_Subgroups_Message_Remove, true);
if (result == MessageBoxQuestionResult.Yes)
{
foreach (GroupViewModel group in ProjectService.ProjectViewModel.Groups)
{
group.RemoveSubgroup(subgroupViewModel);
}
_projectService.ProjectViewModel.Subgroups.Remove(subgroupViewModel);
}
}
}
#endregion
}
}

View File

@@ -10,17 +10,22 @@ using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using TimetableDesigner.Commands; using TimetableDesigner.Commands;
using TimetableDesigner.Core; using TimetableDesigner.Core;
using TimetableDesigner.MessageBox; using TimetableDesigner.Customs;
using TimetableDesigner.Properties; using TimetableDesigner.Properties;
using TimetableDesigner.ViewModels.Base; using TimetableDesigner.Services;
using TimetableDesigner.Services.MessageBox;
using TimetableDesigner.Services.Project;
using TimetableDesigner.ViewModels.Models; using TimetableDesigner.ViewModels.Models;
namespace TimetableDesigner.ViewModels namespace TimetableDesigner.ViewModels.Views
{ {
public class ProjectSettingsTabViewModel : BaseTabViewModel public class ProjectSettingsViewModel : BaseViewViewModel
{ {
#region FIELDS #region FIELDS
private IMessageBoxService _messageBoxService;
private IProjectService _projectService;
private string _newDayName; private string _newDayName;
private DateTime? _newSlotFrom; private DateTime? _newSlotFrom;
private DateTime? _newSlotTo; private DateTime? _newSlotTo;
@@ -31,13 +36,10 @@ namespace TimetableDesigner.ViewModels
#region PROPERTIES #region PROPERTIES
public ICommand AddDayCommand { get; set; } // Project
public ICommand AddSlotCommand { get; set; } public ProjectViewModel? Project => _projectService.ProjectViewModel;
public ICommand RemoveDayCommand { get; set; }
public ICommand RemoveSlotCommand { get; set; }
public ProjectViewModel? Project { get; set; }
// Fields
public string NewDayName public string NewDayName
{ {
get => _newDayName; get => _newDayName;
@@ -67,15 +69,22 @@ namespace TimetableDesigner.ViewModels
} }
} }
// Commands
public ICommand AddDayCommand { get; set; }
public ICommand AddSlotCommand { get; set; }
public ICommand RemoveDayCommand { get; set; }
public ICommand RemoveSlotCommand { get; set; }
#endregion #endregion
#region CONSTRUCTORS #region CONSTRUCTORS
public ProjectSettingsTabViewModel() : base() public ProjectSettingsViewModel()
{ {
Project = new ProjectViewModel(new Project()); _messageBoxService = ServiceProvider.Instance.GetService<IMessageBoxService>();
_projectService = ServiceProvider.Instance.GetService<IProjectService>();
AddDayCommand = new RelayCommand<object>(param => AddDay()); AddDayCommand = new RelayCommand<object>(param => AddDay());
AddSlotCommand = new RelayCommand<object>(param => AddSlot()); AddSlotCommand = new RelayCommand<object>(param => AddSlot());
@@ -95,24 +104,34 @@ namespace TimetableDesigner.ViewModels
private void AddDay() private void AddDay()
{ {
if (!string.IsNullOrWhiteSpace(NewDayName)) if (Project is not null && !string.IsNullOrWhiteSpace(NewDayName))
{ {
Project.TimetableTemplate.AddDay(new TimetableDay(NewDayName)); Project.TimetableTemplate.AddDay(new TimetableDay(NewDayName));
NewDayName = string.Empty; NewDayName = string.Empty;
} }
} }
private void RemoveDay(TimetableDay day) => Project.TimetableTemplate.RemoveDay(day); private void RemoveDay(TimetableDay day)
{
if (Project is not null)
{
foreach (TeacherViewModel teacher in Project.Teachers)
{
teacher.RemoveDay(day);
}
Project.TimetableTemplate.RemoveDay(day);
}
}
private void AddSlot() private void AddSlot()
{ {
if (NewSlotFrom.HasValue && NewSlotTo.HasValue) if (Project is not null && NewSlotFrom.HasValue && NewSlotTo.HasValue)
{ {
TimeOnly from = new TimeOnly(NewSlotFrom.Value.Hour, NewSlotFrom.Value.Minute); TimeOnly from = new TimeOnly(NewSlotFrom.Value.Hour, NewSlotFrom.Value.Minute);
TimeOnly to = new TimeOnly(NewSlotTo.Value.Hour, NewSlotTo.Value.Minute); TimeOnly to = new TimeOnly(NewSlotTo.Value.Hour, NewSlotTo.Value.Minute);
if (from >= to) if (from >= to)
{ {
MessageBoxService.ShowError(Resources.ProjectSettings_Message_FromHigherThanTo); _messageBoxService.ShowError(Resources.ProjectSettings_Message_FromHigherThanTo);
return; return;
} }
@@ -128,7 +147,7 @@ namespace TimetableDesigner.ViewModels
} }
catch (ArgumentException) catch (ArgumentException)
{ {
MessageBoxService.ShowError(Resources.ProjectSettings_Message_SlotCollision); _messageBoxService.ShowError(Resources.ProjectSettings_Message_SlotCollision);
} }
} }
} }

View File

@@ -7,19 +7,24 @@ using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using TimetableDesigner.Commands; using TimetableDesigner.Commands;
using TimetableDesigner.Core; using TimetableDesigner.Core;
using TimetableDesigner.MessageBox; using TimetableDesigner.Customs;
using TimetableDesigner.Properties; using TimetableDesigner.Properties;
using TimetableDesigner.ViewModels.Base; using TimetableDesigner.Services;
using TimetableDesigner.Services.MessageBox;
using TimetableDesigner.Services.Project;
using TimetableDesigner.Services.TabNavigation;
using TimetableDesigner.ViewModels.Models; using TimetableDesigner.ViewModels.Models;
namespace TimetableDesigner.ViewModels namespace TimetableDesigner.ViewModels.Views
{ {
public class TeacherEditTabViewModel : BaseTabViewModel public class TeacherEditViewModel : BaseViewViewModel
{ {
#region FIELDS #region FIELDS
private IMessageBoxService _messageBoxService;
private IProjectService _projectService;
private TeacherViewModel _teacher; private TeacherViewModel _teacher;
private TimetableTemplateViewModel _timetableTemplate;
private TimetableDay _selectedDay; private TimetableDay _selectedDay;
@@ -45,15 +50,23 @@ namespace TimetableDesigner.ViewModels
} }
} }
} }
public TimetableTemplateViewModel TimetableTemplate public TimetableTemplateViewModel? TimetableTemplate => _projectService.ProjectViewModel?.TimetableTemplate;
public string Name
{ {
get => _timetableTemplate; get => _teacher.Name;
set set
{ {
if (value != _timetableTemplate) if (_teacher.Name != value)
{ {
_timetableTemplate = value; _teacher.Name = value;
NotifyPropertyChanged(nameof(TimetableTemplate)); NotifyPropertyChanged(nameof(Teacher));
TabItem? tab = ServiceProvider.Instance.GetService<ITabNavigationService>().Tabs.Where(tab => tab.ViewModel == this).FirstOrDefault();
if (tab != null)
{
tab.Title = $"{Resources.Tabs_ClassroomEdit}: {_teacher.Name}";
}
} }
} }
} }
@@ -117,13 +130,15 @@ namespace TimetableDesigner.ViewModels
#region CONSTRUCTORS #region CONSTRUCTORS
public TeacherEditTabViewModel() : this(new TeacherViewModel(new Core.Teacher()), new TimetableTemplateViewModel(new Core.TimetableTemplate())) public TeacherEditViewModel() : this(new TeacherViewModel(new Teacher()))
{ } { }
public TeacherEditTabViewModel(TeacherViewModel teacher, TimetableTemplateViewModel timetableTemplate) : base() public TeacherEditViewModel(TeacherViewModel teacher)
{ {
_messageBoxService = ServiceProvider.Instance.GetService<IMessageBoxService>();
_projectService = ServiceProvider.Instance.GetService<IProjectService>();
_teacher = teacher; _teacher = teacher;
_timetableTemplate = timetableTemplate;
AddDayCommand = new RelayCommand<object>(param => AddDay()); AddDayCommand = new RelayCommand<object>(param => AddDay());
RemoveDayCommand = new RelayCommand<TimetableDay>(RemoveDay); RemoveDayCommand = new RelayCommand<TimetableDay>(RemoveDay);
@@ -161,7 +176,7 @@ namespace TimetableDesigner.ViewModels
TimeOnly to = new TimeOnly(NewHourTo.Value.Hour, NewHourTo.Value.Minute); TimeOnly to = new TimeOnly(NewHourTo.Value.Hour, NewHourTo.Value.Minute);
if (from >= to) if (from >= to)
{ {
MessageBoxService.ShowError(Resources.TeacherEdit_Message_FromHigherThanTo); _messageBoxService.ShowError(Resources.TeacherEdit_Message_FromHigherThanTo);
return; return;
} }
@@ -171,7 +186,7 @@ namespace TimetableDesigner.ViewModels
} }
catch (ArgumentException) catch (ArgumentException)
{ {
MessageBoxService.ShowError(Resources.TeacherEdit_Message_HourCollision); _messageBoxService.ShowError(Resources.TeacherEdit_Message_HourCollision);
} }
} }
NotifyPropertyChanged(nameof(SelectedDayHours)); NotifyPropertyChanged(nameof(SelectedDayHours));

View File

@@ -3,15 +3,15 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using TimetableDesigner.ViewModels.Base; using TimetableDesigner.Customs;
namespace TimetableDesigner.ViewModels namespace TimetableDesigner.ViewModels.Views
{ {
public class WelcomeTabViewModel : BaseTabViewModel public class WelcomeViewModel : BaseViewViewModel
{ {
#region CONSTRUCTORS #region CONSTRUCTORS
public WelcomeTabViewModel() public WelcomeViewModel()
{ } { }
#endregion #endregion

View File

@@ -1,35 +0,0 @@
<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,51 @@
<UserControl x:Class="TimetableDesigner.Views.ClassroomEditView"
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.Views"
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:ClassroomEditViewModel/>
</UserControl.DataContext>
<ScrollViewer>
<StackPanel>
<GroupBox VerticalAlignment="Top" Header="{x:Static p:Resources.ClassroomEdit_Groups_Basics}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="{x:Static p:Resources.ClassroomEdit_Name}"/>
<TextBox Grid.Column="1" Grid.Row="0" Margin="5" Text="{Binding Name}" MaxLines="1"/>
<Label Grid.Column="0" Grid.Row="1" Content="{x:Static p:Resources.ClassroomEdit_Description}"/>
<TextBox Grid.Column="1" Grid.Row="1" Margin="5" MinLines="3" Text="{Binding Classroom.Description}" AcceptsReturn="True"/>
</Grid>
</GroupBox>
<GroupBox VerticalAlignment="Top" Header="{x:Static p:Resources.ClassroomEdit_Groups_Capacity}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<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>
</GroupBox>
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@@ -15,9 +15,9 @@ using System.Windows.Shapes;
namespace TimetableDesigner.Views namespace TimetableDesigner.Views
{ {
public partial class ProjectSettingsTabView : UserControl public partial class ClassroomEditView : UserControl
{ {
public ProjectSettingsTabView() public ClassroomEditView()
{ {
InitializeComponent(); InitializeComponent();
} }

View File

@@ -0,0 +1,72 @@
<UserControl x:Class="TimetableDesigner.Views.GroupEditView"
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:p = "clr-namespace:TimetableDesigner.Properties"
xmlns:vm="clr-namespace:TimetableDesigner.ViewModels.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TimetableDesigner.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<vm:GroupEditViewModel/>
</UserControl.DataContext>
<ScrollViewer>
<StackPanel>
<GroupBox Header="{x:Static p:Resources.GroupEdit_Groups_Basics}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="{x:Static p:Resources.GroupEdit_Name}"/>
<TextBox Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="0" Margin="5" Text="{Binding Name}" MaxLines="1"/>
<Label Grid.Column="0" Grid.Row="1" Content="{x:Static p:Resources.GroupEdit_Description}"/>
<TextBox Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="1" Margin="5" MinLines="3" Text="{Binding Group.Description}"/>
</Grid>
</GroupBox>
<GroupBox Header="{x:Static p:Resources.GroupEdit_Groups_Subgroups}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Margin="5" MinHeight="100" HorizontalContentAlignment="Stretch" ItemsSource="{Binding Subgroups}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Key.Name}"
IsChecked="{Binding Value}"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=DataContext}"
Command="{Binding DataContext.EditSubgroupAssignmentCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding Key}">
<CheckBox.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.GroupEdit_DeleteSubgroup}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.Tag.DeleteSubgroupCommand}"
CommandParameter="{Binding Key}"/>
</ContextMenu>
</CheckBox.ContextMenu>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Label Grid.Row="2" Grid.Column="0" Margin="5" Content="{x:Static p:Resources.GroupEdit_NewSubgroupLabel}"/>
<TextBox Grid.Row="2" Grid.Column="1" MaxLines="1" Margin="5" Text="{Binding NewSubgroupName}"/>
<Button Grid.Row="2" Grid.Column="2" Margin="5" Command="{Binding AddSubgroupCommand}">
<Image Source="{StaticResource AddImage}" Width="20" Height="20"/>
</Button>
</Grid>
</GroupBox>
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@@ -15,12 +15,9 @@ using System.Windows.Shapes;
namespace TimetableDesigner.Views namespace TimetableDesigner.Views
{ {
/// <summary> public partial class GroupEditView : UserControl
/// Interaction logic for WelcomeTabView.xaml
/// </summary>
public partial class WelcomeTabView : UserControl
{ {
public WelcomeTabView() public GroupEditView()
{ {
InitializeComponent(); InitializeComponent();
} }

View File

@@ -1,8 +1,7 @@
<rib:RibbonWindow x:Class="TimetableDesigner.Views.MainWindow" <rib:RibbonWindow x:Class="TimetableDesigner.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:TimetableDesigner.ViewModels" xmlns:vm="clr-namespace:TimetableDesigner.ViewModels.Views"
xmlns:vmm="clr-namespace:TimetableDesigner.ViewModels.Models"
xmlns:p = "clr-namespace:TimetableDesigner.Properties" xmlns:p = "clr-namespace:TimetableDesigner.Properties"
xmlns:rib="urn:fluent-ribbon" xmlns:rib="urn:fluent-ribbon"
Height="450" Height="450"
@@ -10,7 +9,7 @@
<rib:RibbonWindow.Title> <rib:RibbonWindow.Title>
<MultiBinding StringFormat="Timetable Designer {0} ({1})"> <MultiBinding StringFormat="Timetable Designer {0} ({1})">
<Binding Path="Version"/> <Binding Path="Version"/>
<Binding Path="Project.Name" FallbackValue="{x:Static p:Resources.Global_NoProjectLoadedTitle}"/> <Binding Path="ProjectService.Project.Name" FallbackValue="{x:Static p:Resources.Global_NoProjectLoadedTitle}"/>
</MultiBinding> </MultiBinding>
</rib:RibbonWindow.Title> </rib:RibbonWindow.Title>
<rib:RibbonWindow.DataContext> <rib:RibbonWindow.DataContext>
@@ -33,12 +32,12 @@
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_File_NewOpen_Open}" Icon="{StaticResource OpenImage}" Command="{Binding OpenProjectCommand}"/> <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:DropDownButton Header="{x:Static p:Resources.Main_Ribbon_File_NewOpen_OpenRecent}" Icon="{StaticResource OpenRecentImage}"/>
</rib:RibbonGroupBox> </rib:RibbonGroupBox>
<rib:RibbonGroupBox Header="{x:Static p:Resources.Main_Ribbon_File_Save}" IsEnabled="{Binding Project, Converter={StaticResource IsNotNullToBooleanConverter}}"> <rib:RibbonGroupBox Header="{x:Static p:Resources.Main_Ribbon_File_Save}" IsEnabled="{Binding ProjectService.ProjectViewModel, 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_Save}" Icon="{StaticResource SaveImage}"/>
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_File_Save_SaveAs}" Icon="{StaticResource SaveAsImage}"/> <rib:Button Header="{x:Static p:Resources.Main_Ribbon_File_Save_SaveAs}" Icon="{StaticResource SaveAsImage}"/>
</rib:RibbonGroupBox> </rib:RibbonGroupBox>
</rib:RibbonTabItem> </rib:RibbonTabItem>
<rib:RibbonTabItem Header="{x:Static p:Resources.Main_Ribbon_Project}" IsEnabled="{Binding Project, Converter={StaticResource IsNotNullToBooleanConverter}}"> <rib:RibbonTabItem Header="{x:Static p:Resources.Main_Ribbon_Project}" IsEnabled="{Binding ProjectService.ProjectViewModel, Converter={StaticResource IsNotNullToBooleanConverter}}">
<rib:RibbonGroupBox Header="{x:Static p:Resources.Main_Ribbon_Project_Settings}"> <rib:RibbonGroupBox Header="{x:Static p:Resources.Main_Ribbon_Project_Settings}">
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_Settings_ProjectSettings}" <rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_Settings_ProjectSettings}"
Command="{Binding ProjectSettingsCommand}" Command="{Binding ProjectSettingsCommand}"
@@ -47,13 +46,22 @@
<rib:RibbonGroupBox Header="{x:Static p:Resources.Main_Ribbon_Project_New}"> <rib:RibbonGroupBox Header="{x:Static p:Resources.Main_Ribbon_Project_New}">
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewClassroom}" Icon="{StaticResource ClassroomAddImage}" Command="{Binding NewClassroomCommand}"/> <rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewClassroom}" Icon="{StaticResource ClassroomAddImage}" Command="{Binding NewClassroomCommand}"/>
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewTeacher}" Icon="{StaticResource TeacherAddImage}" Command="{Binding NewTeacherCommand}"/> <rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewTeacher}" Icon="{StaticResource TeacherAddImage}" Command="{Binding NewTeacherCommand}"/>
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewGroup}"/> <rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewGroup}" Icon="{StaticResource GroupAddImage}" Command="{Binding NewGroupCommand}"/>
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewSubgroup}"/> </rib:RibbonGroupBox>
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_New_NewClass}"/> <rib:RibbonGroupBox Header="{x:Static p:Resources.Main_Ribbon_Project_Import}">
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_Import_ImportClassrooms}"/>
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_Import_ImportTeachers}"/>
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Project_Import_ImportGroups}"/>
</rib:RibbonGroupBox>
</rib:RibbonTabItem>
<rib:RibbonTabItem Header="{x:Static p:Resources.Main_Ribbon_Export}" IsEnabled="{Binding ProjectService.ProjectViewModel, Converter={StaticResource IsNotNullToBooleanConverter}}">
<rib:RibbonGroupBox Header="{x:Static p:Resources.Main_Ribbon_Export_Timetable}">
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Export_Timetable_PDF}"/>
<rib:Button Header="{x:Static p:Resources.Main_Ribbon_Export_Timetable_XLS}"/>
</rib:RibbonGroupBox> </rib:RibbonGroupBox>
</rib:RibbonTabItem> </rib:RibbonTabItem>
</rib:Ribbon> </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}}"> <TreeView Grid.Row="1" Grid.Column="0" DataContext="{Binding ProjectService.ProjectViewModel}" Visibility="{Binding RelativeSource={RelativeSource Mode=Self}, Path=DataContext, Converter={StaticResource IsNullToVisibilityConverter}}">
<TreeViewItem> <TreeViewItem>
<TreeViewItem.Header> <TreeViewItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
@@ -123,24 +131,70 @@
</DataTemplate> </DataTemplate>
</TreeViewItem.ItemTemplate> </TreeViewItem.ItemTemplate>
</TreeViewItem> </TreeViewItem>
<TreeViewItem ItemsSource="{Binding Groups}">
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource GroupImage}" Width="18" Height="18"/>
<Label Content="{x:Static p:Resources.Main_Treeview_Groups}"/>
</StackPanel>
</TreeViewItem.Header>
<TreeViewItem.ItemTemplate>
<DataTemplate>
<TreeViewItem Header="{Binding Name}" ItemsSource="{Binding AssignedSubgroups}" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=rib:RibbonWindow}, Path=DataContext}">
<TreeViewItem.InputBindings>
<MouseBinding Gesture="LeftDoubleClick"/>
</TreeViewItem.InputBindings>
<TreeViewItem.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.Main_Treeview_ContextMenu_EditTimetable}"/>
<MenuItem Header="{x:Static p:Resources.Main_Treeview_Groups_ContextMenu_Edit}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.Tag.EditGroupCommand}"
CommandParameter="{Binding}"/>
<Separator/>
<MenuItem Header="{x:Static p:Resources.Main_Treeview_ContextMenu_Remove}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.Tag.RemoveGroupCommand}"
CommandParameter="{Binding}"/>
</ContextMenu>
</TreeViewItem.ContextMenu>
<TreeViewItem.ItemTemplate>
<DataTemplate>
<TreeViewItem Header="{Binding Name}" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=rib:RibbonWindow}, Path=DataContext}">
<TreeViewItem.InputBindings>
<MouseBinding Gesture="LeftDoubleClick"/>
</TreeViewItem.InputBindings>
<TreeViewItem.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.Main_Treeview_ContextMenu_EditTimetable}"/>
<Separator/>
<MenuItem Header="{x:Static p:Resources.Main_Treeview_ContextMenu_Remove}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.Tag.RemoveSubgroupCommand}"
CommandParameter="{Binding}"/>
</ContextMenu>
</TreeViewItem.ContextMenu>
</TreeViewItem>
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
</TreeViewItem> </TreeViewItem>
</TreeView> </TreeView>
<GridSplitter Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Stretch" ShowsPreview="True" Width="2"/> <GridSplitter Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Stretch" ShowsPreview="True" Width="2"/>
<TabControl Grid.Row="1" Grid.Column="2" ItemsSource="{Binding Tabs}" SelectedItem="{Binding SelectedTab}"> <TabControl Grid.Row="1" Grid.Column="2" DataContext="{Binding TabNavigationService}" ItemsSource="{Binding Tabs}" SelectedItem="{Binding SelectedTab, Mode=OneWay}">
<TabControl.ItemTemplate> <TabControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Image Source="{Binding TabImage}" <Image Source="{Binding Image}"
Visibility="{Binding TabImage, Converter={StaticResource IsNullToVisibilityConverter}}" Visibility="{Binding Image, Converter={StaticResource IsNullToVisibilityConverter}}"
Width="15" Width="15"
Height="15"/> Height="15"/>
<TextBlock Text="{Binding TabTitle}"/> <TextBlock Text="{Binding Title}"/>
<Button BorderThickness="0" <Button BorderThickness="0"
Background="Transparent" Background="Transparent"
Margin="5,0,0,0" Margin="5,0,0,0"
Visibility="{Binding IsTabClosable, Converter={StaticResource boolToVisibilityConverter}}" Visibility="{Binding IsClosable, Converter={StaticResource boolToVisibilityConverter}}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=DataContext.CloseTabCommand}" Command="{Binding CloseCommand}">
CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=DataContext}">
<Image Source="{StaticResource CloseImage}" <Image Source="{StaticResource CloseImage}"
Width="15" Width="15"
Height="15"/> Height="15"/>
@@ -150,7 +204,7 @@
</TabControl.ItemTemplate> </TabControl.ItemTemplate>
<TabControl.ContentTemplate> <TabControl.ContentTemplate>
<DataTemplate> <DataTemplate>
<ContentControl Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=DataContext.SelectedTab, UpdateSourceTrigger = PropertyChanged, Converter={StaticResource ViewModelToViewConverter}}"/> <ContentControl Content="{Binding ViewModel, Converter={StaticResource ViewModelToViewConverter}}"/>
</DataTemplate> </DataTemplate>
</TabControl.ContentTemplate> </TabControl.ContentTemplate>
</TabControl> </TabControl>

View File

@@ -1,15 +1,15 @@
<UserControl x:Class="TimetableDesigner.Views.ProjectSettingsTabView" <UserControl x:Class="TimetableDesigner.Views.ProjectSettingsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:TimetableDesigner.ViewModels" xmlns:vm="clr-namespace:TimetableDesigner.ViewModels.Views"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:p = "clr-namespace:TimetableDesigner.Properties" xmlns:p = "clr-namespace:TimetableDesigner.Properties"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext> <UserControl.DataContext>
<vm:ProjectSettingsTabViewModel/> <vm:ProjectSettingsViewModel/>
</UserControl.DataContext> </UserControl.DataContext>
<ScrollViewer> <ScrollViewer>
<StackPanel> <StackPanel>
@@ -44,7 +44,7 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Label Content="{x:Static p:Resources.ProjectSettings_Days}"/> <Label Content="{x:Static p:Resources.ProjectSettings_Days}"/>
<Border Grid.Row="1" Grid.Column="0" Margin="5" BorderThickness="1" BorderBrush="DarkGray"> <Border Grid.Row="1" Grid.Column="0" Margin="5" BorderThickness="0.3" BorderBrush="Black">
<ItemsControl ItemsSource="{Binding Project.TimetableTemplate.Days}" HorizontalContentAlignment="Stretch" MinHeight="100"> <ItemsControl ItemsSource="{Binding Project.TimetableTemplate.Days}" HorizontalContentAlignment="Stretch" MinHeight="100">
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
@@ -77,7 +77,7 @@
</Button> </Button>
</Grid> </Grid>
<Label Grid.Row="0" Grid.Column="1" Content="{x:Static p:Resources.ProjectSettings_Hours}"/> <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"> <Border Grid.Row="1" Grid.Column="1" Margin="5" BorderThickness="0.3" BorderBrush="Black">
<ItemsControl ItemsSource="{Binding Project.TimetableTemplate.Slots}" HorizontalContentAlignment="Stretch" MinHeight="100"> <ItemsControl ItemsSource="{Binding Project.TimetableTemplate.Slots}" HorizontalContentAlignment="Stretch" MinHeight="100">
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>

View File

@@ -15,12 +15,9 @@ using System.Windows.Shapes;
namespace TimetableDesigner.Views namespace TimetableDesigner.Views
{ {
/// <summary> public partial class ProjectSettingsView : UserControl
/// Interaction logic for TeacherEditTabView.xaml
/// </summary>
public partial class TeacherEditTabView : UserControl
{ {
public TeacherEditTabView() public ProjectSettingsView()
{ {
InitializeComponent(); InitializeComponent();
} }

View File

@@ -1,18 +1,19 @@
<UserControl x:Class="TimetableDesigner.Views.TeacherEditTabView" <UserControl x:Class="TimetableDesigner.Views.TeacherEditView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:p = "clr-namespace:TimetableDesigner.Properties" xmlns:p = "clr-namespace:TimetableDesigner.Properties"
xmlns:vm="clr-namespace:TimetableDesigner.ViewModels" xmlns:vm="clr-namespace:TimetableDesigner.ViewModels.Views"
xmlns:local="clr-namespace:TimetableDesigner.Views" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:local="clr-namespace:TimetableDesigner.Views" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext> <UserControl.DataContext>
<vm:TeacherEditTabViewModel/> <vm:TeacherEditViewModel/>
</UserControl.DataContext> </UserControl.DataContext>
<ScrollViewer> <ScrollViewer>
<StackPanel> <StackPanel>
<GroupBox Header="{x:Static p:Resources.TeacherEdit_Groups_Basics}">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
@@ -23,12 +24,13 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="{x:Static p:Resources.TeacherEdit_Name}"/> <Label Grid.Column="0" Grid.Row="0" Content="{x:Static p:Resources.TeacherEdit_Name}"/>
<TextBox Grid.Column="1" Grid.Row="0" Margin="5" Text="{Binding Teacher.Name}" MaxLines="1"/> <TextBox Grid.Column="1" Grid.Row="0" Margin="5" Text="{Binding Name}" MaxLines="1"/>
<Label Grid.Column="0" Grid.Row="1" Content="{x:Static p:Resources.TeacherEdit_Description}"/> <Label Grid.Column="0" Grid.Row="1" Content="{x:Static p:Resources.TeacherEdit_Description}"/>
<TextBox Grid.Column="1" Grid.Row="1" Margin="5" MinLines="3" Text="{Binding Teacher.Description}"/> <TextBox Grid.Column="1" Grid.Row="1" Margin="5" MinLines="3" Text="{Binding Teacher.Description}" AcceptsReturn="True"/>
</Grid> </Grid>
</GroupBox>
<GroupBox Header="{x:Static p:Resources.TeacherEdit_Groups_AvailabilityHours}">
<StackPanel> <StackPanel>
<Label Content="{x:Static p:Resources.TeacherEdit_AvailabilityHours}"/>
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition/> <ColumnDefinition/>
@@ -38,9 +40,11 @@
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Border Grid.Column="0" Grid.Row="0" Margin="5" BorderThickness="1" BorderBrush="DarkGray"> <Label Grid.Column="0" Grid.Row="0" Content="{x:Static p:Resources.TeacherEdit_Days}"/>
<ListBox MinHeight="100" ItemsSource="{Binding Teacher.AvailabilityHours.Keys}" SelectedItem="{Binding SelectedDay}" HorizontalContentAlignment="Stretch"> <Label Grid.Column="2" Grid.Row="0" Content="{x:Static p:Resources.TeacherEdit_Hours}"/>
<ListBox Grid.Column="0" Grid.Row="1" Margin="5" MinHeight="100" ItemsSource="{Binding Teacher.AvailabilityHours.Keys}" SelectedItem="{Binding SelectedDay}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate> <DataTemplate>
<Grid HorizontalAlignment="Stretch" Margin="5"> <Grid HorizontalAlignment="Stretch" Margin="5">
@@ -60,10 +64,8 @@
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>
</ListBox> </ListBox>
</Border> <Image Grid.Column="1" Grid.Row="1" VerticalAlignment="Center" Source="{StaticResource RightArrowImage}" Width="20" Height="20"/>
<Image Grid.Column="1" Grid.Row="0" VerticalAlignment="Center" Source="{StaticResource RightArrowImage}" Width="20" Height="20"/> <ListBox Grid.Column="2" Grid.Row="1" Margin="5" ItemsSource="{Binding SelectedDayHours}" HorizontalContentAlignment="Stretch">
<Border Grid.Column="2" Grid.Row="0" Margin="5" BorderThickness="1" BorderBrush="DarkGray">
<ListBox ItemsSource="{Binding SelectedDayHours}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate> <DataTemplate>
<Grid HorizontalAlignment="Stretch" Margin="5"> <Grid HorizontalAlignment="Stretch" Margin="5">
@@ -87,18 +89,17 @@
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>
</ListBox> </ListBox>
</Border> <Grid Grid.Column="0" Grid.Row="2">
<Grid Grid.Column="0" Grid.Row="1">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition/> <ColumnDefinition/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<ComboBox Grid.Column="0" Margin="5" ItemsSource="{Binding TimetableTemplate.Days}" SelectedItem="{Binding SelectedNewDay}" DisplayMemberPath="Name"/> <ComboBox Grid.Column="0" Margin="5" ItemsSource="{Binding TimetableTemplate.Days}" SelectedItem="{Binding SelectedNewDay}" DisplayMemberPath="Name" VerticalContentAlignment="Center"/>
<Button Grid.Column="1" Margin="5" Width="26" Height="26" Command="{Binding AddDayCommand}"> <Button Grid.Column="1" Margin="5" Width="26" Height="26" Command="{Binding AddDayCommand}">
<Image Source="{StaticResource AddImage}" Width="20" Height="20"/> <Image Source="{StaticResource AddImage}" Width="20" Height="20"/>
</Button> </Button>
</Grid> </Grid>
<Grid Grid.Column="2" Grid.Row="1"> <Grid Grid.Column="2" Grid.Row="2">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition/> <ColumnDefinition/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
@@ -114,6 +115,7 @@
</Grid> </Grid>
</Grid> </Grid>
</StackPanel> </StackPanel>
</GroupBox>
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
</UserControl> </UserControl>

View File

@@ -15,12 +15,9 @@ using System.Windows.Shapes;
namespace TimetableDesigner.Views namespace TimetableDesigner.Views
{ {
/// <summary> public partial class TeacherEditView : UserControl
/// Interaction logic for ClassroomEditTabView.xaml
/// </summary>
public partial class ClassroomEditTabView : UserControl
{ {
public ClassroomEditTabView() public TeacherEditView()
{ {
InitializeComponent(); InitializeComponent();
} }

View File

@@ -1,14 +1,16 @@
<UserControl x:Class="TimetableDesigner.Views.WelcomeTabView" <UserControl x:Class="TimetableDesigner.Views.WelcomeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:TimetableDesigner.ViewModels" xmlns:vm="clr-namespace:TimetableDesigner.ViewModels.Views"
xmlns:c="clr-namespace:TimetableDesigner.Controls"
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.DataContext> <UserControl.DataContext>
<vm:WelcomeTabViewModel/> <vm:WelcomeViewModel/>
</UserControl.DataContext> </UserControl.DataContext>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Label Content="Open or create new project to begin"/> <Label Content="Open or create new project to begin"/>
<c:TimetableEditorControl/>
</StackPanel> </StackPanel>
</UserControl> </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 WelcomeView : UserControl
{
public WelcomeView()
{
InitializeComponent();
}
}
}