Add Task Page (#166)

This commit is contained in:
2022-12-10 17:50:35 +08:00
parent 0d1bad5551
commit 59af1e468d
13 changed files with 731 additions and 4 deletions

View File

@ -50,6 +50,7 @@ namespace GrasscutterTools.Forms
this.TCMain = new System.Windows.Forms.TabControl();
this.TPMail = new System.Windows.Forms.TabPage();
this.MenuSpawnEntityFilter = new System.Windows.Forms.ContextMenuStrip(this.components);
this.TPLoopTasks = new System.Windows.Forms.TabPage();
this.GrpCommand.SuspendLayout();
this.TCMain.SuspendLayout();
this.SuspendLayout();
@ -177,6 +178,7 @@ namespace GrasscutterTools.Forms
this.TCMain.Controls.Add(this.TPWeapon);
this.TCMain.Controls.Add(this.TPManage);
this.TCMain.Controls.Add(this.TPMail);
this.TCMain.Controls.Add(this.TPLoopTasks);
this.TCMain.Controls.Add(this.TPQuest);
this.TCMain.Controls.Add(this.TPScene);
this.TCMain.Controls.Add(this.TPAbout);
@ -194,6 +196,12 @@ namespace GrasscutterTools.Forms
this.MenuSpawnEntityFilter.Name = "MenuSpawnEntityFilter";
resources.ApplyResources(this.MenuSpawnEntityFilter, "MenuSpawnEntityFilter");
//
// TPLoopTasks
//
resources.ApplyResources(this.TPLoopTasks, "TPLoopTasks");
this.TPLoopTasks.Name = "TPLoopTasks";
this.TPLoopTasks.UseVisualStyleBackColor = true;
//
// FormMain
//
resources.ApplyResources(this, "$this");
@ -234,5 +242,6 @@ namespace GrasscutterTools.Forms
private System.Windows.Forms.TabControl TCMain;
private System.Windows.Forms.TabPage TPMail;
private System.Windows.Forms.ContextMenuStrip MenuSpawnEntityFilter;
private System.Windows.Forms.TabPage TPLoopTasks;
}
}

View File

@ -97,6 +97,7 @@ namespace GrasscutterTools.Forms
TPWeapon.Controls.Add(CreatePage<PageGiveWeapon>());
TPManage.Controls.Add(CreatePage<PageManagement>());
TPMail.Controls.Add(CreatePage<PageMail>());
TPLoopTasks.Controls.Add(CreatePage<PageTasks>());
TPQuest.Controls.Add(CreatePage<PageQuest>());
TPScene.Controls.Add(CreatePage<PageScene>());
TPAbout.Controls.Add(CreatePage<PageAbout>());

View File

@ -316,7 +316,7 @@
<value>TCMain</value>
</data>
<data name="&gt;&gt;TPAbout.ZOrder" xml:space="preserve">
<value>12</value>
<value>13</value>
</data>
<data name="TPManage.Location" type="System.Drawing.Point, System.Drawing">
<value>4, 26</value>
@ -370,7 +370,7 @@
<value>TCMain</value>
</data>
<data name="&gt;&gt;TPScene.ZOrder" xml:space="preserve">
<value>11</value>
<value>12</value>
</data>
<data name="TPItem.Location" type="System.Drawing.Point, System.Drawing">
<value>4, 26</value>
@ -508,7 +508,7 @@
<value>TCMain</value>
</data>
<data name="&gt;&gt;TPQuest.ZOrder" xml:space="preserve">
<value>10</value>
<value>11</value>
</data>
<data name="TPArtifact.Location" type="System.Drawing.Point, System.Drawing">
<value>4, 26</value>
@ -621,6 +621,33 @@
<data name="&gt;&gt;TPMail.ZOrder" xml:space="preserve">
<value>9</value>
</data>
<data name="TPLoopTasks.Location" type="System.Drawing.Point, System.Drawing">
<value>4, 26</value>
</data>
<data name="TPLoopTasks.Padding" type="System.Windows.Forms.Padding, System.Windows.Forms">
<value>3, 3, 3, 3</value>
</data>
<data name="TPLoopTasks.Size" type="System.Drawing.Size, System.Drawing">
<value>652, 245</value>
</data>
<data name="TPLoopTasks.TabIndex" type="System.Int32, mscorlib">
<value>17</value>
</data>
<data name="TPLoopTasks.Text" xml:space="preserve">
<value>循环</value>
</data>
<data name="&gt;&gt;TPLoopTasks.Name" xml:space="preserve">
<value>TPLoopTasks</value>
</data>
<data name="&gt;&gt;TPLoopTasks.Type" xml:space="preserve">
<value>System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="&gt;&gt;TPLoopTasks.Parent" xml:space="preserve">
<value>TCMain</value>
</data>
<data name="&gt;&gt;TPLoopTasks.ZOrder" xml:space="preserve">
<value>10</value>
</data>
<data name="TCMain.Location" type="System.Drawing.Point, System.Drawing">
<value>12, 12</value>
</data>

