首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 移除 SourceSafe 綁定工具 (續篇):非同步執行緒、事件通知、UI 響應完整實作

     編輯:關於C語言

承接上篇核心遞迴掃描邏輯,本文完整呈現WinForms UI 層:資料夾選擇、非同步背景執行緒處理、事件驅動進度通知、執行緒安全 UI 更新、取消/完成狀態管理。可直接作為「批次檔案處理工具」的參考架構。

UI 介面佈局

控制項Name關鍵屬性
TextBoxtxtFolderReadOnly=true, 寬度佔大部
ButtonbtnBrowseText="瀏覽..."
TextBoxtxtLogMultiline=true, ReadOnly=true, ScrollBars=Vertical, Dock=Fill
ButtonbtnStartText="開始移除"
ButtonbtnCancelText="取消", Enabled=false

核心架構:背景執行緒 + 事件通知


// 事件參數:攜帶訊息字串
public class VssEventArgs : EventArgs
{
    public string Message { get; }
    public VssEventArgs(string msg) => Message = msg;
}

// 委派定義
public delegate void OperateNotifyHandler(object sender, VssEventArgs e);

// 背景工作類別
public class VssConverter
{
    private readonly string _rootPath;
    private int _processedCount;

    public event OperateNotifyHandler OperateNotify;
    public event EventHandler ThreadCompleted;

    public VssConverter(string rootPath) => _rootPath = rootPath;

    public void RemoveVss()
    {
        Task.Run(() =>
        {
            try
            {
                ProcessDirectory(_rootPath);
                ThreadCompleted?.Invoke(this, EventArgs.Empty);
            }
            catch (Exception ex)
            {
                OperateNotify?.Invoke(this, new VssEventArgs($"錯誤:{ex.Message}"));
                ThreadCompleted?.Invoke(this, EventArgs.Empty);
            }
        });
    }

    private void ProcessDirectory(string dir)
    {
        // 遞迴掃描 .vssscc、mssccprj.scc 等 SourceSafe 檔案並刪除
        // 每處理一個檔案/目錄觸發 OperateNotify
        foreach (var file in Directory.GetFiles(dir, "*.vssscc"))
        {
            File.Delete(file);
            OnNotify($"已刪除:{file}");
        }
        foreach (var subDir in Directory.GetDirectories(dir))
        {
            ProcessDirectory(subDir);
            // 檢查空目錄刪除邏輯...
        }
    }

    protected virtual void OnNotify(string msg)
    {
        OperateNotify?.Invoke(this, new VssEventArgs(msg));
    }
}

Form 邏輯層完整代碼


public partial class MainForm : Form
{
    private VssConverter _converter;
    private int _convertState; // 0=閒置, 1=執行中, 2=完成

    public MainForm() => InitializeComponent();

    // 瀏覽資料夾
    private void btnBrowse_Click(object sender, EventArgs e)
    {
        using (var dlg = new FolderBrowserDialog
        {
            Description = "選擇包含 SourceSafe 綁定的方案/專案根目錄",
            ShowNewFolderButton = false
        })
        {
            if (dlg.ShowDialog() == DialogResult.OK)
                txtFolder.Text = dlg.SelectedPath;
        }
    }

    // 開始處理
    private void btnStart_Click(object sender, EventArgs e)
    {
        if (string.IsNullOrWhiteSpace(txtFolder.Text) || !Directory.Exists(txtFolder.Text))
        {
            MessageBox.Show("請選擇有效的目錄路徑!");
            return;
        }

        txtLog.Clear();
        _convertState = 1;
        SetUiBusy(true);

        _converter = new VssConverter(txtFolder.Text);
        _converter.OperateNotify += OnConverterNotify;
        _converter.ThreadCompleted += OnConverterCompleted;
        _converter.RemoveVss();
    }

    // 取消 (簡易版:標記狀態,背景執行緒需定期檢查 CancellationToken)
    private void btnCancel_Click(object sender, EventArgs e)
    {
        _convertState = 0;
        AppendLog("使用者取消操作...");
        SetUiBusy(false);
    }

    // 背景執行緒通知 → UI 執行緒更新 (BeginInvoke)
    private void OnConverterNotify(object sender, VssEventArgs e)
    {
        if (txtLog.IsDisposed) return;
        txtLog.BeginInvoke(new Action(() => AppendLog(e.Message)));
    }

    private void AppendLog(string msg)
    {
        txtLog.AppendText($"[{DateTime.Now:HH:mm:ss}] {msg}{Environment.NewLine}");
        txtLog.ScrollToCaret();
    }

