一些项目方法总结

一、WinForm项目双语化实现(WPF项目下也可使用):

  1. 设置一个全局变量
string Language = "zh-CN";
string Language = "en";
  1. 建立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);
    }
}
  1. 建立 LangMRs.resx和LangMRsen.resx资源文件,设置窗体界面的Localization属性为true
  2. 在FrmMain.cs或者Program.cs中进行语言设置
if(Language == "en" || Language == "zh-CN")
{
    MultiLanguage.SetDeFaultLanguage(Language);
}
else
{
    MultiLanguage.SetDeFaultLanguage("zh-CN");
}
  1. 可以将全局变量存放到一个.txt文件中
string languageFilePath = System.IO.Path.Combine(Application.StartupPath, "Language.txt");
System.IO.File.WriteAllText(languageFilePath, GlobalVar.Language, Encoding.UTF8);
  1. 需要时从.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();
  1. WPF界面实现代码中使用
  2. 缺点:使用这种资源文件的方法只能在软件重启后生效、同时可能会导致窗体加载时不断闪烁
  3. 当切换到了哪种语言,就使用那种语言进行提示

二、实现不同进程中的信息同步,异步使用公共资源

  1. 创建一个消息同步类:EquipmentSyncManager.cs
    添加同步对象,使用属性进行封装调用
public static class EquipmentSyncManager
{
    private static readonly object _lockObject = new object();
    public static object LockObject => _lockObject;
}
  1. 在需要独占设备、参数、方法等类似功能的地方使用
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;
        }
    }
}

五、跨组件点击事件

  1. 在子组件(例:ControlDev)上创建事件对象:
public event EventHandler ControlClicked;
  1. 在子组件的事件方法中添加下面的代码:
ControlClicked?.Invoke(this, EventArgs.Empty);
  1. 父组件在调用子组件时,添加父组件上的事件方法:
ControlDev controlDev = new ControlDev();
controlDev.DeviceModeChanged += ControlDev_ControlClicked;;
  1. 在父组件上实现相应的事件方法
private void ControlDev_ControlClicked(object sender, EventArgs e)  {  }   	
  1. 在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()等窗体生命函数修改为正常的函数方法,需要使用时调用即可

posted @ 2025-09-25 10:59  鸭子进京赶烤  阅读(10)  评论(0)    收藏  举报