View File

@ -238,6 +238,12 @@
<Compile Include="Pages\PageSpawn.Designer.cs">
<DependentUpon>PageSpawn.cs</DependentUpon>
</Compile>
<Compile Include="Pages\PageTasks.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Pages\PageTasks.Designer.cs">
<DependentUpon>PageTasks.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.zh-TW.Designer.cs">
@ -496,6 +502,9 @@
<EmbeddedResource Include="Pages\PageSpawn.zh-TW.resx">
<DependentUpon>PageSpawn.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Pages\PageTasks.resx">
<DependentUpon>PageTasks.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.zh-TW.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.zh-TW.Designer.cs</LastGenOutput>

View File

@ -19,6 +19,7 @@
using System;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
@ -51,7 +52,7 @@ namespace GrasscutterTools.Pages
/// <param name="args">参数</param>
public SetCommandHandler SetCommand { get; set; }
public delegate Task<bool> RunCommandsHandler(string[] commands);
public delegate Task<bool> RunCommandsHandler(params string[] commands);
/// <summary>
/// 运行命令
@ -63,6 +64,30 @@ namespace GrasscutterTools.Pages
/// </summary>
public Func<string> GetCommand { get; set; }
/// <summary>
/// 运行原始命令(未处理的竖线分割命令文本)
/// </summary>
/// <param name="commands">未处理的竖线分割命令文本</param>
/// <returns>是否运行成功</returns>
protected async Task<bool> RunRawCommands(string commands)
{
if (commands.IndexOf('|') == -1)
return await RunCommands(FormatCommand(commands));
else
return await RunCommands(commands.Split('|').Select(it => FormatCommand(it)).ToArray());
}
/// <summary>
/// 格式化命令
/// (去除收尾空白,替换换行)
/// </summary>
/// <param name="raw">原始输入</param>
/// <returns>格式化后可执行命令</returns>
private string FormatCommand(string raw)
{
return raw.Trim().Replace("\\r", "\r").Replace("\\n", "\n");
}
#endregion - -
#region - -

View File

