refactor: use WPF binding instead of old way for updating fields

This commit is contained in:
2025-09-04 21:56:31 +08:00
parent d81dcecc4e
commit 26737c1695
10 changed files with 376 additions and 436 deletions

View File

@ -14,24 +14,29 @@
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="所有数据均为即时保存(编辑即保存)" Grid.Row="0" Foreground="DarkGreen" Background="Cyan" Padding="10"/>
<TextBlock Text="所有数据均为即时保存(编辑即保存)" Grid.Row="0" Foreground="#CDDC39" Background="#0097A7" Padding="10"/>
<TabControl Grid.Row="1">
<TabItem Header="课程基本信息" Padding="5">
<ScrollViewer>
<StackPanel Orientation="Vertical" Margin="10,0,10,0">
<TextBlock Text="课程名称" FontWeight="Bold" Margin="0,10,0,0"/>
<TextBox x:Name="uiCourseName" Text="{Binding name}" Width="200" HorizontalAlignment="Left" Margin="0,5,0,0"/>
<TextBox Text="{Binding name, Mode=TwoWay}" Width="200" HorizontalAlignment="Left" Margin="0,5,0,0"/>
<TextBlock Text="课程颜色" FontWeight="Bold" Margin="0,10,0,0"/>
<Rectangle x:Name="uiCourseColor" Height="20" Width="200" HorizontalAlignment="Left" Stroke="Black" StrokeThickness="1" Cursor="Hand" Margin="0,5,0,0">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding color}"/>
</Rectangle.Fill>
</Rectangle>
<TextBlock Text="课程颜色" FontWeight="Bold" Padding="5" Margin="0,10,0,0"/>
<Border ToolTip="单击改变颜色" Width="200" HorizontalAlignment="Left" BorderBrush="Black" BorderThickness="1" Cursor="Hand" Margin="0,5,0,0">
<Border.Background>
<SolidColorBrush Color="{Binding color.background, Mode=TwoWay}"/>
</Border.Background>
<TextBlock Text="示例颜色" Margin="5">
<TextBlock.Foreground>
<SolidColorBrush Color="{Binding color.foreground, Mode=TwoWay}"/>
</TextBlock.Foreground>
</TextBlock>
</Border>
<TextBlock Text="注释" FontWeight="Bold" Margin="0,10,0,0"/>
<TextBox x:Name="uiCourseDescription" Text="{Binding description}" Height="100" Width="400" HorizontalAlignment="Left" AcceptsReturn="True" Margin="0,5,0,0"/>
<TextBox Text="{Binding description, Mode=TwoWay}" Height="100" Width="400" HorizontalAlignment="Left" AcceptsReturn="True" Padding="5" Margin="0,5,0,0"/>
</StackPanel>
</ScrollViewer>
</TabItem>

View File

@ -14,24 +14,24 @@
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="所有数据均为即时保存(编辑即保存)" Grid.Row="0" Foreground="DarkGreen" Background="Cyan" Padding="10"/>
<TextBlock Text="所有数据均为即时保存(编辑即保存)" Grid.Row="0" Foreground="#CDDC39" Background="#0097A7" Padding="10"/>
<ScrollViewer Grid.Row="1">
<StackPanel Orientation="Vertical" Margin="10,0,10,0">
<TextBlock Text="周设置" FontWeight="Bold" Margin="0,10,0,0"/>
<TextBlock Text="输入格式:开始周-结束周 或 周号1,周号2,周号3" Margin="0,5,0,0"/>
<TextBlock Text="例如3-11 或 1,3,5" Margin="0,5,0,0"/>
<TextBox x:Name="uiScheduleWeek" Text="{Binding week}" Width="200" HorizontalAlignment="Left" Margin="0,5,0,0"/>
<TextBox Text="{Binding week, Mode=TwoWay}" Width="200" HorizontalAlignment="Left" Padding="5" Margin="0,5,0,0"/>
<TextBlock Text="课程星期" FontWeight="Bold" Margin="0,10,0,0"/>
<TextBlock Text="输入格式:开始星期-结束星期 或 星期号1,星期号2,星期号3 或 星期号" Margin="0,5,0,0"/>
<TextBlock Text="例如3-7 或 1,3,5 或 6" Margin="0,5,0,0"/>
<TextBox x:Name="uiScheduleDay" Text="{Binding day}" Width="200" HorizontalAlignment="Left" Margin="0,5,0,0"/>
<TextBox Text="{Binding day, Mode=TwoWay}" Width="200" HorizontalAlignment="Left" Padding="5" Margin="0,5,0,0"/>
<TextBlock Text="当日课程节次" FontWeight="Bold" Margin="0,10,0,0"/>
<TextBlock Text="输入格式:开始节次-结束节次 或 节次号1,节次号2,节次号3" Margin="0,5,0,0"/>
<TextBlock Text="例如3-11 或 1,3,5" Margin="0,5,0,0"/>
<TextBox x:Name="uiScheduleIndex" Text="{Binding index}" Width="200" HorizontalAlignment="Left" Margin="0,5,0,0"/>
<TextBox Text="{Binding index, Mode=TwoWay}" Width="200" HorizontalAlignment="Left" Padding="5" Margin="0,5,0,0"/>
</StackPanel>
</ScrollViewer>
</Grid>

View File

