fix: improve use experience

This commit is contained in:
2025-09-08 13:52:50 +08:00
parent 5019bc31dc
commit 9e64813681
8 changed files with 216 additions and 294 deletions

View File

@ -83,7 +83,6 @@
<Compile Include="Dialog\Simulator.xaml.cs"> <Compile Include="Dialog\Simulator.xaml.cs">
<DependentUpon>Simulator.xaml</DependentUpon> <DependentUpon>Simulator.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="ImageExport.cs" />
<Compile Include="Kernel\Arranger.cs" /> <Compile Include="Kernel\Arranger.cs" />
<Compile Include="Kernel\Data\Built.cs" /> <Compile Include="Kernel\Data\Built.cs" />
<Compile Include="Kernel\Data\Presentation.cs" /> <Compile Include="Kernel\Data\Presentation.cs" />

View File

@ -1,116 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace HFUTCourseSimulation {
//public static class ImageExport {
// public static readonly int WeekHeaderHeight = 50;
// public static readonly int BodyCellWidth = 150;
// public static readonly int BodyCellHeight = 80;
// public static readonly int HeaderCellHeight = 40;
// public static readonly int LeftCellWidth = 25;
// public static readonly int Blank = 20;
// static Dictionary<int, string> weekDict = new Dictionary<int, string>() {
// {0, "星期一"},
// {1, "星期二"},
// {2, "星期三"},
// {3, "星期四"},
// {4, "星期五"},
// {5, "星期六"},
// {6, "星期日"}
// };
// public static bool Export(DateTime startDate, int weekCount, string file) {
// var simuCore = new SimulationCore(startDate, weekCount);
// simuCore.Generate();
// if (simuCore.ErrorList.Count != 0) return false;
// //define size
// int weekPerLine = (int)Math.Sqrt(weekCount) + 1;
// var weekWidth = (Blank + LeftCellWidth + 7 * BodyCellWidth);
// var weekHeight = (Blank + WeekHeaderHeight + HeaderCellHeight + 11 * BodyCellHeight);
// var img = new Bitmap(Blank + weekPerLine * weekWidth, Blank + (weekCount / weekPerLine + (weekCount % weekPerLine == 0 ? 0 : 1)) * weekHeight);
// var graphics = Graphics.FromImage(img);
// //declare graphics value
// var background = new SolidBrush(Color.White);
// var headerBrush = new SolidBrush(Color.Gray);
// var textBrush = new SolidBrush(Color.Black);
// Brush cellBrush;
// var textFont = new Font("Microsoft YaHei UI", 12);
// var textMargin = graphics.MeasureString("测试字符", textFont).Height;
// //overlay all background
// graphics.FillRectangle(background, 0, 0, Blank + weekPerLine * weekWidth, Blank + (weekCount / weekPerLine + (weekCount % weekPerLine == 0 ? 0 : 1)) * weekHeight);
// //output each week
// for (int i = 0; i < weekCount; i++) {
// //generate week data
// var weekData = simuCore.Render(i + 1);
// //get start position
// var top = Blank + (i / weekPerLine) * weekHeight;
// var left = Blank + (i % weekPerLine) * weekWidth;
// graphics.TranslateTransform(left, top);
// //draw week head
// graphics.DrawString($"教学周:{weekData.WeekIndex}", textFont, textBrush, 0, 0);
// //move transform
// graphics.TranslateTransform(0, WeekHeaderHeight);
// //draw header
// graphics.FillRectangle(headerBrush, 0, 0, weekWidth - Blank, HeaderCellHeight);
// graphics.FillRectangle(headerBrush, 0, 0, LeftCellWidth, weekHeight - WeekHeaderHeight - Blank);
// //draw header text
// for (int j = 0; j < 7; j++) {
// graphics.DrawString(weekDict[j], textFont, textBrush, LeftCellWidth + j * BodyCellWidth, 0);
// graphics.DrawString(weekData.WeekDate[j], textFont, textBrush, LeftCellWidth + j * BodyCellWidth, textMargin);
// }
// //draw left text
// for (int j = 0; j < 11; j++) {
// graphics.DrawString((j + 1).ToString(), textFont, textBrush, 0, HeaderCellHeight + j * BodyCellHeight);
// }
// //move transform
// graphics.TranslateTransform(LeftCellWidth, HeaderCellHeight);
// foreach(var courses in weekData.CourseList) {
// var cellx = courses.Start.week - 1;
// var celly = courses.Start.index - 1;
// //background
// cellBrush = new SolidBrush(Util.ColorTrans.ToWinformColor(courses.BkColor));
// graphics.FillRectangle(cellBrush, cellx * BodyCellWidth, celly * BodyCellHeight, BodyCellWidth, BodyCellHeight * courses.Span);
// //draw text
// var rect = new RectangleF(cellx * BodyCellWidth, celly * BodyCellHeight, BodyCellWidth, celly * BodyCellHeight);
// graphics.DrawString(courses.Name, textFont, textBrush, rect);
// var thisTextMargin = graphics.MeasureString(courses.Name, textFont, BodyCellWidth).Height;
// graphics.DrawString(courses.Desc, textFont, textBrush, cellx * BodyCellWidth, celly * BodyCellHeight + thisTextMargin);
// }
// //reset
// graphics.TranslateTransform(-LeftCellWidth, -HeaderCellHeight);
// graphics.TranslateTransform(0, -WeekHeaderHeight);
// graphics.TranslateTransform(-left, -top);
// }
// System.IO.File.Delete(file);
// img.Save(file, ImageFormat.Png);
// img.Dispose();
// return true;
// }
//}
}