@ -0,0 +1,250 @@
namespace GrasscutterTools.Pages
{
partial class PageTasks
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.GrpTasks = new System.Windows.Forms.GroupBox();
this.ListTasks = new System.Windows.Forms.ListView();
this.ColTag = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.ColContent = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.ColDelay = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.ColCount = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.GrpTask = new System.Windows.Forms.GroupBox();
this.BtnRemove = new System.Windows.Forms.Button();
this.BtnAccept = new System.Windows.Forms.Button();
this.NUDTriggerCount = new System.Windows.Forms.NumericUpDown();
this.LblTriggerCount = new System.Windows.Forms.Label();
this.DTPDelay = new System.Windows.Forms.DateTimePicker();
this.LblDelay = new System.Windows.Forms.Label();
this.TxtTag = new System.Windows.Forms.TextBox();
this.LblTag = new System.Windows.Forms.Label();
this.GrpTasks.SuspendLayout();
this.GrpTask.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.NUDTriggerCount)).BeginInit();
this.SuspendLayout();
//
// GrpTasks
//
this.GrpTasks.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.GrpTasks.Controls.Add(this.ListTasks);
this.GrpTasks.Location = new System.Drawing.Point(3, 3);
this.GrpTasks.Name = "GrpTasks";
this.GrpTasks.Size = new System.Drawing.Size(640, 183);
this.GrpTasks.TabIndex = 0;
this.GrpTasks.TabStop = false;
this.GrpTasks.Text = "任务列表";
//
// ListTasks
//
this.ListTasks.AllowColumnReorder = true;
this.ListTasks.CheckBoxes = true;
this.ListTasks.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.ColTag,
this.ColContent,
this.ColDelay,
this.ColCount});
this.ListTasks.Dock = System.Windows.Forms.DockStyle.Fill;
this.ListTasks.HideSelection = false;
this.ListTasks.Location = new System.Drawing.Point(3, 19);
this.ListTasks.MultiSelect = false;
this.ListTasks.Name = "ListTasks";
this.ListTasks.Size = new System.Drawing.Size(634, 161);
this.ListTasks.TabIndex = 0;
this.ListTasks.UseCompatibleStateImageBehavior = false;
this.ListTasks.View = System.Windows.Forms.View.Details;
this.ListTasks.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.ListTasks_ItemChecked);
this.ListTasks.SelectedIndexChanged += new System.EventHandler(this.ListTasks_SelectedIndexChanged);
//
// ColTag
//
this.ColTag.Text = "标签";
this.ColTag.Width = 150;
//
// ColContent
//
this.ColContent.Text = "内容";
this.ColContent.Width = 330;
//
// ColDelay
//
this.ColDelay.Text = "延迟";
//
// ColCount
//
this.ColCount.Text = "次数";
//
// GrpTask
//
this.GrpTask.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.GrpTask.Controls.Add(this.BtnRemove);
this.GrpTask.Controls.Add(this.BtnAccept);
this.GrpTask.Controls.Add(this.NUDTriggerCount);
this.GrpTask.Controls.Add(this.LblTriggerCount);
this.GrpTask.Controls.Add(this.DTPDelay);
this.GrpTask.Controls.Add(this.LblDelay);
this.GrpTask.Controls.Add(this.TxtTag);
this.GrpTask.Controls.Add(this.LblTag);
this.GrpTask.Location = new System.Drawing.Point(3, 186);
this.GrpTask.Name = "GrpTask";
this.GrpTask.Size = new System.Drawing.Size(640, 50);
this.GrpTask.TabIndex = 1;
this.GrpTask.TabStop = false;
this.GrpTask.Text = "任务";
//
// BtnRemove
//
this.BtnRemove.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.BtnRemove.Location = new System.Drawing.Point(534, 18);
this.BtnRemove.Name = "BtnRemove";
this.BtnRemove.Size = new System.Drawing.Size(100, 23);
this.BtnRemove.TabIndex = 7;
this.BtnRemove.Text = "× 删除";
this.BtnRemove.UseVisualStyleBackColor = true;
this.BtnRemove.Click += new System.EventHandler(this.BtnRemove_Click);
//
// BtnAccept
//
this.BtnAccept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.BtnAccept.Location = new System.Drawing.Point(428, 18);
this.BtnAccept.Name = "BtnAccept";
this.BtnAccept.Size = new System.Drawing.Size(100, 23);
this.BtnAccept.TabIndex = 6;
this.BtnAccept.Text = "√ 确定";
this.BtnAccept.UseVisualStyleBackColor = true;
this.BtnAccept.Click += new System.EventHandler(this.BtnAccept_Click);
//
// NUDTriggerCount
//
this.NUDTriggerCount.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.NUDTriggerCount.Location = new System.Drawing.Point(372, 19);
this.NUDTriggerCount.Maximum = new decimal(new int[] {
1000,
0,
0,
0});
this.NUDTriggerCount.Minimum = new decimal(new int[] {
1,
0,
0,
-2147483648});
this.NUDTriggerCount.Name = "NUDTriggerCount";
this.NUDTriggerCount.Size = new System.Drawing.Size(50, 23);
this.NUDTriggerCount.TabIndex = 5;
this.NUDTriggerCount.Value = new decimal(new int[] {
1,
0,
0,
-2147483648});
//
// LblTriggerCount
//
this.LblTriggerCount.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.LblTriggerCount.AutoSize = true;
this.LblTriggerCount.Location = new System.Drawing.Point(334, 22);
this.LblTriggerCount.Name = "LblTriggerCount";
this.LblTriggerCount.Size = new System.Drawing.Size(32, 17);
this.LblTriggerCount.TabIndex = 4;
this.LblTriggerCount.Text = "次数";
//
// DTPDelay
//
this.DTPDelay.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.DTPDelay.CustomFormat = "";
this.DTPDelay.Format = System.Windows.Forms.DateTimePickerFormat.Time;
this.DTPDelay.Location = new System.Drawing.Point(258, 19);
this.DTPDelay.Name = "DTPDelay";
this.DTPDelay.ShowUpDown = true;
this.DTPDelay.Size = new System.Drawing.Size(70, 23);
this.DTPDelay.TabIndex = 3;
this.DTPDelay.Value = new System.DateTime(2022, 12, 10, 0, 1, 0, 0);
//
// LblDelay
//
this.LblDelay.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.LblDelay.AutoSize = true;
this.LblDelay.Location = new System.Drawing.Point(220, 22);
this.LblDelay.Name = "LblDelay";
this.LblDelay.Size = new System.Drawing.Size(32, 17);
this.LblDelay.TabIndex = 2;
this.LblDelay.Text = "延迟";
//
// TxtTag
//
this.TxtTag.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.TxtTag.Location = new System.Drawing.Point(49, 18);
this.TxtTag.Name = "TxtTag";
this.TxtTag.Size = new System.Drawing.Size(165, 23);
this.TxtTag.TabIndex = 1;
//
// LblTag
//
this.LblTag.AutoSize = true;
this.LblTag.Location = new System.Drawing.Point(11, 21);
this.LblTag.Name = "LblTag";
this.LblTag.Size = new System.Drawing.Size(32, 17);
this.LblTag.TabIndex = 0;
this.LblTag.Text = "标签";
//
// PageTasks
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.GrpTask);
this.Controls.Add(this.GrpTasks);
this.Name = "PageTasks";
this.GrpTasks.ResumeLayout(false);
this.GrpTask.ResumeLayout(false);
this.GrpTask.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.NUDTriggerCount)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.GroupBox GrpTasks;
private System.Windows.Forms.GroupBox GrpTask;
private System.Windows.Forms.DateTimePicker DTPDelay;
private System.Windows.Forms.Label LblDelay;
private System.Windows.Forms.TextBox TxtTag;
private System.Windows.Forms.Label LblTag;
private System.Windows.Forms.NumericUpDown NUDTriggerCount;
private System.Windows.Forms.Label LblTriggerCount;
private System.Windows.Forms.Button BtnRemove;
private System.Windows.Forms.Button BtnAccept;
private System.Windows.Forms.ListView ListTasks;
private System.Windows.Forms.ColumnHeader ColTag;
private System.Windows.Forms.ColumnHeader ColContent;
private System.Windows.Forms.ColumnHeader ColDelay;
private System.Windows.Forms.ColumnHeader ColCount;
}
}

