一些项目方法总结
一、WinForm项目双语化实现(WPF项目下也可使用):
- 设置一个全局变量
string Language = "zh-CN";
string Language = "en";
- 建立MultiLanguage静态方法类
static class MultiLannguage
{
public static string DefaultLanguage = "zh-CN";
public static void SetDefaultLanguage(string lang)
{
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(lang);
}
}
- 建立 LangMRs.resx和LangMRsen.resx资源文件,设置窗体界面的Localization属性为true
- 在FrmMain.cs或者Program.cs中进行语言设置
if(Language == "en" || Language == "zh-CN")
{
MultiLanguage.SetDeFaultLanguage(Language);
}
else
{
MultiLanguage.SetDeFaultLanguage("zh-CN");
}
- 可以将全局变量存放到一个.txt文件中
string languageFilePath = System.IO.Path.Combine(Application.StartupPath, "Language.txt");
System.IO.File.WriteAllText(languageFilePath, GlobalVar.Language, Encoding.UTF8);
- 需要时从.txt文件中取出
string languageFilePath = System.IO.Path.Combine(Application.StartupPath, "Language.txt");
string savedLanguage = System.IO.File.ReadAllText(languageFilePath, Encoding.UTF8).Trim();
GlobalVar.Language = savedLanguage.ToString();
- WPF界面实现代码中使用
- 缺点:使用这种资源文件的方法只能在软件重启后生效、同时可能会导致窗体加载时不断闪烁
- 当切换到了哪种语言,就使用那种语言进行提示
二、实现不同进程中的信息同步,异步使用公共资源
- 创建一个消息同步类:EquipmentSyncManager.cs
添加同步对象,使用属性进行封装调用
public static class EquipmentSyncManager
{
private static readonly object _lockObject = new object();
public static object LockObject => _lockObject;
}
- 在需要独占设备、参数、方法等类似功能的地方使用
lock (EquipmentSyncManager.LockObject)
{
// 添加需要异步操作的代码
}
三、子页面在父界面中间位置打开(WinForm)
this.StartPosition = FormStartPosition.CenterParent;
四、Rijndael分组密码算法
其分组长度和密钥长度可以是128、192或256位。
AES(高级加密标准)是Rijndael的子集,其分组长度固定为128位,密钥长度可以是128、192或256位。
这里采用128位进行加密(分组长度为128位,密钥长度也为128位):
1)、密钥扩展(Key Expansion)
2)、初始轮密钥加(AddRoundKey)
3)、9轮循环(每轮包括:字节代换(SubByte)、行移位(ShiftRow)、列混合(MixColumn)、轮密钥加(AddRoundKey))
4)、最后一轮(不包括MixColumns):SubBytes, ShiftRows, AddRoundKey
应用:
同时存入,可以单独读取:
static string dat_SystemConfig = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory + @"\\DB", "SystemConfig.dat");
public static void SystemConfigWrite(int index, string DataCollectionTime, string Day, string CsvPath)
{
//写入
Hashtable para = new Hashtable();
para.Add("Index", index);
para.Add("DataCollectionTime", DataCollectionTime);
para.Add("Day", Day);
para.Add("CsvPath", CsvPath);
EncryptUtilSeal.EncryptObject(para, dat_SystemConfig);
}
public static bool IndexRead(out string index)
{
if (File.Exists(dat_SystemConfig))
{
Hashtable para = new Hashtable();
object obj = EncryptUtilSeal.DecryptObject(dat_SystemConfig);
para = obj as Hashtable;
index = para["Index"].ToString();
//存在
return true;
}
else
{
index = "0";
return false;
}
}
public static bool DataCollectionTimeSaveDayRead(out string DataCollectionTime)
{
if (File.Exists(dat_SystemConfig))
{
Hashtable para = new Hashtable();
object obj = EncryptUtilSeal.DecryptObject(dat_SystemConfig);
para = obj as Hashtable;
DataCollectionTime = para["DataCollectionTime"].ToString();
//存在
return true;
}
else
{
DataCollectionTime = "1";
return false;
}
}
public static bool HistorySaveDayRead(out string Day)
{
if (File.Exists(dat_SystemConfig))
{
Hashtable para = new Hashtable();
object obj = EncryptUtilSeal.DecryptObject(dat_SystemConfig);
para = obj as Hashtable;
Day = para["Day"].ToString();
//存在
return true;
}
else
{
Day = "180天";
return false;
}
}
public static bool SavePathRead(out string CsvPath)
{
if (File.Exists(dat_SystemConfig))
{
Hashtable para = new Hashtable();
object obj = EncryptUtilSeal.DecryptObject(dat_SystemConfig);
para = obj as Hashtable;
CsvPath = para["CsvPath"].ToString();
//存在
return true;
}
else
{
CsvPath = Application.StartupPath + @"\CsvData";
//不存在
return false;
}
}
/// <summary>
/// 加密、解密
/// </summary>
public class EncryptUtilSeal
{
private static byte[] key = new byte[] { 16,16,16...16,16 };
private static byte[] iv = new byte[] { 16,16,16...16,16 };
private static IFormatter S_Formatter = null;
static EncryptUtilSeal()
{
S_Formatter = new BinaryFormatter();//创建一个序列化的对象
}
/// <summary>
/// 采用Rijndael128位加密二进制可序列化对象至文件
/// </summary>
/// <param name="para">二进制对象</param>
/// <param name="filePath">文件路径</param>
/// <returns></returns>
public static bool EncryptObject(object para, string filePath)
{
//创建.bat文件 如果之前创建.bat文件则覆盖,无则创建
using (Stream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream csEncrypt = new CryptoStream(fs, RMCrypto.CreateEncryptor(key, iv), CryptoStreamMode.Write);
S_Formatter.Serialize(csEncrypt, para);//将数据序列化后给csEncrypt
csEncrypt.Close();
fs.Close();
return true;
}
}
/// <summary>
/// 从采用Rijndael128位加密的文件读取二进制对象
/// </summary>
/// <param name="filePath">文件路径</param>
/// <returns>二进制对象</returns>
public static object DecryptObject(string filePath)
{
//打开.bat文件
using (Stream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
object para;
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream csEncrypt = new CryptoStream(fs, RMCrypto.CreateDecryptor(key, iv), CryptoStreamMode.Read);
para = S_Formatter.Deserialize(csEncrypt); //将csEncrypt反序列化回原来的数据格式;
csEncrypt.Close();
fs.Close();
return para;
}
}
}
五、跨组件点击事件
- 在子组件(例:ControlDev)上创建事件对象:
public event EventHandler ControlClicked;
- 在子组件的事件方法中添加下面的代码:
ControlClicked?.Invoke(this, EventArgs.Empty);
- 父组件在调用子组件时,添加父组件上的事件方法:
ControlDev controlDev = new ControlDev();
controlDev.DeviceModeChanged += ControlDev_ControlClicked;;
- 在父组件上实现相应的事件方法
private void ControlDev_ControlClicked(object sender, EventArgs e) { }
- 在C#中,EventArgs是一个用于传递事件数据的基类。
(1)如果你需要传递自定义数据,你需要创建一个派生自EventArgs的类,并在其中包含你需要的属性。
public class MyEventArgs : EventArgs
{
public string Message { get; set; }
public int Value { get; set; }
public MyEventArgs(string message, int value)
{
Message = message;
Value = value;
}
}
(2)在子组件中,你可以使用这个自定义的事件参数类来定义事件:
public event EventHandler<MyEventArgs> ControlClicked;
(3)在触发事件的地方,你需要创建MyEventArgs的实例并传递给它:
ControlClicked?.Invoke(this, new MyEventArgs("Hello", 123));
(4)在父组件中,订阅事件的方法需要匹配EventHandler
controlDev.ControlClicked += ControlDev_ControlClicked;
private void ControlDev_ControlClicked(object sender, MyEventArgs e)
{
string message = e.Message;
int value = e.Value;
// 处理事件
}
六、线程操作 Task
1、创建Task
最基础的方法,使用Task.run:
Task task = Task.Run(() =>
{
// 执行的任务
Thread.Sleep(1000);
});
2、等待Task完成
(1)使用Wait()方法(阻塞当前线程)
task.Wait();
console.WritelLine("任务完成,主线程继续执行");
(2)使用await关键字(异步等待不阻塞线程)
async Task DoWorkAsync()
{
await task;
Console.WriteLine(”任务完成,异步方法继续执行");
}
(3)使用Task.WaitAll等待多个任务
Task[] tasks = new task[3];
for(int i=0; i<3; i++)
{
int taskID = i;
Task[i] = Task.Run(()=>
{
Console.WriteLine($"任务{taskID}正在运行");
Thread.Sleep(1000);
})
}
Task.WaitAll(tasks);
Console.Writline("所有任务已完成");
3、处理Task返回值
对于有返回值的任务,使用Task
Task<int> taskWithResult = Task.Run(()=>
{
Console.WriteLine("计算中...");
Thread.Sleep(1000);
return 42;
});
// 1-获取任务结果(会阻塞知道任务结束)
int result = taskWithResult.Result;
Console.WriteLine($"计算结果:{result}");
// 2-异步获取结果
async Task GetresultAsync()
{
int result = await taskWithResult;
Console.WriteLine($"异步获取结果:{result}");
}
4、处理Task异常
Task faultedTask = Task.Run(() =>{ throw new InvalidOperationException("任务中发生了错误");});
try
{
faultedTask.Wait(); // 等待任务会抛出AggregateException
}
catch (AggregateException ae)
{
foreach (var e in ae.InnerExceptions)
{
Console.WriteLine($"捕获到异常: {e.GetType().Name}: {e.Message}");
}
}
// 使用ContinueWith处理异常
Task.Run(() =>
{
throw new ArgumentException("另一个错误");
}).ContinueWith(t =>
{
if (t.IsFaulted) { Console.WriteLine($"任务失败: {t.Exception.InnerException.Message}"); }
else if (t.IsCanceled) { Console.WriteLine("任务被取消"); }
else { Console.WriteLine("任务成功完成"); }
});
5、取消Task
使用CancellationTokenSource取消任务
// 创建CancellationTokenSource
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// 创建可取消的任务
Task cancellableTask = Task.Run(() =>
{
for (int i = 0; i < 100; i++)
{
// 检查取消请求
if (token.IsCancellationRequested)
{
Console.WriteLine("任务被取消");
token.ThrowIfCancellationRequested(); // 抛出OperationCanceledException
}
Console.WriteLine($"工作进度: {i}%");
Thread.Sleep(100);
}
}, token);
// 3秒后取消任务
Thread.Sleep(3000);
cts.Cancel();
try
{
cancellableTask.Wait();
}
catch (AggregateException ae)
{
if (ae.InnerException is OperationCanceledException)
{
Console.WriteLine("任务已成功取消");
}
}
6、延续任务
Task<int> firstTask = Task.Run(() =>
{
Console.WriteLine("第一个任务");
return 10;
});
// 1-延续任务,接收第一个任务的结果
Task<string> continuationTask = firstTask.ContinueWith(t =>
{
Console.WriteLine($"第一个任务的结果: {t.Result}");
return $"处理后的结果: {t.Result * 2}";
});
// 等待延续任务完成
continuationTask.Wait();
Console.WriteLine($"最终结果: {continuationTask.Result}");
// 2-多个延续任务
Task.Run(() => Console.WriteLine("任务A"))
.ContinueWith(t => Console.WriteLine("任务B(无论A成功与否都会执行)"), TaskContinuationOptions.None)
.ContinueWith(t => Console.WriteLine("任务C(只有B成功时才执行)"), TaskContinuationOptions.OnlyOnRanToCompletion);
7、使用async/await简化异步编程
async Task<int> CalculateAsync()
{
Console.WriteLine("开始异步计算");
// 异步执行耗时操作
int result = await Task.Run(() =>
{
Thread.Sleep(2000);
return 100;
});
Console.WriteLine("计算完成");
return result;
}
// 在线程中执行多个线程
async Task<string> FetchStringAsync()
{
await Task.Delay(1000);
return "Hello";
}
async Task<int> FetchIntAsync()
{
await Task.Delay(1500);
return 42;
}
async Task ProcessDataAsync()
{
try
{
// 顺序执行异步操作
int data = await CalculateAsync();
Console.WriteLine($"获取到数据: {data}");
// 并行执行多个异步操作
Task<string> task1 = FetchStringAsync();
Task<int> task2 = FetchIntAsync();
// 等待所有任务完成
await Task.WhenAll(task1, task2);
Console.WriteLine($"结果: {task1.Result}, {task2.Result}");
}
catch (Exception ex)
{
Console.WriteLine($"处理数据时出错: {ex.Message}");
}
}
// 调用异步方法
ProcessDataAsync().Wait();
七、使用CancellationTokenSource进行Task线程控制
1、创建CancellationTokenSource实例,它将用于产生取消令牌并发送取消请求:
CancellationTokenSource cts = new CancellationTokenSource();
2、从CancellationTokenSource中获取Token属性,这个Token将被传递给需要取消的任务
CancellationToken token = cts.Token;
3、在Task中检查取消请求
(1)使用ThrowIfCancellationRequested方法:
这种方法会在取消请求时抛出OperationCanceledException,从而终止任务
Task task = Task.Run(()=>
{
for(int i=0; i<100; i++)
{
// 检查取消请求,如果取消则抛出异常
token.ThrowIfCancellationRequested();
// 功能代码
}
}, token); //注意:将token传递给Task.Run,以便任务在开始前就能检测到取消请求
(2)使用IsCancellationRequested属性
这种方法可以让我们在任务中执行一些清理工作,然后优雅地退出
Task task = Task.Run(()=>
{
for(int i=0; i<100; i++)
{
if(token.IsCancellationRequested)
{
// 任务取消,清理操作
break;
}
// 功能操作
}
});
4、取消任务:
在需要取消任务的时候,调用CancellationTokenSource的Cancel方法
// 在5秒后取消任务
Thread.Sleep(5000);
cts.Cancel();
5、处理任务取消
当任务被取消时,我们需要处理取消后的情况。如果任务是通过ThrowIfCancellationRequested取消的,那么任务状态会变为Canceled,并且会抛出OperationCanceledException(包装在AggregateException中)。我们可以通过Wait方法或者Try-Catch块来捕获异常
(1)使用Wait方法并捕获异常
try
{
task.Wait();
}
catch (AggregateException ae)
{
// 遍历所有内部异常
foreach (var e in ae.InnerExceptions)
{
if (e is OperationCanceledException)
{
Console.WriteLine("任务已被取消。");
}
else
{
// 处理其他异常
Console.WriteLine($"其他异常: {e.Message}");
}
}
}
(2)使用await(异步方式)
如果使用async/await,则可以直接捕获OperationCanceledException
try
{
await task;
}
catch (OperationCanceledException)
{
Console.WriteLine("任务已被取消。");
}
6、使用函数封装线程方法
(1)创建CancellationTokenSource对象类:
private CancellationTokenSource _cancellationTokenSource;
(2)创建Task线程对象:
private Task _appTask1;
private Task _appTask2;
private readonly List<Task> _appTasks = new List<Task>();
(3)实现线程方法
private void AppTaskFunction1(CancellationToken token)
{
while(token.IsCancellationRequested)
{
// 功能方法代码
}
}
private void AppTaskFunction2(CancellationToken token){ }
private void AppTasksFunction(CancellationToken token, string name){ }
(4)启动后台任务
private void StartBackgroundTasks()
{
// 防止重复启动
if (_cancellationTokenSource != null)
{
Console.WriteLine("[StartBackgroundTasks] Tasks are already running.");
return;
}
_cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = _cancellationTokenSource.Token;
_appTask1 = Task.Run(()=>AppTaskFunction1(token), token);
_appTask2 = Task.Run(()=>AppTaskFunction2(token), token);
foreach(var name in Names)
{
Task task = Task.Run(()=>AppTasksFunction(token, name), token);
_addTasks.Add(task);
}
}
(5)停止后台线程
private void StopBackgroundTasks()
{
if (_cancellationTokenSource == null) return;
try
{
_cancellationTokenSource.Cancel(); // 调用取消令牌
// 等待所有任务完成
var tasks = new List<Task>();
if (_appTask1 != null) tasks.Add(_appTask1);
if (_appTask2 != null) tasks.Add(_appTask2);
// 添加所有设备数据任务
tasks.AddRange(_appTasks);
Task.WaitAll(tasks.ToArray(), TimeSpan.FromSeconds(TaskTimeoutSeconds));
_csvCancellationTokenSources.Clear();
_appTasks.Clear();
catch (Exception ex)
{
Console.WriteLine($"等待任务完成时出错: {ex.Message}");
}
finally
{
_cancellationTokenSource.Dispose();
_cancellationTokenSource = null;
_deviceTasks.Clear();
}
}
八、从.exe文件获取软件最后编译发布时间
private static string GetLastCompileDate() //最后编译时间发布
{
string sEXE = Assembly.GetExecutingAssembly().Location;
System.IO.FileInfo fi = new System.IO.FileInfo(sEXE);
return fi.LastWriteTime.ToString("yyyyMMdd");
}
九、打开指定文件exportFilePath的文件夹,并选中指定文件
// 打开文件夹并选中导出的文件
try
{
System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{exportFilePath}\"");
}
catch (Exception ex)
{
MessageBox.Show($"打开文件位置失败: {ex.Message}", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
十、将窗体类转换为用户控件类
将 public partial class FrmControlDev : XtraFrom
修改为 public partial class FrmControlDev : XtraUserControl
然后将Form_Load()等窗体生命函数修改为正常的函数方法,需要使用时调用即可

浙公网安备 33010602011771号