@ -92,6 +92,8 @@
</Compile>
<Compile Include="SimulationCore.cs" />
<Compile Include="SimulationItem.cs" />
<Compile Include="Util\ColorPreset.cs" />
<Compile Include="Util\Win32Dialog.cs" />
<Page Include="Dialog\EditCourse.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View File

@ -17,7 +17,8 @@ namespace HFUTCourseSimulation.Kernel {
public UserData.Semester currentSemester;
public UserData.Course currentCourse;
public UserData.Schedule currentSchedule;
public UserData.ColorPair currentColor;
public string currentFilePath;
}
}

View File

@ -4,7 +4,6 @@ using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
namespace HFUTCourseSimulation.Kernel.SimData {
@ -106,7 +105,7 @@ namespace HFUTCourseSimulation.Kernel.SimData {
/// <summary>
/// 课程的颜色
/// </summary>
public readonly Color color;
public readonly UserData.ColorPair color;
/// <summary>
/// 课程的起始节次。
/// 该类保证该值位于1到indexCount之间含首尾

View File

@ -1,11 +1,12 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Globalization;
using Newtonsoft.Json;
namespace HFUTCourseSimulation.Kernel.UserData {
@ -28,44 +29,44 @@ namespace HFUTCourseSimulation.Kernel.UserData {
/// </summary>
[JsonProperty("start_date")]
[JsonConverter(typeof(CustomDateTimeConverter))]
public DateTime startDate;
public DateTime startDate { get; set; }
/// <summary>
/// 教学周个数
/// </summary>
[JsonProperty("week_count")]
public string weekCount;
public string weekCount { get; set; }
/// <summary>
/// 每天课程的节次数
/// </summary>
[JsonProperty("index_count")]
public string indexCount;
public string indexCount { get; set; }
/// <summary>
/// 早餐插入在第几节次后
/// </summary>
[JsonProperty("breakfast_at")]
public string breakfastAt;
public string breakfastAt { get; set; }
/// <summary>
/// 午餐插入在第几节次后
/// </summary>
[JsonProperty("lunch_at")]
public string lunchAt;
public string lunchAt { get; set; }
/// <summary>
/// 晚餐插入在第几节次后
/// </summary>
[JsonProperty("dinner_at")]
public string dinnerAt;
public string dinnerAt { get; set; }
/// <summary>
/// 课程列表
/// </summary>
[JsonProperty("courses")]
public List<Course> courses;
public List<Course> courses { get; set; }
}
public class Course {
public Course() {
name = "";
description = "";
color = Color.LightBlue;
color = Util.ColorPreset.MdColors.Indigo;
schedules = new List<Schedule>();
}
@ -73,23 +74,22 @@ namespace HFUTCourseSimulation.Kernel.UserData {
/// 课程的名称
/// </summary>
[JsonProperty("name")]
public string name;
public string name { get; set; }
/// <summary>
/// 课程的说明,例如教室位置,教师姓名等。
/// </summary>
[JsonProperty("description")]
public string description;
public string description { get; set; }
/// <summary>
/// 课程的颜色
/// </summary>
[JsonProperty("color")]
[JsonConverter(typeof(CustomColorConverter))]
public Color color;
public ColorPair color { get; set; }
/// <summary>
/// 课程的所有安排
/// </summary>
[JsonProperty("schedules")]
public List<Schedule> schedules;
public List<Schedule> schedules { get; set; }
}
public class Schedule {
@ -103,26 +103,49 @@ namespace HFUTCourseSimulation.Kernel.UserData {
/// 安排在哪些周
/// </summary>
[JsonProperty("week")]
public string week;
public string week { get; set; }
/// <summary>
/// 安排在周的星期几
/// </summary>
[JsonProperty("day")]
public string day;
public string day { get; set; }
/// <summary>
/// 安排在这些日子的哪些节次
/// </summary>
[JsonProperty("index")]
public string index;
public string index { get; set; }
}
/// <summary>
/// 代表一个颜色对,包括前景色和背景色
/// </summary>
public struct ColorPair {
public ColorPair(Color foreground, Color background) {
this.foreground = foreground;
this.background = background;
}
/// <summary>
/// 前景色(文本的颜色)
/// </summary>
[JsonProperty("foreground")]
[JsonConverter(typeof(CustomColorConverter))]
public Color foreground { get; set; }
/// <summary>
/// 背景色
/// </summary>
[JsonProperty("background")]
[JsonConverter(typeof(CustomColorConverter))]
public Color background { get; set; }
}
internal class CustomDateTimeConverter : JsonConverter<DateTime> {
private static readonly string DATETIME_FORMAT = "yyyy-MM-dd";
private static readonly string DatetimeFormat = "yyyy-MM-dd";
public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer) {
if (reader.TokenType == JsonToken.String) {
var value = reader.Value as string;
if (DateTime.TryParseExact(value, DATETIME_FORMAT, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime rv)) {
if (DateTime.TryParseExact(value, DatetimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime rv)) {
return rv;
} else {
throw new JsonSerializationException($"given string can not be parsed as DateTime: {value}");
@ -133,7 +156,7 @@ namespace HFUTCourseSimulation.Kernel.UserData {
}
public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer) {
writer.WriteValue(value.ToString(DATETIME_FORMAT, CultureInfo.InvariantCulture));
writer.WriteValue(value.ToString(DatetimeFormat, CultureInfo.InvariantCulture));
}
}

View File

@ -4,7 +4,9 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:HFUTCourseSimulation"
xmlns:userdata="clr-namespace:HFUTCourseSimulation.Kernel.UserData"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance userdata:Semester}"
Title="HFUT课表模拟" Height="600" Width="800">
<Grid>
<Grid.RowDefinitions>
@ -15,17 +17,23 @@
<Menu IsMainMenu="True" Grid.Row="0">
<MenuItem Header="文件" Padding="5">
<MenuItem x:Name="uiMenuNew" Header="新建" Click="uiMenuNew_Click" />
<MenuItem x:Name="uiMenuOpen" Header="打开" Click="uiMenuOpen_Click" />
<MenuItem x:Name="uiMenuOpen" Header="打开" Click="uiMenuOpen_Click"/>
<MenuItem x:Name="uiMenuOpenOld" Header="打开旧版">
<MenuItem x:Name="uiMenuOpenV1" Header="打开V1版本" Click="uiMenuOpenV1_Click"/>
</MenuItem>
<Separator/>
<MenuItem x:Name="uiMenuSave" Header="保存" Click="uiMenuSave_Click"/>
<MenuItem x:Name="uiMenuSaveAs" Header="另存为" Click="uiMenuSave_Click"/>
<Separator/>
<MenuItem x:Name="uiMenuClose" Header="关闭" Click="uiMenuClose_Click"/>
<MenuItem x:Name="uiMenuQuit" Header="退出" Click="uiMenuQuit_Click"/>
</MenuItem>
<MenuItem Header="输出" Padding="5">
<MenuItem x:Name="uiMenuSimulator" Header="实时模拟" Click="uiMenuSimulator_Click"/>
<MenuItem x:Name="uiMenuRender" Header="导出为图片" Click="uiMenuRender_Click"/>
</MenuItem>
<MenuItem Header="关于" Padding="5">
<MenuItem x:Name="uiMenuAbout" Header="关于" Click="uiMenuAbout_Click"/>
<MenuItem x:Name="uiMenuAbout" Header="关于本程序" Click="uiMenuAbout_Click"/>
</MenuItem>
</Menu>
@ -37,124 +45,39 @@
<ScrollViewer>
<StackPanel Orientation="Vertical" Margin="10,0,10,0">
<TextBlock Text="起始日期(教学第一周星期一的日期)" Margin="0,20,0,0"/>
<DatePicker x:Name="uiStartDate" Height="25" Width="200" Margin="0,5,0,0" HorizontalAlignment="Left" SelectedDateChanged="uiStartDate_SelectedDateChanged"/>
<DatePicker SelectedDate="{Binding startDate}" Height="25" Width="200" Margin="0,5,0,0" HorizontalAlignment="Left"/>
<TextBlock Text="本学期教学周数" Margin="0,20,0,0"/>
<TextBox x:Name="uiWeekCount" Width="200" Margin="0,5,0,0" HorizontalAlignment="Left" TextChanged="uiWeekCount_TextChanged"/>
<TextBox Text="{Binding weekCount, Mode=TwoWay}" Width="200" Padding="5" Margin="0,5,0,0" HorizontalAlignment="Left"/>
<TextBlock Text="每天节次数" Margin="0,20,0,0"/>
<TextBox x:Name="uiIndexCount" Width="200" Margin="0,5,0,0" HorizontalAlignment="Left"/>
<TextBox Text="{Binding indexCount, Mode=TwoWay}" Width="200" Padding="5" Margin="0,5,0,0" HorizontalAlignment="Left"/>
<TextBlock Text="早餐在哪个节次后" Margin="0,10,0,0"/>
<TextBox x:Name="uiBreakfastAt" Width="200" Margin="0,5,0,0" HorizontalAlignment="Left"/>
<TextBox Text="{Binding breakfastAt, Mode=TwoWay}" Width="200" Padding="5" Margin="0,5,0,0" HorizontalAlignment="Left"/>
<TextBlock Text="午餐在哪个节次后" Margin="0,10,0,0"/>
<TextBox x:Name="uiLunchAt" Width="200" Margin="0,5,0,0" HorizontalAlignment="Left"/>
<TextBox Text="{Binding lunchAt, Mode=TwoWay}" Width="200" Padding="5" Margin="0,5,0,0" HorizontalAlignment="Left"/>
<TextBlock Text="晚餐在哪个节次后" Margin="0,10,0,0"/>
<TextBox x:Name="uiDinnerAt" Width="200" Margin="0,5,0,0" HorizontalAlignment="Left"/>
<TextBox Text="{Binding dinnerAt, Mode=TwoWay}" Width="200" Padding="5" Margin="0,5,0,0" HorizontalAlignment="Left"/>
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="课程设置" Padding="5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Background="LightYellow">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Text="课程列表" HorizontalAlignment="Center" Margin="0,10,0,10"/>
<ListBox x:Name="uiCoursesList" Margin="5" Grid.Row="1" Background="#00000000" MouseDoubleClick="uiCoursesList_MouseDoubleClick"/>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="uiCourseBtnAdd" Content="添加" ToolTip="将右侧列表所填写的数据作为一个新项插入列表" Margin="5" Click="uiCourseBtnAdd_Click"/>
<Button x:Name="uiCourseBtnUpdate" Content="更新" Grid.Column="1" ToolTip="将右侧列表中的内容更新到列表的选中项中" Margin="5" Click="uiCourseBtnUpdate_Click"/>
<Button x:Name="uiCourseBtnDelete" Content="删除" Grid.Column="2" ToolTip="删除列表选中项" Margin="5" Click="uiCourseBtnDelete_Click"/>
</Grid>
</Grid>
<ScrollViewer Grid.Column="1">
<StackPanel Orientation="Vertical">
<GroupBox Header="基本信息" Margin="5">
<StackPanel Orientation="Vertical">
<TextBlock Text="课程名称" Margin="10,10,10,5"/>
<TextBox x:Name="uiCourseName" Width="200" HorizontalAlignment="Left" Margin="10,0,10,10" TextChanged="uiCourseName_TextChanged"/>
<TextBlock Text="课程颜色" Margin="10,10,10,5"/>
<Border x:Name="uiCourseColor" Height="20" Width="200" HorizontalAlignment="Left" Margin="10,0,10,10" BorderBrush="Black" BorderThickness="1"/>
<Button x:Name="uiCourseBtnColor" Content="选择颜色" HorizontalAlignment="Left" Margin="10,0,10,10" Click="uiCourseBtnColor_Click"/>
<TextBlock Text="注释" Margin="10,10,10,5"/>
<TextBox x:Name="uiCourseDescription" Height="100" Width="400" HorizontalAlignment="Left" Margin="10,0,10,10" AcceptsReturn="True" TextChanged="uiCourseDescription_TextChanged"/>
</StackPanel>
</GroupBox>
<GroupBox Header="课时设置" Margin="5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Background="LightYellow">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Text="课时安排" HorizontalAlignment="Center" Margin="0,10,0,10"/>
<ListBox x:Name="uiScheduleList" Margin="5" Grid.Row="1" Background="#00000000" MinHeight="200" MouseDoubleClick="uiScheduleList_MouseDoubleClick"/>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="uiScheduleBtnAdd" Content="添加" ToolTip="将右侧列表所填写的数据作为一个新项插入列表" Margin="5" Click="uiScheduleBtnAdd_Click"/>
<Button x:Name="uiScheduleBtnUpdate" Content="更新" Grid.Column="1" ToolTip="将右侧列表中的内容更新到列表的选中项中" Margin="5" Click="uiScheduleBtnUpdate_Click"/>
<Button x:Name="uiScheduleBtnDelete" Content="删除" Grid.Column="2" ToolTip="删除列表选中项" Margin="5" Click="uiScheduleBtnDelete_Click"/>
</Grid>
</Grid>
<StackPanel Grid.Column="1" Orientation="Vertical">
<TextBlock Text="周设置" Margin="10,10,10,5"/>
<TextBlock Text="输入格式:开始周-结束周 或 周号1,周号2,周号3" Margin="10,5,10,5"/>
<TextBlock Text="例如3-11 或 1,3,5" Margin="10,5,10,5"/>
<TextBox x:Name="uiScheduleWeek" Width="200" HorizontalAlignment="Left" Margin="10,0,10,10" TextChanged="uiScheduleWeek_TextChanged"/>
<TextBlock Text="课程星期" Margin="10,10,10,5"/>
<TextBlock Text="输入格式:开始星期-结束星期 或 星期号1,星期号2,星期号3 或 星期号" Margin="10,5,10,5"/>
<TextBlock Text="例如3-7 或 1,3,5 或 6" Margin="10,5,10,5"/>
<TextBox x:Name="uiScheduleDay" Width="200" HorizontalAlignment="Left" Margin="10,0,10,10" TextChanged="uiScheduleDay_TextChanged"/>
<TextBlock Text="当日课程节次" Margin="10,10,10,5"/>
<TextBlock Text="输入格式:开始节次-结束节次 或 节次号1,节次号2,节次号3" Margin="10,5,10,5"/>
<TextBlock Text="例如3-11 或 1,3,5" Margin="10,5,10,5"/>
<TextBox x:Name="uiScheduleIndex" Width="200" HorizontalAlignment="Left" Margin="10,0,10,10" TextChanged="uiScheduleIndex_TextChanged"/>
</StackPanel>
</Grid>
</GroupBox>
</StackPanel>
</ScrollViewer>
</Grid>
<ListBox x:Name="uiCoursesList" ItemsSource="{Binding courses}" Margin="10" MouseDoubleClick="uiCoursesList_MouseDoubleClick">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid d:DataContext="{d:DesignInstance userdata:Course}">
<TextBlock Text="{Binding name}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem x:Name="uiCtxMenuNewCourse" Header="插入新课程" Click="uiCtxMenuNewCourse_Click"/>
<MenuItem x:Name="uiCtxMenuEditCourse" Header="编辑选中" Click="uiCtxMenuEditCourse_Click"/>
<MenuItem x:Name="uiCtxMenuDeleteCourse" Header="删除选中" Click="uiCtxMenuDeleteCourse_Click"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
</TabItem>
</TabControl>

View File

@ -22,134 +22,186 @@ namespace HFUTCourseSimulation {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
UpdateUI();
this.context = Kernel.Context.Instance;
UpdateUiLayout();
}
bool isUpdatingData = false;
#region Context and Assistant Functions
void UpdateUI() {
if (General.FilePath == "") {
//no file
uiMenuNew.IsEnabled = true;
uiMenuOpen.IsEnabled = true;
uiMenuSave.IsEnabled = false;
uiMenuClose.IsEnabled = false;
uiMenuSimulator.IsEnabled = false;
uiMenuRender.IsEnabled = false;
private readonly Kernel.Context context;
uiMainTab.Visibility = Visibility.Collapsed;
/*
uiCourseBtnAdd.IsEnabled = false;
uiCourseBtnDelete.IsEnabled = false;
uiCourseBtnUpdate.IsEnabled = false;
/// <summary>
/// 返回当前是否有文件被加载。
/// </summary>
/// <returns></returns>
private bool IsFileLoaded() {
return !(context.currentSemester is null);
}
uiScheduleBtnAdd.IsEnabled = false;
uiScheduleBtnDelete.IsEnabled = false;
uiScheduleBtnUpdate.IsEnabled = false;
/// <summary>
/// 检查当前加载的文件是否是关联到某个本地文件上的。
/// </summary>
/// <returns></returns>
private bool HasAssocFile() {
return !(context.currentFilePath is null);
}
uiScheduleIndex.IsEnabled = false;
uiScheduleWeek.IsEnabled = false;
uiScheduleDay.IsEnabled = false;
/// <summary>
/// 更新整个界面的UI状态包括启用与否等
/// </summary>
/// <returns></returns>
private void UpdateUiLayout() {
bool isFileLoaded = this.IsFileLoaded();
bool hasAssocFile = this.HasAssocFile();
uiCourseName.IsEnabled = false;
uiCourseDescription.IsEnabled = false;
// Menus
uiMenuNew.IsEnabled = !isFileLoaded;
uiMenuOpen.IsEnabled = !isFileLoaded;
uiMenuOpenOld.IsEnabled = !isFileLoaded;
uiMenuOpenV1.IsEnabled = uiMenuOpenOld.IsEnabled;
uiStartDate.IsEnabled = false;
uiWeekCount.IsEnabled = false;
uiMenuSave.IsEnabled = isFileLoaded;
uiMenuSaveAs.IsEnabled = isFileLoaded;
uiCoursesList.IsEnabled = false;
uiScheduleList.IsEnabled = false;
*/
uiMenuClose.IsEnabled = isFileLoaded;
uiMenuQuit.IsEnabled = true;
uiMenuSimulator.IsEnabled = isFileLoaded;
uiMenuRender.IsEnabled = isFileLoaded;
// Main Area
uiMainTab.Visibility = isFileLoaded ? Visibility.Visible : Visibility.Collapsed;
// Window Caption
if (isFileLoaded) {
if (hasAssocFile) {
this.Title = $"HFUT课表模拟 - {context.currentFilePath}";
} else {
this.Title = "HFUT课表模拟 - [未命名]";
}
} else {
//no file
uiMenuNew.IsEnabled = false;
uiMenuOpen.IsEnabled = false;
uiMenuSave.IsEnabled = true;
uiMenuClose.IsEnabled = true;
uiMenuSimulator.IsEnabled = true;
uiMenuRender.IsEnabled = true;
uiMainTab.Visibility = Visibility.Visible;
/*
uiCourseBtnAdd.IsEnabled = true;
uiCourseBtnDelete.IsEnabled = true;
uiCourseBtnUpdate.IsEnabled = true;
uiScheduleBtnAdd.IsEnabled = true;
uiScheduleBtnDelete.IsEnabled = true;
uiScheduleBtnUpdate.IsEnabled = true;
uiScheduleIndex.IsEnabled = true;
uiScheduleWeek.IsEnabled = true;
uiScheduleDay.IsEnabled = true;
uiCourseName.IsEnabled = true;
uiCourseDescription.IsEnabled = true;
uiStartDate.IsEnabled = true;
uiWeekCount.IsEnabled = true;
uiCoursesList.IsEnabled = true;
uiScheduleList.IsEnabled = true;
*/
this.Title = "HFUT课表模拟";
}
}
/// <summary>
/// 更新界面的数据源
/// </summary>
private void UpdateDataSource() {
if (this.IsFileLoaded()) {
this.DataContext = context.currentSemester;
} else {
this.DataContext = null;
}
}
#endregion
#region Menu Handlers
#region menu
private void uiMenuNew_Click(object sender, RoutedEventArgs e) {
var sp = new Microsoft.Win32.SaveFileDialog();
sp.RestoreDirectory = true;
sp.Filter = "学期课程存档文件(*.json)|*.json|所有文件(*.*)|*.*";
var status = sp.ShowDialog();
if (!(status.HasValue && status.Value)) return;
var getFile = sp.FileName;
General.FilePath = getFile;
General.GeneralSheet = new Semester();
UpdateUI();
SyncMainData();
context.currentSemester = new Kernel.UserData.Semester();
context.currentFilePath = null;
UpdateUiLayout();
UpdateDataSource();
}
private void uiMenuOpen_Click(object sender, RoutedEventArgs e) {
Microsoft.Win32.OpenFileDialog op = new Microsoft.Win32.OpenFileDialog();
op.RestoreDirectory = true;
op.Multiselect = false;
op.Filter = "学期课程存档文件(*.json)|*.json|所有文件(*.*)|*.*";
var status = op.ShowDialog();
if (!(status.HasValue && status.Value)) return;
var getFile = op.FileName;
// Fetch file path.
var filepath = Util.Win32Dialog.OpenSemester();
if (filepath is null) return;
if (!File.Exists(getFile)) {
MessageBox.Show("文件不存在", "错误", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
// Try to read file.
try {
using (var fs = new StreamReader(filepath, Encoding.UTF8)) {
context.currentSemester = JsonConvert.DeserializeObject<Kernel.UserData.Semester>(fs.ReadToEnd());
context.currentFilePath = filepath;
}
} catch (Exception ex) {
Util.Win32Dialog.Error(ex.ToString(), "打开文件时出错");
return;
}
General.FilePath = getFile;
// Update UI and data source
UpdateUiLayout();
UpdateDataSource();
}
var fs = new StreamReader(getFile, Encoding.UTF8);
General.GeneralSheet = JsonConvert.DeserializeObject<Semester>(fs.ReadToEnd());
fs.Close();
fs.Dispose();
private void uiMenuOpenV1_Click(object sender, RoutedEventArgs e) {
UpdateUI();
SyncMainData();
// Update UI and data source
UpdateUiLayout();
UpdateDataSource();
}
private void uiMenuSave_Click(object sender, RoutedEventArgs e) {
var fs = new StreamWriter(General.FilePath, false, Encoding.UTF8);
fs.Write(JsonConvert.SerializeObject(General.GeneralSheet));
fs.Close();
fs.Dispose();
// Check whether there is associated file.
// If it not, order user select one.
if (context.currentFilePath is null) {
var filepath = Util.Win32Dialog.SaveSemester();
if (filepath is null) return;
context.currentFilePath = filepath;
}
// Try to save file
try {
using (var fs = new StreamWriter(context.currentFilePath, false, Encoding.UTF8)) {
fs.Write(JsonConvert.SerializeObject(context.currentSemester));
}
} catch (Exception ex) {
Util.Win32Dialog.Error(ex.ToString(), "保存文件时出错");
return;
}
// Update UI
UpdateUiLayout();
}
private void uiMenuSaveAs_Click(object sender, RoutedEventArgs e) {
// Always order user give a path
var filepath = Util.Win32Dialog.SaveSemester();
if (filepath is null) return;
// Update it to current path for following editing.
context.currentFilePath = filepath;
// Try to save file
try {
using (var fs = new StreamWriter(context.currentFilePath, false, Encoding.UTF8)) {
fs.Write(JsonConvert.SerializeObject(context.currentSemester));
}
} catch (Exception ex) {
Util.Win32Dialog.Error(ex.ToString(), "保存文件时出错");
return;
}
// Update UI
UpdateUiLayout();
}
private void uiMenuClose_Click(object sender, RoutedEventArgs e) {
//save
this.uiMenuSave_Click(sender, e);
// Confirm close
var rv = Util.Win32Dialog.Confirm("确认关闭吗?所有未保存的更改都将永久丢失!", "确认关闭");
if (!rv) return;
General.FilePath = "";
UpdateUI();
// Clear semester and assoc file
context.currentSemester = null;
context.currentFilePath = null;
// Update UI and data source
UpdateUiLayout();
UpdateDataSource();
}
private void uiMenuQuit_Click(object sender, RoutedEventArgs e) {
// If there is an opened document, let we confirm it first
if (IsFileLoaded()) {
var rv = Util.Win32Dialog.Confirm("确认退出吗?所有未保存的更改都将永久丢失!", "确认退出");
if (!rv) return;
}
// Close window
this.Close();
}
private void uiMenuSimulator_Click(object sender, RoutedEventArgs e) {
@ -176,7 +228,7 @@ namespace HFUTCourseSimulation {
var status = sp.ShowDialog();
if (!(status.HasValue && status.Value)) return;
var getFile = sp.FileName;
//export
var res = ImageExport.Export(originDate, weekCount, getFile);
if (!res) MessageBox.Show("当前课程安排存在冲突,请通过实时预览消除所有错误后才能使用此功能来导出为图片", "错误", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
@ -188,211 +240,32 @@ namespace HFUTCourseSimulation {
"本程序旨在为各位选课的同学提供一个用于查验课程冲突以及查看课表的功能。如果您想参与开发提交PullRequest即可欢迎您的加入。", "关于", MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK);
}
void SyncMainData() {
isUpdatingData = true;
this.uiStartDate.SelectedDate = DateTime.Parse(General.GeneralSheet.StartDate);
this.uiWeekCount.Text = General.GeneralSheet.WeekCount.ToString();
SyncCourseListData();
isUpdatingData = false;
}
#endregion
#region course
private void uiCourseBtnAdd_Click(object sender, RoutedEventArgs e) {
if (this.uiCourseName.Text == "") {
MessageBox.Show("课程名不得为空", "错误", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
return;
}
#region Main Area Handlers
General.GeneralSheet.Courses.Add(General.CourseCache.Clone());
SyncCourseListData();
}
private void uiCourseBtnUpdate_Click(object sender, RoutedEventArgs e) {
if (this.uiCourseName.Text == "") {
MessageBox.Show("课程名不得为空", "错误", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
return;
}
if (this.uiCoursesList.SelectedIndex < 0) {
MessageBox.Show("未选择任何项", "错误", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
return;
}
var index = this.uiCoursesList.SelectedIndex;
General.GeneralSheet.Courses[index] = General.CourseCache.Clone();
SyncCourseListData();
}
private void uiCourseBtnDelete_Click(object sender, RoutedEventArgs e) {
if (this.uiCoursesList.SelectedIndex < 0) {
MessageBox.Show("未选择任何项", "错误", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
return;
}
General.GeneralSheet.Courses.RemoveAt(uiCoursesList.SelectedIndex);
SyncCourseListData();
}
void SyncCourseListData() {
this.uiCoursesList.Items.Clear();
foreach (var item in General.GeneralSheet.Courses) {
this.uiCoursesList.Items.Add(item.Name);
}
}
//===========================================================get item
private void uiCoursesList_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
if (this.uiCoursesList.SelectedIndex < 0) {
MessageBox.Show("未选择任何项", "错误", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
return;
}
//if (this.uiCoursesList.SelectedIndex < 0) {
// MessageBox.Show("未选择任何项", "错误", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
// return;
//}
General.CourseCache = General.GeneralSheet.Courses[uiCoursesList.SelectedIndex].Clone();
SyncCourseData();
//General.CourseCache = General.GeneralSheet.Courses[uiCoursesList.SelectedIndex].Clone();
//SyncCourseData();
}
void SyncCourseData() {
isUpdatingData = true;
this.uiCourseName.Text = General.CourseCache.Name;
this.uiCourseDescription.Text = General.CourseCache.Description;
this.uiCourseColor.Background = new SolidColorBrush(General.CourseCache.BkColor.IntToColor());
SyncScheduleListData();
isUpdatingData = false;
private void uiCtxMenuNewCourse_Click(object sender, RoutedEventArgs e) {
}
#endregion
private void uiCtxMenuEditCourse_Click(object sender, RoutedEventArgs e) {
#region schedule
void SyncScheduleListData() {
this.uiScheduleList.Items.Clear();
foreach (var item in General.CourseCache.Schedule) {
uiScheduleList.Items.Add(item.ToString());
}
}
void SyncScheduleData() {
isUpdatingData = true;
this.uiScheduleIndex.Text = General.ScheduleCache.Index;
this.uiScheduleWeek.Text = General.ScheduleCache.Week;
this.uiScheduleDay.Text = General.ScheduleCache.Day;
isUpdatingData = false;
private void uiCtxMenuDeleteCourse_Click(object sender, RoutedEventArgs e) {
}
private void uiScheduleBtnAdd_Click(object sender, RoutedEventArgs e) {
if (this.uiScheduleIndex.Text == "" || this.uiScheduleWeek.Text == "") {
MessageBox.Show("数据不得为空", "错误", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
return;
}
General.CourseCache.Schedule.Add(General.ScheduleCache.Clone());
SyncScheduleListData();
}
private void uiScheduleBtnUpdate_Click(object sender, RoutedEventArgs e) {
if (this.uiScheduleIndex.Text == "" || this.uiScheduleWeek.Text == "") {
MessageBox.Show("数据不得为空", "错误", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
return;
}
if (this.uiScheduleList.SelectedIndex < 0) {
MessageBox.Show("未选择任何项", "错误", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
return;
}
var index = this.uiScheduleList.SelectedIndex;
General.CourseCache.Schedule[index] = General.ScheduleCache.Clone();
SyncScheduleListData();
}
private void uiScheduleBtnDelete_Click(object sender, RoutedEventArgs e) {
if (this.uiScheduleList.SelectedIndex < 0) {
MessageBox.Show("未选择任何项", "错误", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
return;
}
General.CourseCache.Schedule.RemoveAt(this.uiScheduleList.SelectedIndex);
SyncScheduleListData();
}
private void uiScheduleList_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
if (this.uiScheduleList.SelectedIndex < 0) {
MessageBox.Show("未选择任何项", "错误", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
return;
}
General.ScheduleCache = General.CourseCache.Schedule[uiScheduleList.SelectedIndex].Clone();
SyncScheduleData();
}
#endregion
#region control change
//schedule
private void uiScheduleWeek_TextChanged(object sender, TextChangedEventArgs e) {
if (isUpdatingData) return;
General.ScheduleCache.Week = this.uiScheduleWeek.Text;
}
private void uiScheduleIndex_TextChanged(object sender, TextChangedEventArgs e) {
if (isUpdatingData) return;
General.ScheduleCache.Index = this.uiScheduleIndex.Text;
}
private void uiScheduleDay_TextChanged(object sender, TextChangedEventArgs e) {
if (isUpdatingData) return;
General.ScheduleCache.Day = this.uiScheduleDay.Text;
}
//course
private void uiCourseName_TextChanged(object sender, TextChangedEventArgs e) {
if (isUpdatingData) return;
General.CourseCache.Name = this.uiCourseName.Text;
}
private void uiCourseDescription_TextChanged(object sender, TextChangedEventArgs e) {
if (isUpdatingData) return;
General.CourseCache.Description = this.uiCourseDescription.Text;
}
private void uiCourseBtnColor_Click(object sender, RoutedEventArgs e) {
var cor = new System.Windows.Forms.ColorDialog();
var status = cor.ShowDialog();
if (!(status == System.Windows.Forms.DialogResult.OK)) return;
var color = cor.Color.ConvertWinformColor();
//force a channel
color.A = 255;
General.CourseCache.BkColor = color.ColorToInt();
this.uiCourseColor.Background = new SolidColorBrush(color);
}
//main
private void uiStartDate_SelectedDateChanged(object sender, SelectionChangedEventArgs e) {
if (isUpdatingData) return;
General.GeneralSheet.StartDate = this.uiStartDate.SelectedDate.ToString();
}
private void uiWeekCount_TextChanged(object sender, TextChangedEventArgs e) {
if (isUpdatingData) return;
int res;
var tryres = int.TryParse(this.uiWeekCount.Text, out res);
if (tryres) General.GeneralSheet.WeekCount = res;
else General.GeneralSheet.WeekCount = 1;
}
#endregion
}

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using HFUTCourseSimulation.Kernel.UserData;
namespace HFUTCourseSimulation.Util.ColorPreset {
/// <summary>
/// Material Design颜色合集
/// </summary>
public static class MdColors {
private static Color LightText => Color.White;
private static Color DarkText => Color.FromArgb(33, 33, 33);
public static ColorPair Red => new ColorPair(LightText, Color.FromArgb(244, 67, 54));
public static ColorPair Pink => new ColorPair(LightText, Color.FromArgb(233, 30, 99));
public static ColorPair Purple => new ColorPair(LightText, Color.FromArgb(156, 39, 176));
public static ColorPair DeepPurple => new ColorPair(LightText, Color.FromArgb(103, 58, 183));
public static ColorPair Indigo => new ColorPair(LightText, Color.FromArgb(63, 81, 181));
public static ColorPair Blue => new ColorPair(LightText, Color.FromArgb(33, 150, 243));
public static ColorPair LightBlue => new ColorPair(LightText, Color.FromArgb(3, 169, 244));
public static ColorPair Cyan => new ColorPair(LightText, Color.FromArgb(0, 188, 212));
public static ColorPair Teal => new ColorPair(LightText, Color.FromArgb(0, 150, 136));
public static ColorPair Green => new ColorPair(LightText, Color.FromArgb(76, 175, 80));
public static ColorPair LightGreen => new ColorPair(DarkText, Color.FromArgb(139, 195, 74));
public static ColorPair Lime => new ColorPair(DarkText, Color.FromArgb(205, 220, 57));
public static ColorPair Yellow => new ColorPair(DarkText, Color.FromArgb(255, 235, 59));
public static ColorPair Amber => new ColorPair(DarkText, Color.FromArgb(255, 193, 7));
public static ColorPair Orange => new ColorPair(DarkText, Color.FromArgb(255, 152, 0));
public static ColorPair DeepOrange => new ColorPair(LightText, Color.FromArgb(255, 87, 34));
public static ColorPair Brown => new ColorPair(LightText, Color.FromArgb(121, 85, 72));
public static ColorPair Grey => new ColorPair(DarkText, Color.FromArgb(158, 158, 158));
public static ColorPair BlueGrey => new ColorPair(LightText, Color.FromArgb(96, 125, 139));
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace HFUTCourseSimulation.Util {
public static class Win32Dialog {
/// <summary>
/// 打开学期存档文件
/// </summary>
/// <returns>如果用户选定了文件返回文件路径否则返回null</returns>
public static string OpenSemester() {
Microsoft.Win32.OpenFileDialog op = new Microsoft.Win32.OpenFileDialog();
op.RestoreDirectory = true;
op.Multiselect = false;
op.Filter = "学期课程存档文件(*.json)|*.json|所有文件(*.*)|*.*";
var status = op.ShowDialog();
if (!(status.HasValue && status.Value)) return null;
else return op.FileName;
}
/// <summary>
/// 保存学期存档文件
/// </summary>
/// <returns>如果用户选定了文件返回文件路径否则返回null</returns>
public static string SaveSemester() {
var sp = new Microsoft.Win32.SaveFileDialog();
sp.RestoreDirectory = true;
sp.Filter = "学期课程存档文件(*.json)|*.json|所有文件(*.*)|*.*";
var status = sp.ShowDialog();
if (!(status.HasValue && status.Value)) return null;
else return sp.FileName;
}
/// <summary>
/// 保存渲染的学期图片文件
/// </summary>
/// <returns>如果用户选定了文件返回文件路径否则返回null</returns>
public static string SaveRender() {
var sp = new Microsoft.Win32.SaveFileDialog();
sp.RestoreDirectory = true;
sp.Filter = "图片文件(*.png)|*.png|所有文件(*.*)|*.*";
var status = sp.ShowDialog();
if (!(status.HasValue && status.Value)) return null;
else return sp.FileName;
}
/// <summary>
/// 显示一个经典的Windows错误对话框
/// </summary>
public static void Error(string message, string caption) {
MessageBox.Show(message, caption, MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
}
/// <summary>
/// 显示一个经典的Windows信息对话框
/// </summary>
public static void Info(string message, string caption) {
MessageBox.Show(message, caption, MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK);
}
/// <summary>
/// 显示一个经典的Windows确认操作对话框
/// </summary>
/// <returns>如果用户点击了确认则返回true否则返回false</returns>
public static bool Confirm(string message, string caption) {
var rv = MessageBox.Show(message, caption, MessageBoxButton.OKCancel, MessageBoxImage.Question, MessageBoxResult.Cancel);
return rv == MessageBoxResult.OK;
}
}
}