View File

@ -0,0 +1,265 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using GrasscutterTools.Properties;
using GrasscutterTools.Utils;
using Newtonsoft.Json;
namespace GrasscutterTools.Pages
{
internal partial class PageTasks : BasePage
{
private const string TAG = nameof(PageTasks);
public PageTasks()
{
InitializeComponent();
ListTasks.FullRowSelect = true;
if (DesignMode) return;
InitTasks();
}
/// <summary>
/// 循环任务
/// </summary>
private class LoopTask
{
/// <summary>
/// 任务标签
/// </summary>
public string Tag { get; set; }
/// <summary>
/// 任务内容
/// </summary>
public string Content { get; set; }
/// <summary>
/// 延迟时间(秒)
/// </summary>
public int DelayS { get; set; }
/// <summary>
/// 触发次数 -1为无限循环
/// </summary>
public int TriggerCount { get; set; }
}
/// <summary>
/// 任务列表路径
/// </summary>
private readonly string TasksJsonPath = Common.GetAppDataFile("Tasks.json");
/// <summary>
/// 任务集合
/// </summary>
private List<LoopTask> Tasks;
/// <summary>
/// 运行中的任务集合
/// </summary>
private readonly ConcurrentDictionary<string, CancellationTokenSource> RunningTasks = new ConcurrentDictionary<string, CancellationTokenSource>();
/// <summary>
/// 初始化任务集合
/// </summary>
private void InitTasks()
{
if (File.Exists(TasksJsonPath))
{
try
{
Tasks = JsonConvert.DeserializeObject<List<LoopTask>>(File.ReadAllText(TasksJsonPath));
ListTasks.Items.AddRange(Tasks.Select(t => TaskToViewItem(t)).ToArray());
}
catch (Exception ex)
{
Tasks = new List<LoopTask>();
Logger.W(TAG, "Parsing Tasks json failed", ex);
}
}
else
{
Tasks = new List<LoopTask>();
}
}
/// <summary>
/// 关闭时触发
/// </summary>
public override void OnClosed()
{
// 取消所有正在运行的任务
foreach (var cs in RunningTasks.Values)
cs.Cancel();
// 清空列表
RunningTasks.Clear();
// 保存任务列表
File.WriteAllText(TasksJsonPath, JsonConvert.SerializeObject(Tasks));
}
/// <summary>
/// 任务转为列表项
/// </summary>
/// <param name="task">任务</param>
/// <returns>列表项</returns>
private static ListViewItem TaskToViewItem(LoopTask task) => new ListViewItem(new string[]
{
task.Tag,
task.Content,
TimeSpan.FromSeconds(task.DelayS).ToString(),
task.TriggerCount.ToString(),
});
/// <summary>
/// 列表选中项改变时触发
/// </summary>
private void ListTasks_SelectedIndexChanged(object sender, EventArgs e)
{
if (ListTasks.SelectedIndices.Count == 0) return;
int i = ListTasks.SelectedIndices[0];
var task = Tasks[i];
TxtTag.Text = task.Tag;
DTPDelay.Value = DateTime.Today.Add(TimeSpan.FromSeconds(task.DelayS));
NUDTriggerCount.Value = task.TriggerCount;
// 设置命令
SetCommand(task.Content);
}
/// <summary>
/// 点击确定按钮时触发
/// </summary>
private void BtnAccept_Click(object sender, EventArgs e)
{
var tag = TxtTag.Text.Trim();
var commands = GetCommand();
var delay = DTPDelay.Value.TimeOfDay;
var count = (int)NUDTriggerCount.Value;
if (string.IsNullOrEmpty(tag) || string.IsNullOrEmpty(commands) || delay.Ticks == 0)
{
MessageBox.Show(Resources.EmptyInputTip, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 查找是否已经存在
var i = Tasks.FindIndex(t => t.Tag == tag);
if (i == -1)
{
// 新建任务
var t = new LoopTask
{
Tag = tag,
Content = commands,
DelayS = (int)delay.TotalSeconds,
TriggerCount = count,
};
ListTasks.Items.Add(TaskToViewItem(t));
Tasks.Add(t);
}
else
{
// 已存在的任务,确认是否正在运行中
if (ListTasks.Items[i].Checked || RunningTasks.ContainsKey(tag))
{
MessageBox.Show(Resources.TaskRunningCannotOperated, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 否则修改任务内容
var task = Tasks[i];
task.Content = commands;
task.DelayS = (int)delay.TotalSeconds;
task.TriggerCount = count;
ListTasks.Items[i] = TaskToViewItem(task);
}
}
/// <summary>
/// 点击删除按钮时触发
/// </summary>
private void BtnRemove_Click(object sender, EventArgs e)
{
var tag = TxtTag.Text.Trim();
// 查找是否已经存在
var i = Tasks.FindIndex(t => t.Tag == tag);
if (i == -1) return;
if (ListTasks.Items[i].Checked || RunningTasks.ContainsKey(tag))
{
MessageBox.Show(Resources.TaskRunningCannotOperated, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 删除实例
ListTasks.Items.RemoveAt(i);
Tasks.RemoveAt(i);
}
/// <summary>
/// 任务前复选框改变时触发
/// </summary>
private void ListTasks_ItemChecked(object sender, ItemCheckedEventArgs e)
{
try
{
var item = e.Item;
if (item.Index < 0 || item.Index >= Tasks.Count)
return;
var task = Tasks[item.Index];
// 先将旧的任务取消
if (RunningTasks.TryRemove(task.Tag, out var source))
source.Cancel();
if (item.Checked)
{
var cancelSource = new CancellationTokenSource();
RunningTasks.TryAdd(task.Tag, cancelSource);
var token = cancelSource.Token;
Task.Run(async () =>
{
try
{
Logger.I(TAG, $"Task \"{task.Tag}\" started");
// 循环执行命令
for (int c = 0;
!token.IsCancellationRequested
&& (c < task.TriggerCount || task.TriggerCount <= 0);
c++)
{
// 延迟
await Task.Delay(task.DelayS * 1000, token);
// 使用UI线程执行
var ret = Invoke(new Func<string, Task<bool>>(RunRawCommands), task.Content);
if (ret is Task<bool> b && b.Result == false)
break;
// 执行
//if (!await RunRawCommands(task.Content))
// break;
}
}
finally
{
// 任务结束后取消勾选状态
BeginInvoke(new Action(() => item.Checked = false));
Logger.I(TAG, $"Task \"{task.Tag}\" stoped");
}
}, token);
}
}
catch (Exception ex)
{
Logger.E(TAG, "Start or Stop Task failed.", ex);
MessageBox.Show(ex.ToString(), Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}

View File

@ -0,0 +1,120 @@
<?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>
</root>

View File

@ -1185,6 +1185,15 @@ namespace GrasscutterTools.Properties {
}
}
/// <summary>
/// 查找类似 任务已经启动,无法操作 的本地化字符串。
/// </summary>
internal static string TaskRunningCannotOperated {
get {
return ResourceManager.GetString("TaskRunningCannotOperated", resourceCulture);
}
}
/// <summary>
/// 查找类似 提示 的本地化字符串。
/// </summary>

View File

@ -294,4 +294,7 @@ Improvement suggestions have been submitted, please use caution to send emails t
<data name="All" xml:space="preserve">
<value>All</value>
</data>
<data name="TaskRunningCannotOperated" xml:space="preserve">
<value>The task has already started and cannot be operated</value>
</data>
</root>

View File

@ -306,4 +306,7 @@
<data name="All" xml:space="preserve">
<value>全部</value>
</data>
<data name="TaskRunningCannotOperated" xml:space="preserve">
<value>任务已经启动,无法操作</value>
</data>
</root>

View File

@ -282,4 +282,7 @@
<data name="All" xml:space="preserve">
<value>Все</value>
</data>
<data name="TaskRunningCannotOperated" xml:space="preserve">
<value>Задача уже запущена и не может быть выполнена</value>
</data>
</root>

View File

@ -288,4 +288,7 @@
<data name="All" xml:space="preserve">
<value>全部</value>
</data>
<data name="TaskRunningCannotOperated" xml:space="preserve">
<value>任務已經啟動,無法操作</value>
</data>
</root>