View File

@ -16,12 +16,12 @@ namespace HFUTCourseSimulation.Kernel.Data.LegacyStorage.V1 {
WeekCount = 1; WeekCount = 1;
} }
[JsonProperty("StartDate")] [JsonProperty("StartDate", Required = Required.Always)]
[JsonConverter(typeof(CustomDateTimeConverter))] [JsonConverter(typeof(CustomDateTimeConverter))]
public DateTime StartDate { get; set; } public DateTime StartDate { get; set; }
[JsonProperty("WeekCount")] [JsonProperty("WeekCount", Required = Required.Always)]
public int WeekCount { get; set; } public int WeekCount { get; set; }
[JsonProperty("Courses")] [JsonProperty("Courses", Required = Required.Always)]
public List<CourseItem> Courses { get; set; } public List<CourseItem> Courses { get; set; }
public Storage.Semester ToLatest() { public Storage.Semester ToLatest() {
@ -40,14 +40,14 @@ namespace HFUTCourseSimulation.Kernel.Data.LegacyStorage.V1 {
this.Schedule = new List<ScheduleItem>(); this.Schedule = new List<ScheduleItem>();
} }
[JsonProperty("Name")] [JsonProperty("Name", Required = Required.Always)]
public string Name { get; set; } public string Name { get; set; }
[JsonProperty("Description")] [JsonProperty("Description", Required = Required.Always)]
public string Description { get; set; } public string Description { get; set; }
[JsonProperty("BkColor")] [JsonProperty("BkColor", Required = Required.Always)]
[JsonConverter(typeof(CustomColorConverter))] [JsonConverter(typeof(CustomColorConverter))]
public Color BkColor { get; set; } public Color BkColor { get; set; }
[JsonProperty("Schedule")] [JsonProperty("Schedule", Required = Required.Always)]
public List<ScheduleItem> Schedule { get; set; } public List<ScheduleItem> Schedule { get; set; }
public Storage.Course ToLatest() { public Storage.Course ToLatest() {
@ -64,11 +64,11 @@ namespace HFUTCourseSimulation.Kernel.Data.LegacyStorage.V1 {
} }
public class ScheduleItem { public class ScheduleItem {
[JsonProperty("Week")] [JsonProperty("Week", Required = Required.Always)]
public string Week { get; set; } public string Week { get; set; }
[JsonProperty("Day")] [JsonProperty("Day", Required = Required.Always)]
public string Day { get; set; } public string Day { get; set; }
[JsonProperty("Index")] [JsonProperty("Index", Required = Required.Always)]
public string Index { get; set; } public string Index { get; set; }
public Storage.Schedule ToLatest() { public Storage.Schedule ToLatest() {

View File

@ -38,38 +38,38 @@ namespace HFUTCourseSimulation.Kernel.Data.Storage {
/// <summary> /// <summary>
/// 学期开始日期(星期一) /// 学期开始日期(星期一)
/// </summary> /// </summary>
[JsonProperty("start_date")] [JsonProperty("start_date", Required = Required.Always)]
[JsonConverter(typeof(CustomDateTimeConverter))] [JsonConverter(typeof(CustomDateTimeConverter))]
public DateTime StartDate { get; set; } public DateTime StartDate { get; set; }
/// <summary> /// <summary>
/// 教学周个数 /// 教学周个数
/// </summary> /// </summary>
[JsonProperty("week_count")] [JsonProperty("week_count", Required = Required.Always)]
public string WeekCount { get; set; } public string WeekCount { get; set; }
/// <summary> /// <summary>
/// 每天课程的节次数 /// 每天课程的节次数
/// </summary> /// </summary>
[JsonProperty("index_count")] [JsonProperty("index_count", Required = Required.Always)]
public string IndexCount { get; set; } public string IndexCount { get; set; }
/// <summary> /// <summary>
/// 早餐插入在第几节次后 /// 早餐插入在第几节次后
/// </summary> /// </summary>
[JsonProperty("breakfast_at")] [JsonProperty("breakfast_at", Required = Required.Always)]
public string BreakfastAt { get; set; } public string BreakfastAt { get; set; }
/// <summary> /// <summary>
/// 午餐插入在第几节次后 /// 午餐插入在第几节次后
/// </summary> /// </summary>
[JsonProperty("lunch_at")] [JsonProperty("lunch_at", Required = Required.Always)]
public string LunchAt { get; set; } public string LunchAt { get; set; }
/// <summary> /// <summary>
/// 晚餐插入在第几节次后 /// 晚餐插入在第几节次后
/// </summary> /// </summary>
[JsonProperty("dinner_at")] [JsonProperty("dinner_at", Required = Required.Always)]
public string DinnerAt { get; set; } public string DinnerAt { get; set; }
/// <summary> /// <summary>
/// 课程列表 /// 课程列表
/// </summary> /// </summary>
[JsonProperty("courses")] [JsonProperty("courses", Required = Required.Always)]
public List<Course> Courses { get; set; } public List<Course> Courses { get; set; }
} }
@ -84,22 +84,22 @@ namespace HFUTCourseSimulation.Kernel.Data.Storage {
/// <summary> /// <summary>
/// 课程的名称 /// 课程的名称
/// </summary> /// </summary>
[JsonProperty("name")] [JsonProperty("name", Required = Required.Always)]
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// 课程的说明,例如教室位置,教师姓名等。 /// 课程的说明,例如教室位置,教师姓名等。
/// </summary> /// </summary>
[JsonProperty("description")] [JsonProperty("description", Required = Required.Always)]
public string Description { get; set; } public string Description { get; set; }
/// <summary> /// <summary>
/// 课程的颜色。 /// 课程的颜色。
/// </summary> /// </summary>
[JsonProperty("color")] [JsonProperty("color", Required = Required.Always)]
public ColorPair Color { get; set; } public ColorPair Color { get; set; }
/// <summary> /// <summary>
/// 课程的所有安排 /// 课程的所有安排
/// </summary> /// </summary>
[JsonProperty("schedules")] [JsonProperty("schedules", Required = Required.Always)]
public List<Schedule> Schedules { get; set; } public List<Schedule> Schedules { get; set; }
} }
@ -113,17 +113,17 @@ namespace HFUTCourseSimulation.Kernel.Data.Storage {
/// <summary> /// <summary>
/// 安排在哪些周 /// 安排在哪些周
/// </summary> /// </summary>
[JsonProperty("week")] [JsonProperty("week", Required = Required.Always)]
public string Week { get; set; } public string Week { get; set; }
/// <summary> /// <summary>
/// 安排在周的星期几 /// 安排在周的星期几
/// </summary> /// </summary>
[JsonProperty("day")] [JsonProperty("day", Required = Required.Always)]
public string Day { get; set; } public string Day { get; set; }
/// <summary> /// <summary>
/// 安排在这些日子的哪些节次 /// 安排在这些日子的哪些节次
/// </summary> /// </summary>
[JsonProperty("index")] [JsonProperty("index", Required = Required.Always)]
public string Index { get; set; } public string Index { get; set; }
} }
@ -139,13 +139,13 @@ namespace HFUTCourseSimulation.Kernel.Data.Storage {
/// <summary> /// <summary>
/// 前景色(文本) /// 前景色(文本)
/// </summary> /// </summary>
[JsonProperty("foreground")] [JsonProperty("foreground", Required = Required.Always)]
[JsonConverter(typeof(CustomColorConverter))] [JsonConverter(typeof(CustomColorConverter))]
public Color Foreground { get; set; } public Color Foreground { get; set; }
/// <summary> /// <summary>
/// 背景色(背景) /// 背景色(背景)
/// </summary> /// </summary>
[JsonProperty("background")] [JsonProperty("background", Required = Required.Always)]
[JsonConverter(typeof(CustomColorConverter))] [JsonConverter(typeof(CustomColorConverter))]
public Color Background { get; set; } public Color Background { get; set; }
} }

View File

@ -218,14 +218,14 @@ namespace HFUTCourseSimulation.Kernel {
var font = new Font("Sarasa Mono SC", 12); var font = new Font("Sarasa Mono SC", 12);
// 填充背景为白色 // 填充背景为白色
g.Clear(Color.White); g.Clear(ColorTrans.ToGdiColor(ColorConsistency.Backboard.Background));
// 按教学周周分别输出 // 按教学周周分别输出
for (int week = 1; week <= semester.weekCount; ++week) { for (int week = 1; week <= semester.weekCount; ++week) {
var week_instance = semester.weeks[week - 1]; var week_instance = semester.weeks[week - 1];
// 教学周文本 // 教学周文本
g.DrawString($"教学周:{week}", font, brushes.GetBrush(Color.Black), geometry.GetTitleTextRect(week)); g.DrawString($"教学周:{week}", font, brushes.GetBrush(ColorConsistency.Backboard.Foreground), geometry.GetTitleTextRect(week));
// 绘制Headbar底层 // 绘制Headbar底层
g.FillRectangle(brushes.GetBrush(ColorConsistency.HeadbarColor.Background), geometry.GetHeadbarBoxRect(week)); g.FillRectangle(brushes.GetBrush(ColorConsistency.HeadbarColor.Background), geometry.GetHeadbarBoxRect(week));

View File

@ -6,34 +6,51 @@
xmlns:local="clr-namespace:HFUTCourseSimulation" xmlns:local="clr-namespace:HFUTCourseSimulation"
xmlns:uidata="clr-namespace:HFUTCourseSimulation.Kernel.Data.Ui" xmlns:uidata="clr-namespace:HFUTCourseSimulation.Kernel.Data.Ui"
mc:Ignorable="d" mc:Ignorable="d"
d:DataContext="{d:DesignInstance uidata:Semester}" d:DataContext="{d:DesignInstance local:MainWindowContext}"
Title="HFUT课表模拟" Height="600" Width="800"> Title="{Binding AppTitle, Mode=OneWay}" Height="600" Width="800">
<Window.Resources>
<RoutedUICommand x:Key="commandMenuNew"/>
<RoutedUICommand x:Key="commandMenuOpen"/>
<RoutedUICommand x:Key="commandMenuSave"/>
</Window.Resources>
<Window.InputBindings>
<KeyBinding Gesture="Ctrl+N" Command="{StaticResource commandMenuNew}"/>
<KeyBinding Gesture="Ctrl+O" Command="{StaticResource commandMenuOpen}"/>
<KeyBinding Gesture="Ctrl+S" Command="{StaticResource commandMenuSave}"/>
</Window.InputBindings>
<Window.CommandBindings>
<CommandBinding Command="{StaticResource commandMenuNew}" Executed="commandMenuNew_Click" CanExecute="commandCanExec_NotLoaded"/>
<CommandBinding Command="{StaticResource commandMenuOpen}" Executed="commandMenuOpen_Click" CanExecute="commandCanExec_NotLoaded"/>
<CommandBinding Command="{StaticResource commandMenuSave}" Executed="commandMenuSave_Click" CanExecute="commandCanExec_Loaded"/>
</Window.CommandBindings>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="auto"/> <RowDefinition Height="auto"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Menu IsMainMenu="True" Grid.Row="0"> <Menu IsMainMenu="True" Grid.Row="0">
<MenuItem Header="文件" Padding="5"> <MenuItem Header="文件(_F)" Padding="5">
<MenuItem x:Name="uiMenuNew" Header="新建" Click="uiMenuNew_Click" /> <MenuItem x:Name="uiMenuNew" Header="新建(_N)" IsEnabled="{Binding MenuOnNotLoaded, Mode=OneWay}" InputGestureText="Ctrl+N" Click="uiMenuNew_Click"/>
<MenuItem x:Name="uiMenuOpen" Header="打开" Click="uiMenuOpen_Click"/> <MenuItem x:Name="uiMenuOpen" Header="打开(_O)" IsEnabled="{Binding MenuOnNotLoaded, Mode=OneWay}" InputGestureText="Ctrl+O" Click="uiMenuOpen_Click"/>
<MenuItem x:Name="uiMenuOpenOld" Header="打开旧版"> <MenuItem x:Name="uiMenuOpenOld" Header="打开旧版" IsEnabled="{Binding MenuOnNotLoaded, Mode=OneWay}">
<MenuItem x:Name="uiMenuOpenV1" Header="打开V1版本" Click="uiMenuOpenV1_Click"/> <MenuItem x:Name="uiMenuOpenV1" Header="打开V1版本" IsEnabled="{Binding MenuOnNotLoaded, Mode=OneWay}" Click="uiMenuOpenV1_Click"/>
</MenuItem> </MenuItem>
<Separator/> <Separator/>
<MenuItem x:Name="uiMenuSave" Header="保存" Click="uiMenuSave_Click"/> <MenuItem x:Name="uiMenuSave" Header="保存(_S)" IsEnabled="{Binding MenuOnLoaded, Mode=OneWay}" InputGestureText="Ctrl+S" Click="uiMenuSave_Click"/>
<MenuItem x:Name="uiMenuSaveAs" Header="另存为" Click="uiMenuSaveAs_Click"/> <MenuItem x:Name="uiMenuSaveAs" Header="另存为" IsEnabled="{Binding MenuOnLoaded, Mode=OneWay}" Click="uiMenuSaveAs_Click"/>
<Separator/> <Separator/>
<MenuItem x:Name="uiMenuClose" Header="关闭" Click="uiMenuClose_Click"/> <MenuItem x:Name="uiMenuClose" Header="关闭" IsEnabled="{Binding MenuOnLoaded, Mode=OneWay}" Click="uiMenuClose_Click"/>
<MenuItem x:Name="uiMenuQuit" Header="退出" Click="uiMenuQuit_Click"/> <MenuItem x:Name="uiMenuQuit" Header="退出" Click="uiMenuQuit_Click"/>
</MenuItem> </MenuItem>
<MenuItem Header="输出" Padding="5"> <MenuItem Header="输出(_O)" IsEnabled="{Binding MenuOnLoaded, Mode=OneWay}" Padding="5">
<MenuItem x:Name="uiMenuCheck" Header="检查" Click="uiMenuCheck_Click"/> <MenuItem x:Name="uiMenuCheck" Header="检查" IsEnabled="{Binding MenuOnLoaded, Mode=OneWay}" Click="uiMenuCheck_Click"/>
<MenuItem x:Name="uiMenuSimulator" Header="实时模拟" Click="uiMenuSimulator_Click"/> <MenuItem x:Name="uiMenuSimulator" Header="实时模拟" IsEnabled="{Binding MenuOnLoaded, Mode=OneWay}" Click="uiMenuSimulator_Click"/>
<MenuItem x:Name="uiMenuRender" Header="导出为图片" Click="uiMenuRender_Click"/> <MenuItem x:Name="uiMenuRender" Header="导出为图片" IsEnabled="{Binding MenuOnLoaded, Mode=OneWay}" Click="uiMenuRender_Click"/>
</MenuItem> </MenuItem>
<MenuItem Header="关于" Padding="5"> <MenuItem Header="关于(_A)" Padding="5">
<MenuItem x:Name="uiMenuAbout" Header="关于本程序" Click="uiMenuAbout_Click"/> <MenuItem x:Name="uiMenuAbout" Header="关于本程序" Click="uiMenuAbout_Click"/>
</MenuItem> </MenuItem>
@ -41,8 +58,9 @@
<TextBlock Text="打开或新建一个课表文件" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="20" Opacity="0.5"/> <TextBlock Text="打开或新建一个课表文件" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="20" Opacity="0.5"/>
<TabControl x:Name="uiMainTab" Grid.Row="1"> <!-- 我不能在TabControl上设置DataContext不然它的Visibility就没法绑定了。-->
<TabItem Header="学期设置" Padding="5"> <TabControl Visibility="{Binding MainAreaVisibility, Mode=OneWay}" Grid.Row="1">
<TabItem DataContext="{Binding Semester, Mode=OneWay}" Header="学期设置" Padding="5">
<ScrollViewer> <ScrollViewer>
<StackPanel Orientation="Vertical" Margin="10,0,10,0"> <StackPanel Orientation="Vertical" Margin="10,0,10,0">
<TextBlock Text="起始日期(教学第一周星期一的日期)" Margin="0,20,0,0"/> <TextBlock Text="起始日期(教学第一周星期一的日期)" Margin="0,20,0,0"/>
@ -62,7 +80,7 @@
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
</TabItem> </TabItem>
<TabItem Header="课程设置" Padding="5"> <TabItem DataContext="{Binding Semester, Mode=OneWay}" Header="课程设置" Padding="5">
<ListBox x:Name="uiCoursesList" ItemsSource="{Binding Courses, Mode=OneWay}" Margin="10" MouseDoubleClick="uiCoursesList_MouseDoubleClick"> <ListBox x:Name="uiCoursesList" ItemsSource="{Binding Courses, Mode=OneWay}" Margin="10" MouseDoubleClick="uiCoursesList_MouseDoubleClick">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate> <DataTemplate>
@ -101,5 +119,9 @@
</TabItem> </TabItem>
</TabControl> </TabControl>
<StatusBar Grid.Row="2">
<TextBlock x:Name="uiReportText" Margin="5"/>
</StatusBar>
</Grid> </Grid>
</Window> </Window>

View File

@ -1,8 +1,8 @@
using HFUTCourseSimulation.Dialog; using HFUTCourseSimulation.Util;
using HFUTCourseSimulation.Util;
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -16,86 +16,101 @@ using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Navigation; using System.Windows.Navigation;
using System.Windows.Shapes; using System.Windows.Shapes;
using System.Windows.Threading;
namespace HFUTCourseSimulation { namespace HFUTCourseSimulation {
/// <summary>
/// MainWindow.xaml 的交互逻辑 internal class MainWindowContext : INotifyPropertyChanged {
/// </summary> public MainWindowContext() {
public partial class MainWindow : Window { _semester = null;
public MainWindow() { _filePath = null;
InitializeComponent();
UpdateUiLayout();
} }
#region Context and Assistant Functions private Kernel.Data.Ui.Semester _semester;
private string _filePath;
private Kernel.Data.Ui.Semester CurrentSemester { get; set; } public Kernel.Data.Ui.Semester Semester {
private string CurrentFilePath { get; set; } get { return _semester; }
set {
_semester = value;
OnPropertyChanged(nameof(Semester));
OnPropertyChanged(nameof(AppTitle));
OnPropertyChanged(nameof(MenuOnLoaded));
OnPropertyChanged(nameof(MenuOnNotLoaded));
OnPropertyChanged(nameof(MainAreaVisibility));
}
}
public string FilePath {
get { return _filePath; }
set {
_filePath = value;
OnPropertyChanged(nameof(FilePath));
OnPropertyChanged(nameof(AppTitle));
}
}
public string AppTitle {
get {
if (IsFileLoaded()) {
if (HasAssocFile()) {
return $"HFUT课表模拟 - {_filePath}";
} else {
return "HFUT课表模拟 - [未命名]";
}
} else {
return "HFUT课表模拟";
}
}
}
public bool MenuOnLoaded => IsFileLoaded();
public bool MenuOnNotLoaded => !MenuOnLoaded;
public Visibility MainAreaVisibility => IsFileLoaded() ? Visibility.Visible : Visibility.Collapsed;
/// <summary> /// <summary>
/// 返回当前是否有文件被加载。 /// 返回当前是否有文件被加载。
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private bool IsFileLoaded() { public bool IsFileLoaded() {
return !(CurrentSemester is null); return !(_semester is null);
} }
/// <summary> /// <summary>
/// 检查当前加载的文件是否是关联到某个本地文件上的。 /// 检查当前加载的文件是否是关联到某个本地文件上的。
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private bool HasAssocFile() { public bool HasAssocFile() {
return !(CurrentFilePath is null); return !(_filePath is null);
} }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name = null) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
// Setup context and self data context
_context = new MainWindowContext();
this.DataContext = _context;
// Setup report timeout timer
reportTimeout = new DispatcherTimer();
reportTimeout.Interval = TimeSpan.FromSeconds(5);
reportTimeout.Tick += reportTimeout_Tick;
}
#region Context and Assistant Functions
/// <summary> /// <summary>
/// 更新整个界面的UI状态包括启用与否等 /// 当前主窗口的UI上下文
/// </summary> /// </summary>
/// <returns></returns> private MainWindowContext _context;
private void UpdateUiLayout() {
bool isFileLoaded = this.IsFileLoaded();
bool hasAssocFile = this.HasAssocFile();
// Menus
uiMenuNew.IsEnabled = !isFileLoaded;
uiMenuOpen.IsEnabled = !isFileLoaded;
uiMenuOpenOld.IsEnabled = !isFileLoaded;
uiMenuOpenV1.IsEnabled = uiMenuOpenOld.IsEnabled;
uiMenuSave.IsEnabled = isFileLoaded;
uiMenuSaveAs.IsEnabled = isFileLoaded;
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课表模拟 - {CurrentFilePath}";
} else {
this.Title = "HFUT课表模拟 - [未命名]";
}
} else {
this.Title = "HFUT课表模拟";
}
}
/// <summary>
/// 更新界面的数据源
/// </summary>
private void UpdateDataSource() {
if (this.IsFileLoaded()) {
this.DataContext = CurrentSemester;
} else {
this.DataContext = null;
}
}
#endregion #endregion
@ -106,12 +121,8 @@ namespace HFUTCourseSimulation {
var new_obj = new Kernel.Data.Storage.Semester(); var new_obj = new Kernel.Data.Storage.Semester();
// Convert into struct for Ui and clean assoc file path. // Convert into struct for Ui and clean assoc file path.
CurrentSemester = new Kernel.Data.Ui.Semester(new_obj); _context.Semester = new Kernel.Data.Ui.Semester(new_obj);
CurrentFilePath = null; _context.FilePath = null;
// Update UI and data source
UpdateUiLayout();
UpdateDataSource();
} }
private void uiMenuOpen_Click(object sender, RoutedEventArgs e) { private void uiMenuOpen_Click(object sender, RoutedEventArgs e) {
@ -125,17 +136,16 @@ namespace HFUTCourseSimulation {
// Read semester object. // Read semester object.
var obj = JsonConvert.DeserializeObject<Kernel.Data.Storage.Semester>(fs.ReadToEnd()); var obj = JsonConvert.DeserializeObject<Kernel.Data.Storage.Semester>(fs.ReadToEnd());
// Convert into struct for Ui and add assoc file path. // Convert into struct for Ui and add assoc file path.
CurrentSemester = new Kernel.Data.Ui.Semester(obj); _context.Semester = new Kernel.Data.Ui.Semester(obj);
CurrentFilePath = filepath; _context.FilePath = filepath;
} }
} catch (Exception ex) { } catch (Exception ex) {
Util.Win32Dialog.Error(ex.ToString(), "打开文件时出错"); Util.Win32Dialog.Error(ex.ToString(), "打开文件时出错");
return; return;
} }
// Update UI and data source // Show report
UpdateUiLayout(); ReportStatus($"成功打开文件:{filepath}");
UpdateDataSource();
} }
private void OpenLegacy<T>() where T : Kernel.Data.Storage.IFromLegacy { private void OpenLegacy<T>() where T : Kernel.Data.Storage.IFromLegacy {
@ -150,17 +160,16 @@ namespace HFUTCourseSimulation {
var obj = JsonConvert.DeserializeObject<T>(fs.ReadToEnd()); var obj = JsonConvert.DeserializeObject<T>(fs.ReadToEnd());
var latest_obj = obj.ToLatest(); var latest_obj = obj.ToLatest();
// Convert into struct for Ui and add assoc file path. // Convert into struct for Ui and add assoc file path.
CurrentSemester = new Kernel.Data.Ui.Semester(latest_obj); _context.Semester = new Kernel.Data.Ui.Semester(latest_obj);
CurrentFilePath = filepath; _context.FilePath = filepath;
} }
} catch (Exception ex) { } catch (Exception ex) {
Util.Win32Dialog.Error(ex.ToString(), "打开文件时出错"); Util.Win32Dialog.Error(ex.ToString(), "打开文件时出错");
return; return;
} }
// Update UI and data source // Show report
UpdateUiLayout(); ReportStatus($"成功迁移文件:{filepath}");
UpdateDataSource();
} }
private void uiMenuOpenV1_Click(object sender, RoutedEventArgs e) { private void uiMenuOpenV1_Click(object sender, RoutedEventArgs e) {
@ -170,24 +179,24 @@ namespace HFUTCourseSimulation {
private void uiMenuSave_Click(object sender, RoutedEventArgs e) { private void uiMenuSave_Click(object sender, RoutedEventArgs e) {
// Check whether there is associated file. // Check whether there is associated file.
// If it not, order user select one. // If it not, order user select one.
if (CurrentFilePath is null) { if (!_context.HasAssocFile()) {
var filepath = Util.Win32Dialog.SaveSemester(); var filepath = Util.Win32Dialog.SaveSemester();
if (filepath is null) return; if (filepath is null) return;
CurrentFilePath = filepath; _context.FilePath = filepath;
} }
// Try to save file // Try to save file
try { try {
using (var fs = new StreamWriter(CurrentFilePath, false, Encoding.UTF8)) { using (var fs = new StreamWriter(_context.FilePath, false, Encoding.UTF8)) {
fs.Write(JsonConvert.SerializeObject(CurrentSemester.ToStorage())); fs.Write(JsonConvert.SerializeObject(_context.Semester.ToStorage()));
} }
} catch (Exception ex) { } catch (Exception ex) {
Util.Win32Dialog.Error(ex.ToString(), "保存文件时出错"); Util.Win32Dialog.Error(ex.ToString(), "保存文件时出错");
return; return;
} }
// Update UI // Show report
UpdateUiLayout(); ReportStatus($"已保存文件:{_context.FilePath}");
} }
private void uiMenuSaveAs_Click(object sender, RoutedEventArgs e) { private void uiMenuSaveAs_Click(object sender, RoutedEventArgs e) {
@ -196,20 +205,20 @@ namespace HFUTCourseSimulation {
if (filepath is null) return; if (filepath is null) return;
// Update it to current path for following editing. // Update it to current path for following editing.
CurrentFilePath = filepath; _context.FilePath = filepath;
// Try to save file // Try to save file
try { try {
using (var fs = new StreamWriter(CurrentFilePath, false, Encoding.UTF8)) { using (var fs = new StreamWriter(_context.FilePath, false, Encoding.UTF8)) {
fs.Write(JsonConvert.SerializeObject(CurrentSemester.ToStorage())); fs.Write(JsonConvert.SerializeObject(_context.Semester.ToStorage()));
} }
} catch (Exception ex) { } catch (Exception ex) {
Util.Win32Dialog.Error(ex.ToString(), "保存文件时出错"); Util.Win32Dialog.Error(ex.ToString(), "保存文件时出错");
return; return;
} }
// Update UI // Show report
UpdateUiLayout(); ReportStatus($"已另存文件:{_context.FilePath}");
} }
private void uiMenuClose_Click(object sender, RoutedEventArgs e) { private void uiMenuClose_Click(object sender, RoutedEventArgs e) {
@ -218,17 +227,16 @@ namespace HFUTCourseSimulation {
if (!rv) return; if (!rv) return;
// Clear semester and assoc file // Clear semester and assoc file
CurrentSemester = null; _context.Semester = null;
CurrentFilePath = null; _context.FilePath = null;
// Update UI and data source // Show report
UpdateUiLayout(); ReportStatus($"文件已关闭");
UpdateDataSource();
} }
private void uiMenuQuit_Click(object sender, RoutedEventArgs e) { private void uiMenuQuit_Click(object sender, RoutedEventArgs e) {
// If there is an opened document, let we confirm it first // If there is an opened document, let we confirm it first
if (IsFileLoaded()) { if (_context.IsFileLoaded()) {
var rv = Util.Win32Dialog.Confirm("确认退出吗?所有未保存的更改都将永久丢失!", "确认退出"); var rv = Util.Win32Dialog.Confirm("确认退出吗?所有未保存的更改都将永久丢失!", "确认退出");
if (!rv) return; if (!rv) return;
} }
@ -238,13 +246,13 @@ namespace HFUTCourseSimulation {
} }
private void uiMenuCheck_Click(object sender, RoutedEventArgs e) { private void uiMenuCheck_Click(object sender, RoutedEventArgs e) {
var semester = CurrentSemester.ToStorage(); var semester = _context.Semester.ToStorage();
var reporter = new Kernel.Reporter(); var reporter = new Kernel.Reporter();
var rv = Kernel.Arranger.Arrange(semester, reporter); var rv = Kernel.Arranger.Arrange(semester, reporter);
} }
private void uiMenuSimulator_Click(object sender, RoutedEventArgs e) { private void uiMenuSimulator_Click(object sender, RoutedEventArgs e) {
var semester = CurrentSemester.ToStorage(); var semester = _context.Semester.ToStorage();
var reporter = new Kernel.Reporter(); var reporter = new Kernel.Reporter();
var rv = Kernel.Arranger.Arrange(semester, reporter); var rv = Kernel.Arranger.Arrange(semester, reporter);
if (rv is null) return; if (rv is null) return;
@ -255,31 +263,7 @@ namespace HFUTCourseSimulation {
} }
private void uiMenuRender_Click(object sender, RoutedEventArgs e) { private void uiMenuRender_Click(object sender, RoutedEventArgs e) {
////convert data var semester = _context.Semester.ToStorage();
//int weekCount;
//DateTime originDate;
//try {
// originDate = DateTime.Parse(General.GeneralSheet.StartDate);
// weekCount = General.GeneralSheet.WeekCount;
//} catch (Exception) {
// MessageBox.Show("周数或日期转换错误,无法输出", "错误", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
// return;
//}
////open file
//var sp = new Microsoft.Win32.SaveFileDialog();
//sp.RestoreDirectory = true;
//sp.Filter = "图片文件(*.png)|*.png|所有文件(*.*)|*.*";
//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);
//else MessageBox.Show("导出成功", "关于", MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK);
var semester = CurrentSemester.ToStorage();
var reporter = new Kernel.Reporter(); var reporter = new Kernel.Reporter();
var rv = Kernel.Arranger.Arrange(semester, reporter); var rv = Kernel.Arranger.Arrange(semester, reporter);
if (rv is null) return; if (rv is null) return;
@ -289,7 +273,7 @@ namespace HFUTCourseSimulation {
if (filepath is null) return; if (filepath is null) return;
if (Kernel.Render.Rending(rv, filepath)) { if (Kernel.Render.Rending(rv, filepath)) {
Win32Dialog.Info("导出成功", "导出结果"); ReportStatus($"已导出图片:{filepath}");
} else { } else {
Win32Dialog.Error("导出失败。请检查文件是否被占用或检查GDI是否能正常使用。", "导出结果"); Win32Dialog.Error("导出失败。请检查文件是否被占用或检查GDI是否能正常使用。", "导出结果");
} }
@ -303,6 +287,17 @@ namespace HFUTCourseSimulation {
#endregion #endregion
#region Shortcut Handlers
private void commandCanExec_Loaded(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = _context.MenuOnLoaded;
private void commandCanExec_NotLoaded(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = _context.MenuOnNotLoaded;
private void commandMenuNew_Click(object sender, ExecutedRoutedEventArgs e) => uiMenuNew_Click(sender, e);
private void commandMenuOpen_Click(object sender, ExecutedRoutedEventArgs e) => uiMenuOpen_Click(sender, e);
private void commandMenuSave_Click(object sender, ExecutedRoutedEventArgs e) => uiMenuSave_Click(sender, e);
#endregion
#region Main Area Handlers #region Main Area Handlers
private void uiCoursesList_MouseDoubleClick(object sender, MouseButtonEventArgs e) { private void uiCoursesList_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
@ -321,10 +316,10 @@ namespace HFUTCourseSimulation {
var idx = uiCoursesList.SelectedIndex; var idx = uiCoursesList.SelectedIndex;
if (idx < 0) { if (idx < 0) {
// No selection, append. // No selection, append.
CurrentSemester.Courses.Add(dialog.CurrentCourse); _context.Semester.Courses.Add(dialog.CurrentCourse);
} else { } else {
// Has selection, insert // Has selection, insert
CurrentSemester.Courses.Insert(idx, dialog.CurrentCourse); _context.Semester.Courses.Insert(idx, dialog.CurrentCourse);
} }
} }
@ -333,7 +328,7 @@ namespace HFUTCourseSimulation {
if (idx < 0) return; if (idx < 0) return;
var dialog = new Dialog.EditCourse(); var dialog = new Dialog.EditCourse();
dialog.CurrentCourse = CurrentSemester.Courses[idx]; dialog.CurrentCourse = _context.Semester.Courses[idx];
dialog.ShowDialog(); dialog.ShowDialog();
} }
@ -347,7 +342,7 @@ namespace HFUTCourseSimulation {
if (!rv) return; if (!rv) return;
// Remove it // Remove it
CurrentSemester.Courses.RemoveAt(idx); _context.Semester.Courses.RemoveAt(idx);
} }
private void uiCtxMenuClearCourse_Click(object sender, RoutedEventArgs e) { private void uiCtxMenuClearCourse_Click(object sender, RoutedEventArgs e) {
@ -356,7 +351,23 @@ namespace HFUTCourseSimulation {
if (!rv) return; if (!rv) return;
// Clear all schedules // Clear all schedules
CurrentSemester.Courses.Clear(); _context.Semester.Courses.Clear();
}
#endregion
#region Status Bar
private DispatcherTimer reportTimeout;
private void ReportStatus(string message) {
reportTimeout.Stop();
reportTimeout.Start();
uiReportText.Text = message;
}
private void reportTimeout_Tick(object sender, EventArgs e) {
uiReportText.Text = "";
} }
#endregion #endregion

View File

@ -12,6 +12,12 @@ namespace HFUTCourseSimulation.Util {
/// </summary> /// </summary>
public static class ColorConsistency { public static class ColorConsistency {
/// <summary>
/// UI界面的背景色白天或夜晚然而没有实现现在全是白天模式
/// </summary>
public static ColorPair Backboard = new ColorPair(Colors.Black, Colors.White);
//public static ColorPair Backboard = new ColorPair(Colors.White, Colors.Black);
/// <summary> /// <summary>
/// 获取标题栏的配色(星期几和日期) /// 获取标题栏的配色(星期几和日期)
/// </summary> /// </summary>
@ -40,7 +46,7 @@ namespace HFUTCourseSimulation.Util {
/// <summary> /// <summary>
/// 棋盘中的异色颜色的配置 /// 棋盘中的异色颜色的配置
/// </summary> /// </summary>
public static Color ChessboardColor => Color.FromArgb(10, 0, 0, 0); public static Color ChessboardColor => Color.FromArgb(20, 127, 127, 127);
} }