void CMyDialog::OnButtonDelete()
{
POSITION pos = m_list.GetFirstSelectedItemPosition();
int idx = m_list.GetNextSelectedItem(pos);
while (idx != -1)
{
LVITEM lvi = {0};
lvi.iItem = idx;
lvi.iSubItem = 0;
lvi.mask = LVIF_IMAGE;
// 同步刪除 ImageList 中對應圖片
if (m_list.GetItem(&lvi))
{
m_imgList.Remove(lvi.iImage);
}
m_list.DeleteItem(idx);
// 關鍵:獲取下一個選中項,索引已自動更新
idx = m_list.GetNextSelectedItem(pos);
}
// 強制重新排列與重繪,防止中間刪除留白
m_list.Arrange(LVA_DEFAULT);
m_list.RedrawItems(0, m_list.GetItemCount());
}
GetNextSelectedItem(pos) 而非遞增索引?刪除項目後,後續項目索引自動前移。若用 for (int i = 0; i < count; ++i) 會導致漏刪或越界。POSITION 迭代器內部維護當前遍歷狀態,免疫索引偏移。
LVIF_IMAGE 與 ImageList 同步刪除GetItem(&lvi) 取出項目關聯的圖片索引 lvi.iImagem_imgList.Remove(lvi.iImage) 同步清理圖片資源,防止資源洩漏與索引錯位Arrange(LVA_DEFAULT) + RedrawItems 雙重保險Arrange:強制控件按當前排序重新佈局,消除刪除留下的空白區域RedrawItems:觸發完整重繪,確保視覺上立即更新void CMyDialog::OnButtonDeleteOptimized()
{
// 1. 收集所有選中索引(逆序刪除避免索引偏移)
CArray<int, int> selectedIndices;
POSITION pos = m_list.GetFirstSelectedItemPosition();
while (pos)
{
selectedIndices.Add(m_list.GetNextSelectedItem(pos));
}
// 2. 逆序刪除(從大索引到小索引)
for (int i = selectedIndices.GetSize() - 1; i >= 0; --i)
{
int idx = selectedIndices[i];
LVITEM lvi = {0};
lvi.iItem = idx; lvi.iSubItem = 0; lvi.mask = LVIF_IMAGE;
if (m_list.GetItem(&lvi)) m_imgList.Remove(lvi.iImage);
m_list.DeleteItem(idx);
}
// 3. 一次性重繪
m_list.Arrange(LVA_DEFAULT);
m_list.Invalidate(); // 比 RedrawItems 更簡潔
}
優勢:避免多次 GetNextSelectedItem 內部遍歷,大量刪除時性能更優;邏輯更直觀易維護。
| 錯誤寫法 | 後果 | 正確做法 |
|---|---|---|
for (int i=0; i<count; ++i) DeleteItem(i) | 索引偏移導致漏刪/崩潰 | 用 POSITION 迭代器或逆序刪除 |
| 刪除項目不清理 ImageList | 圖標錯位、資源洩漏 | 配合 LVIF_IMAGE 同步 Remove |
| 刪除後不調用 Arrange/Redraw | 報表視圖留白、顯示異常 | 雙重保險:Arrange + RedrawItems/Invalidate |