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

VC++ 6.0 實作完整復原/重做機制並解決介面閃爍問題

     編輯:VC 教學

核心設計理念

成熟系統必備復原與重做功能。傳統做法完整備份文件資料佔用大量記憶體,導致步數受限;全畫面重繪造成閃爍。本文提出臨時檔案模擬堆疊機制:僅儲存操作變化量(差異資料),復原/重做時依變化量還原,並針對性更新視圖,達到記憶體高效、無限步數、零閃爍。

資料結構:雙向堆疊

// Undo/Redo 資料排放示意(m_UndoDataList)
// ====
// |###| } Redo 資料(堆疊頂端)
// |###| }
// |###| }
// ||| } 分界線
// ||| } Undo 資料(堆疊底部)
// ||| }
// =====
  • 操作時:清空 Redo 區,將新變化量 Push 入 Undo 區。
  • 復原:從 Undo 區 Pop 變化量,還原狀態,同時轉入 Redo 區。
  • 重做:從 Redo 區 Pop,重新套用,轉回 Undo 區。

引擎核心實作

1. 初始化臨時檔案

if (m_File.Open(m_TempPath, CFile::modeCreate | CFile::modeReadWrite | CFile::shareExclusive)) {
    m_File.SeekToBegin();
    m_UndoCount = 0; // 可復原步數
    m_RedoCount = 0; // 可重做步數
}

2. 儲存復原資料

int CRedoUndoEngine::PushData(LPVOID pData, DWORD size, DWORD param1, DWORD param2, int* pIndex) {
    // 1. 清除 Redo 資料
    if (m_RedoCount) {
        while (m_RedoCount--) delete (LPISEEUNDOINFO)m_UndoDataList.RemoveTail();
        m_RedoCount = 0;
    }
    // 2. 填寫索引資訊
    LPISEEUNDOINFO pInfo = new ISEEUNDOINFO;
    pInfo->m_index = m_UndoCount;
    pInfo->m_UserData1 = param1; // 操作類型標識
    pInfo->m_UserData2 = param2;
    pInfo->m_DataSize = size;
    pInfo->m_FilePosition = _get_current_overwrite_pos(); // 臨時檔寫入位置

    m_UndoDataList.AddTail(pInfo);

    // 3. 寫入使用者資料到臨時檔
    m_File.Seek(pInfo->m_FilePosition, CFile::begin);
    m_File.Write(pData, size);

    m_UndoCount++;
    ::GlobalFree(pData); // 釋放呼叫端記憶體
    return 1;
}

3. 彈出重做資料

int CIUndoEngine::RedoData(LPVOID* ppData, DWORD* pSize, DWORD* pParam1, DWORD* pParam2, int* pIndex) {
    if (!m_RedoCount) return 0;
    POSITION pos = m_UndoDataList.FindIndex(m_UndoCount);
    LPISEEUNDOINFO pInfo = (LPISEEUNDOINFO)m_UndoDataList.GetAt(pos);

    m_File.Seek(pInfo->m_FilePosition, CFile::begin);
    m_File.Read(*ppData, pInfo->m_DataSize);

    m_UndoCount++; m_RedoCount--;
    if (pSize) *pSize = pInfo->m_DataSize;
    if (pParam1) *pParam1 = pInfo->m_UserData1;
    if (pParam2) *pParam2 = pInfo->m_UserData2;
    if (pIndex) *pIndex = m_RedoCount;
    return 1;
}

實戰範例:服裝 CAD 圖元移動復原

1. 定義操作類型

#define REUNDO_MOV 0x0001 // 圖元移動
#define REUNDO_SEL 0x0002 // 選取變更

2. 記錄移動前後座標

// 移動操作前
int nByte = 4 * sizeof(DWORD);
HGLOBAL hMem = GlobalAlloc(GMEM_FIXED, nByte);
LPVOID pData = GlobalLock(hMem);
memcpy((DWORD*)pData, &m_oldPoint, 2 * sizeof(DWORD)); // 移動前座標
memcpy((DWORD*)pData + 2, &point, 2 * sizeof(DWORD));   // 移動後座標

m_pReUndoEngine->PushData(pData, nByte, REUNDO_MOV, 0, NULL);

3. 復原處理

void CMarkView::MovUndo(LPVOID pData) {
    CPoint pt1, pt2;
    memcpy(&pt1, (DWORD*)pData, 8);
    memcpy(&pt2, (DWORD*)pData + 2, 8);
    // 計算位移量,將圖元移回原位
    // 僅更新該圖元視圖區域,避免全畫面重繪
}

4. 重做處理

MovRedo 邏輯類似,套用移動後座標。

防止閃爍關鍵

引擎提供精確變化量,視圖層僅根據座標差異呼叫 InvalidateRect 更新局部區域,而非 Invalidate() 全域重繪。配合 OnEraseBkgnd 回傳 TRUE 並雙緩衝繪圖,徹底消除閃爍。

優勢總結

傳統全量備份本文差異堆疊法
記憶體隨步數線性增長臨時檔儲存,記憶體常數級
步數受限理論無限步數(受磁碟空間限制)
全域重繪閃爍局部更新零閃爍
實作複雜引擎封裝,業務邏輯單純

結語

以臨時檔模擬堆疊、僅儲存操作差異量,配合引擎封裝與局部視圖更新,完美解決復原/重做的記憶體、步數、閃爍三大痛點。此架構已成功應用於商業級 CAD 系統,穩定性與效能俱佳,強烈推薦給開發文件導向應用的工程師參考採用。

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