    // 背景完成 → 恢復 UI
    private void OnConverterCompleted(object sender, EventArgs e)
    {
        if (this.IsDisposed) return;
        this.BeginInvoke(new Action(() =>
        {
            if (_convertState == 1) // 正常完成
            {
                AppendLog("#### 轉換完成 ####");
            }
            _convertState = 0;
            SetUiBusy(false);
        }));
    }

    private void SetUiBusy(bool busy)
    {
        btnStart.Enabled = !busy;
        btnBrowse.Enabled = !busy;
        btnCancel.Enabled = busy;
        txtFolder.Enabled = !busy;
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        if (_convertState == 1)
        {
            if (MessageBox.Show("正在處理中,確定要關閉?", "確認", MessageBoxButtons.YesNo) == DialogResult.No)
                e.Cancel = true;
        }
        base.OnFormClosing(e);
    }
}

關鍵技術細節深度解析

1. 執行緒安全 UI 更新:BeginInvoke 模式


// 背景執行緒
txtLog.BeginInvoke(new Action(() => AppendLog(msg)));

// 等價於
txtLog.Invoke((MethodInvoker)delegate { AppendLog(msg); }); // 同步阻塞
  • BeginInvoke:非同步投遞,背景執行緒不等待,適合高頻日誌
  • Invoke:同步阻塞,確保順序,適合關鍵狀態更新
  • 必須檢查 IsDisposed/IsHandleCreated 避免 ObjectDisposedException

2. 事件解耦:背景邏輯零 UI 依賴

  • VssConverter 僅定義 OperateNotify/ThreadCompleted 事件
  • Form 訂閱並處理 UI 更新,符合關注點分離
  • 可單元測試 VssConverter 而無需 UI

3. 狀態機管理 UI 啟用/禁用


private int _convertState; // 0=Idle, 1=Running, 2=Done
private void SetUiBusy(bool busy) { ... }
  • 防止重複點擊「開始」
  • 處理中禁用輸入控制項
  • 完成/取消後自動恢復

4. 現代化改良:Task + CancellationToken + IProgress


public class VssConverterModern
{
    public async Task RemoveVssAsync(string rootPath, IProgress progress, CancellationToken ct)
    {
        await Task.Run(() =>
        {
            foreach (var file in Directory.EnumerateFiles(rootPath, "*.vssscc", SearchOption.AllDirectories))
            {
                ct.ThrowIfCancellationRequested();
                File.Delete(file);
                progress.Report($"已刪除:{file}");
            }
        }, ct);
    }
}

// Form 中調用
private async void btnStart_Click(object sender, EventArgs e)
{
    var progress = new Progress(msg => AppendLog(msg));
    var cts = new CancellationTokenSource();
    btnCancel.Click += (s, e) => cts.Cancel();

    SetUiBusy(true);
    try
    {
        await new VssConverterModern().RemoveVssAsync(txtFolder.Text, progress, cts.Token);
        AppendLog("#### 完成 ####");
    }
    catch (OperationCanceledException)
    {
        AppendLog("已取消");
    }
    finally { SetUiBusy(false); }
}

SourceSafe 綁定檔案識別清單

檔案/模式說明處理動作
*.vssscc專案/方案層級綁定資訊刪除
mssccprj.scc專案綁定資訊 (舊版)刪除
*.vspsccWeb 專案綁定刪除
vssver.scc版本控制資訊刪除
.sln 中的 Scc 區塊方案檔內嵌綁定資訊移除 SccNumberOfProjects 等行
.csproj/vbproj 中的 Scc 屬性專案檔內嵌綁定資訊移除 SccProjectName 等屬性

部署與使用建議

  • 備份優先:工具啟動前自動壓縮備份整個目錄
  • 乾跑模式:加入 --dry-run 參數,僅列出將刪除檔案不實際刪除
  • 日誌檔案:同時寫入 VssRemover_yyyyMMdd_HHmmss.log 供審計
  • 管理員權限:app.manifest 設 requireAdministrator 避免權限不足
  • 單元測試:建立臨時目錄結構,驗證刪除邏輯不誤刪正常檔案

結語:這套「背景執行緒 + 事件通知 + BeginInvoke UI 更新」架構,是 WinForms 批次處理工具的黃金模板。核心優勢:邏輯與 UI 完全解耦、執行緒安全、狀態機防呆、易於遷移至 Task/async 現代模式。拿去套用到任何「掃描目錄、批次處理檔案、即時顯示進度」的工具開發中,即刻獲得專業級架構。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved